#![allow(unused_variables)] // TODO(you): remove this lint after implementing this mod #![allow(dead_code)] // TODO(you): remove this lint after implementing this mod pub(crate) mod bloom; mod builder; mod iterator; use std::fs::File; use std::path::Path; use std::sync::Arc; use anyhow::Result; pub use builder::SsTableBuilder; use bytes::Buf; pub use iterator::SsTableIterator; use crate::block::Block; use crate::key::{KeyBytes, KeySlice}; use crate::lsm_storage::BlockCache; use self::bloom::Bloom; #[derive(Clone, Debug, PartialEq, Eq)] pub struct BlockMeta { /// Offset of this data block. pub offset: usize, /// The first key of the data block. pub first_key: KeyBytes, /// The last key of the data block. pub last_key: KeyBytes, } impl BlockMeta { /// Encode block meta to a buffer. /// You may add extra fields to the buffer, /// in order to help keep track of `first_key` when decoding from the same buffer in the future. pub fn encode_block_meta( block_meta: &[BlockMeta], #[allow(clippy::ptr_arg)] // remove this allow after you finish buf: &mut Vec, ) { unimplemented!() } /// Decode block meta from a buffer. pub fn decode_block_meta(buf: impl Buf) -> Vec { unimplemented!() } } /// A file object. pub struct FileObject(Option, u64); impl FileObject { pub fn read(&self, offset: u64, len: u64) -> Result> { use std::os::unix::fs::FileExt; let mut data = vec![0; len as usize]; self.0 .as_ref() .unwrap() .read_exact_at(&mut data[..], offset)?; Ok(data) } pub fn size(&self) -> u64 { self.1 } /// Create a new file object (day 2) and write the file to the disk (day 4). pub fn create(path: &Path, data: Vec) -> Result { std::fs::write(path, &data)?; File::open(path)?.sync_all()?; Ok(FileObject( Some(File::options().read(true).write(false).open(path)?), data.len() as u64, )) } pub fn open(path: &Path) -> Result { let file = File::options().read(true).write(false).open(path)?; let size = file.metadata()?.len(); Ok(FileObject(Some(file), size)) } } /// An SSTable. pub struct SsTable { /// The actual storage unit of SsTable, the format is as above. pub(crate) file: FileObject, /// The meta blocks that hold info for data blocks. pub(crate) block_meta: Vec, /// The offset that indicates the start point of meta blocks in `file`. pub(crate) block_meta_offset: usize, id: usize, block_cache: Option>, first_key: KeyBytes, last_key: KeyBytes, pub(crate) bloom: Option, /// The maximum timestamp stored in this SST, implemented in week 3. max_ts: u64, } impl SsTable { #[cfg(test)] pub(crate) fn open_for_test(file: FileObject) -> Result { Self::open(0, None, file) } /// Open SSTable from a file. pub fn open(id: usize, block_cache: Option>, file: FileObject) -> Result { unimplemented!() } /// Create a mock SST with only first key + last key metadata pub fn create_meta_only( id: usize, file_size: u64, first_key: KeyBytes, last_key: KeyBytes, ) -> Self { Self { file: FileObject(None, file_size), block_meta: vec![], block_meta_offset: 0, id, block_cache: None, first_key, last_key, bloom: None, max_ts: 0, } } /// Read a block from the disk. pub fn read_block(&self, block_idx: usize) -> Result> { unimplemented!() } /// Read a block from disk, with block cache. (Day 4) pub fn read_block_cached(&self, block_idx: usize) -> Result> { unimplemented!() } /// Find the block that may contain `key`. /// Note: You may want to make use of the `first_key` stored in `BlockMeta`. /// You may also assume the key-value pairs stored in each consecutive block are sorted. pub fn find_block_idx(&self, key: KeySlice) -> usize { unimplemented!() } /// Get number of data blocks. pub fn num_of_blocks(&self) -> usize { self.block_meta.len() } pub fn first_key(&self) -> &KeyBytes { &self.first_key } pub fn last_key(&self) -> &KeyBytes { &self.last_key } pub fn table_size(&self) -> u64 { self.file.1 } pub fn sst_id(&self) -> usize { self.id } pub fn max_ts(&self) -> u64 { self.max_ts } }