mod builder; mod iterator; use std::path::Path; use std::sync::Arc; use anyhow::Result; pub use builder::SsTableBuilder; use bytes::{Buf, BufMut, Bytes}; pub use iterator::SsTableIterator; use crate::block::Block; #[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: Bytes, } impl BlockMeta { /// Encode block meta to a buffer. pub fn encode_block_meta(block_meta: &[BlockMeta], buf: &mut Vec) { let mut estimated_size = 0; for meta in block_meta { estimated_size += std::mem::size_of::(); estimated_size += std::mem::size_of::(); estimated_size += meta.first_key.len(); } buf.reserve(estimated_size); let original_len = buf.len(); for meta in block_meta { buf.put_u32(meta.offset as u32); buf.put_u16(meta.first_key.len() as u16); buf.put_slice(&meta.first_key); } assert_eq!(estimated_size, buf.len() - original_len); } /// Decode block meta from a buffer. pub fn decode_block_meta(mut buf: impl Buf) -> Vec { let mut block_meta = Vec::new(); while buf.has_remaining() { let offset = buf.get_u32() as usize; let first_key_len = buf.get_u16() as usize; let first_key = buf.copy_to_bytes(first_key_len); block_meta.push(BlockMeta { offset, first_key }); } block_meta } } /// A file object. pub struct FileObject(Bytes); impl FileObject { pub fn read(&self, offset: u64, len: u64) -> Result> { Ok(self.0[offset as usize..(offset + len) as usize].to_vec()) } pub fn size(&self) -> u64 { self.0.len() as u64 } pub fn create(_path: &Path, data: Vec) -> Result { Ok(FileObject(data.into())) } pub fn open(_path: &Path) -> Result { unimplemented!() } } pub struct SsTable { file: FileObject, block_metas: Vec, block_meta_offset: usize, } impl SsTable { /// Open SSTable from a file. pub fn open(file: FileObject) -> Result { let len = file.size(); let raw_meta_offset = file.read(len - 4, 4)?; let block_meta_offset = (&raw_meta_offset[..]).get_u32() as u64; let raw_meta = file.read(block_meta_offset, len - 4 - block_meta_offset)?; Ok(Self { file, block_metas: BlockMeta::decode_block_meta(&raw_meta[..]), block_meta_offset: block_meta_offset as usize, }) } /// Read a block from the disk. pub fn read_block(&self, block_idx: usize) -> Result> { let offset = self.block_metas[block_idx].offset; let offset_end = self .block_metas .get(block_idx + 1) .map(|x| x.offset) .unwrap_or(self.block_meta_offset); let block_data = self .file .read(offset as u64, (offset_end - offset) as u64)?; Ok(Arc::new(Block::decode(&block_data[..]))) } /// Find the block that may contain `key`. pub fn find_block_idx(&self, key: &[u8]) -> usize { self.block_metas .partition_point(|meta| meta.first_key <= key) .saturating_sub(1) } /// Get number of data blocks. pub fn num_of_blocks(&self) -> usize { self.block_metas.len() } } #[cfg(test)] mod tests;