use std::cmp::Ordering;
use std::io::Write;
use std::sync::Arc;
use crate::error::{Error, Result};
use crate::sstable::block::{Block, BlockData, BlockHandle, BlockIterator, BlockWriter};
use crate::sstable::error::SSTableError;
use crate::sstable::table::{compress_block, read_table_block, write_block_at_offset};
use crate::vfs::File;
use crate::{CompressionType, Options};
#[derive(Clone, Debug)]
pub(crate) struct BlockHandleWithKey {
pub separator_key: Vec<u8>,
pub handle: BlockHandle,
}
impl BlockHandleWithKey {
#[cfg(test)]
pub(crate) fn new(separator_key: Vec<u8>, handle: BlockHandle) -> BlockHandleWithKey {
BlockHandleWithKey {
separator_key,
handle,
}
}
pub(crate) fn offset(&self) -> u64 {
self.handle.offset as u64
}
}
pub(crate) struct IndexWriter {
opts: Arc<Options>,
index_blocks: Vec<BlockWriter>,
current_block: BlockWriter,
max_block_size: usize,
index_size: u64,
num_partitions: u64,
top_level_index_size: u64,
}
impl IndexWriter {
pub(crate) fn new(opts: Arc<Options>, max_block_size: usize) -> IndexWriter {
IndexWriter {
opts: Arc::clone(&opts),
index_blocks: Vec::new(),
current_block: BlockWriter::new(
opts.block_size,
opts.block_restart_interval,
Arc::clone(&opts.internal_comparator),
),
max_block_size,
index_size: 0,
num_partitions: 0,
top_level_index_size: 0,
}
}
pub(crate) fn index_size(&self) -> u64 {
self.index_size
}
pub(crate) fn num_partitions(&self) -> u64 {
self.num_partitions
}
pub(crate) fn top_level_index_size(&self) -> u64 {
self.top_level_index_size
}
pub(crate) fn add(&mut self, key: &[u8], handle: &[u8]) -> Result<()> {
if self.current_block.size_estimate() >= self.max_block_size {
self.finish_current_block();
}
self.current_block.add(key, handle)
}
fn finish_current_block(&mut self) {
let new_block = BlockWriter::new(
self.opts.block_size,
self.opts.block_restart_interval,
Arc::clone(&self.opts.internal_comparator),
);
let finished_block = std::mem::replace(&mut self.current_block, new_block);
self.index_blocks.push(finished_block);
}
fn write_compressed_block<W: Write>(
writer: &mut W,
block: BlockData,
compression_type: CompressionType,
offset: usize,
) -> Result<(BlockHandle, usize)> {
let compressed_block = compress_block(block, compression_type)?;
write_block_at_offset(writer, compressed_block, compression_type, offset)
}
pub(crate) fn finish<W: Write>(
&mut self,
writer: &mut W,
compression_type: CompressionType,
mut offset: usize,
) -> Result<(BlockHandle, usize)> {
let start_offset = offset;
if self.current_block.entries() > 0 {
self.finish_current_block();
}
if self.index_blocks.is_empty() {
let new_block = BlockWriter::new(
self.opts.block_size,
self.opts.block_restart_interval,
Arc::clone(&self.opts.internal_comparator),
);
let old_block = std::mem::replace(&mut self.current_block, new_block);
self.index_blocks.push(old_block);
}
let mut top_level_index = BlockWriter::new(
self.opts.block_size,
self.opts.block_restart_interval,
Arc::clone(&self.opts.internal_comparator),
);
self.num_partitions = self.index_blocks.len() as u64;
let index_blocks = std::mem::take(&mut self.index_blocks);
for block in index_blocks {
let separator_key = block.last_key.clone();
let block_data = block.finish()?;
if separator_key.is_empty() {
continue;
}
let (block_handle, new_offset) =
Self::write_compressed_block(writer, block_data, compression_type, offset)?;
offset = new_offset;
top_level_index.add(&separator_key, &block_handle.encode())?;
}
let top_level_data = top_level_index.finish()?;
self.top_level_index_size = top_level_data.len() as u64;
let (handle, final_offset) =
Self::write_compressed_block(writer, top_level_data, compression_type, offset)?;
self.index_size = (final_offset - start_offset) as u64;
Ok((handle, final_offset))
}
}
#[derive(Clone)]
pub(crate) struct Index {
pub(crate) id: u64,
pub(crate) opts: Arc<Options>,
pub(crate) blocks: Vec<BlockHandleWithKey>,
pub(crate) file: Arc<dyn File>,
}
impl Index {
pub(crate) fn new(
id: u64,
opt: Arc<Options>,
f: Arc<dyn File>,
location: &BlockHandle,
) -> Result<Self> {
let block =
read_table_block(Arc::clone(&opt.internal_comparator), Arc::clone(&f), location)?;
let mut iter = block.iter()?;
let mut blocks = Vec::new();
iter.seek_to_first()?;
while iter.is_valid() {
let key = iter.key_bytes().to_vec();
let handle_bytes = iter.value_bytes();
let (handle, _) = BlockHandle::decode(handle_bytes)?;
blocks.push(BlockHandleWithKey {
separator_key: key,
handle,
});
iter.advance()?;
}
Ok(Index {
id,
opts: opt,
blocks,
file: Arc::clone(&f),
})
}
pub(crate) fn find_block_handle_by_key(
&self,
target: &[u8],
) -> Result<Option<(usize, &BlockHandleWithKey)>> {
if self.blocks.is_empty() {
let err = Error::from(SSTableError::EmptyCorruptPartitionedIndex {
table_id: self.id,
});
log::error!("[INDEX] {}", err);
return Err(err);
}
let internal_cmp = &self.opts.internal_comparator;
let index = self.blocks.partition_point(|block| {
internal_cmp.compare(&block.separator_key, target) == Ordering::Less
});
Ok(self
.blocks
.get(index)
.filter(|block| internal_cmp.compare(target, &block.separator_key) != Ordering::Greater)
.map(|block| (index, block)))
}
pub(crate) fn load_block(&self, block_handle: &BlockHandleWithKey) -> Result<Arc<Block>> {
if let Some(block) = self.opts.block_cache.get_index_block(self.id, block_handle.offset()) {
return Ok(block);
}
let block_data = read_table_block(
Arc::clone(&self.opts.internal_comparator),
Arc::clone(&self.file),
&block_handle.handle,
)?;
let block = Arc::new(block_data);
self.opts.block_cache.insert_index_block(
self.id,
block_handle.offset(),
Arc::clone(&block),
);
Ok(block)
}
}
pub(crate) struct IndexIterator<'a> {
index: &'a Index,
partition_index: usize,
partition_iter: Option<BlockIterator>,
}
impl<'a> IndexIterator<'a> {
pub(crate) fn new(index: &'a Index) -> Self {
Self {
index,
partition_index: 0,
partition_iter: None,
}
}
pub(crate) fn valid(&self) -> bool {
self.partition_iter.as_ref().is_some_and(|iter| iter.is_valid())
}
#[allow(dead_code)]
pub(crate) fn key(&self) -> &[u8] {
debug_assert!(self.valid());
self.partition_iter.as_ref().unwrap().key_bytes()
}
pub(crate) fn block_handle(&self) -> Result<BlockHandle> {
debug_assert!(self.valid());
let val = self.partition_iter.as_ref().unwrap().value_bytes();
let (handle, _) = BlockHandle::decode(val)?;
Ok(handle)
}
pub(crate) fn seek_to_first(&mut self) -> Result<()> {
if self.index.blocks.is_empty() {
self.partition_iter = None;
let err = Error::from(SSTableError::EmptyCorruptPartitionedIndex {
table_id: self.index.id,
});
log::error!("[INDEX] {}", err);
return Err(err);
}
self.partition_index = 0;
let block = self.index.load_block(&self.index.blocks[0])?;
let mut iter = block.iter()?;
iter.seek_to_first()?;
self.partition_iter = Some(iter);
if !self.valid() {
self.next()?;
}
Ok(())
}
pub(crate) fn seek_to_last(&mut self) -> Result<()> {
if self.index.blocks.is_empty() {
self.partition_iter = None;
let err = Error::from(SSTableError::EmptyCorruptPartitionedIndex {
table_id: self.index.id,
});
log::error!("[INDEX] {}", err);
return Err(err);
}
self.partition_index = self.index.blocks.len() - 1;
let block = self.index.load_block(&self.index.blocks[self.partition_index])?;
let mut iter = block.iter()?;
iter.seek_to_last()?;
self.partition_iter = Some(iter);
if !self.valid() {
self.prev()?;
}
Ok(())
}
pub(crate) fn seek(&mut self, target: &[u8]) -> Result<()> {
if self.index.blocks.is_empty() {
self.partition_iter = None;
let err = Error::from(SSTableError::EmptyCorruptPartitionedIndex {
table_id: self.index.id,
});
log::error!("[INDEX] {}", err);
return Err(err);
}
match self.index.find_block_handle_by_key(target)? {
Some((idx, block_handle)) => {
self.partition_index = idx;
let block = self.index.load_block(block_handle)?;
let mut iter = block.iter()?;
iter.seek_internal(target)?;
self.partition_iter = Some(iter);
if !self.valid() {
self.next()?;
}
}
None => {
self.partition_iter = None;
}
}
Ok(())
}
pub(crate) fn next(&mut self) -> Result<bool> {
if let Some(ref mut iter) = self.partition_iter {
if iter.advance()? {
return Ok(true);
}
}
loop {
self.partition_index += 1;
if self.partition_index >= self.index.blocks.len() {
self.partition_iter = None;
return Ok(false);
}
let block = self.index.load_block(&self.index.blocks[self.partition_index])?;
let mut iter = block.iter()?;
iter.seek_to_first()?;
self.partition_iter = Some(iter);
if self.valid() {
return Ok(true);
}
}
}
pub(crate) fn prev(&mut self) -> Result<bool> {
if let Some(ref mut iter) = self.partition_iter {
if iter.prev_internal()? {
return Ok(true);
}
}
loop {
if self.partition_index == 0 {
self.partition_iter = None;
return Ok(false);
}
self.partition_index -= 1;
let block = self.index.load_block(&self.index.blocks[self.partition_index])?;
let mut iter = block.iter()?;
iter.seek_to_last()?;
self.partition_iter = Some(iter);
if self.valid() {
return Ok(true);
}
}
}
}