mod block_handle;
mod iter;
pub use block_handle::{BlockHandle, KeyedBlockHandle};
pub use iter::Iter;
use super::{
Block,
block::{BlockOffset, Encoder, Trailer},
};
use crate::Slice;
use crate::io::{Error, ErrorKind};
use crate::{
SeqNo,
table::{
block::{Decoder, DecoderMeta, ParsedItem},
util::{SliceIndexes, compare_prefixed_slice},
},
};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[derive(Debug)]
pub struct IndexBlockParsedItem {
pub offset: BlockOffset,
pub size: u32,
pub prefix: Option<SliceIndexes>,
pub end_key: SliceIndexes,
pub seqno: SeqNo,
}
impl ParsedItem<KeyedBlockHandle> for IndexBlockParsedItem {
fn compare_key(
&self,
needle: &[u8],
bytes: &[u8],
cmp: &dyn crate::comparator::UserComparator,
) -> core::cmp::Ordering {
if let Some(prefix) = &self.prefix {
let prefix = unsafe { bytes.get_unchecked(prefix.0..prefix.1) };
let rest_key = unsafe { bytes.get_unchecked(self.end_key.0..self.end_key.1) };
compare_prefixed_slice(prefix, rest_key, needle, cmp)
} else {
let key = unsafe { bytes.get_unchecked(self.end_key.0..self.end_key.1) };
cmp.compare(key, needle)
}
}
fn seqno(&self) -> SeqNo {
self.seqno
}
fn key_offset(&self) -> usize {
self.end_key.0
}
fn key_end_offset(&self) -> usize {
self.end_key.1
}
fn materialize(&self, bytes: &Slice) -> KeyedBlockHandle {
#[expect(clippy::indexing_slicing)]
let key = if let Some(prefix) = &self.prefix {
let prefix_key = &bytes[prefix.0..prefix.1];
let rest_key = &bytes[self.end_key.0..self.end_key.1];
Slice::fused(prefix_key, rest_key)
} else {
bytes.slice(self.end_key.0..self.end_key.1)
};
KeyedBlockHandle::new(key, self.seqno, BlockHandle::new(self.offset, self.size))
}
}
#[derive(Clone)]
pub struct IndexBlock {
pub inner: Block,
}
impl IndexBlock {
#[must_use]
pub fn new(inner: Block) -> Self {
Self { inner }
}
#[must_use]
pub fn as_slice(&self) -> &Slice {
&self.inner.data
}
#[must_use]
#[expect(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
Trailer::new(&self.inner).item_count()
}
pub fn try_iter(
&self,
comparator: crate::comparator::SharedComparator,
) -> crate::Result<Iter<'_>> {
use crate::table::block::BlockType;
if self.inner.header.block_type != BlockType::Index {
return Err(crate::Error::InvalidTag((
"BlockType",
self.inner.header.block_type.into(),
)));
}
Ok(Iter::new(
Decoder::<KeyedBlockHandle, IndexBlockParsedItem>::try_new(&self.inner)?,
comparator,
))
}
#[must_use]
pub fn iter(&self, comparator: crate::comparator::SharedComparator) -> Iter<'_> {
Iter::new(
Decoder::<KeyedBlockHandle, IndexBlockParsedItem>::new(&self.inner),
comparator,
)
}
pub fn decoder_meta(&self) -> crate::Result<DecoderMeta> {
Ok(Decoder::<KeyedBlockHandle, IndexBlockParsedItem>::try_new(&self.inner)?.meta())
}
#[must_use]
pub fn iter_with_meta(
&self,
comparator: crate::comparator::SharedComparator,
meta: DecoderMeta,
) -> Iter<'_> {
Iter::new(
Decoder::<KeyedBlockHandle, IndexBlockParsedItem>::from_meta(&self.inner, meta),
comparator,
)
}
pub fn encode_into_vec(items: &[KeyedBlockHandle]) -> crate::Result<Vec<u8>> {
Self::encode_into_vec_with_restart_interval(items, 1)
}
pub fn encode_into_vec_with_restart_interval(
items: &[KeyedBlockHandle],
restart_interval: u8,
) -> crate::Result<Vec<u8>> {
let mut buf = vec![];
Self::encode_into_with_restart_interval(&mut buf, items, restart_interval)?;
Ok(buf)
}
pub fn encode_into(writer: &mut Vec<u8>, items: &[KeyedBlockHandle]) -> crate::Result<()> {
Self::encode_into_with_restart_interval(writer, items, 1)
}
pub fn encode_into_with_restart_interval(
writer: &mut Vec<u8>,
items: &[KeyedBlockHandle],
restart_interval: u8,
) -> crate::Result<()> {
if restart_interval == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
"index block restart interval must be greater than zero",
)
.into());
}
#[expect(clippy::expect_used)]
let first_key = items.first().expect("chunk should not be empty").end_key();
let mut serializer = Encoder::<'_, BlockOffset, KeyedBlockHandle>::new(
writer,
items.len(),
restart_interval,
0.0, first_key,
);
for item in items {
serializer.write(item)?;
}
serializer.finish()
}
}
#[cfg(test)]
#[expect(clippy::unwrap_used, clippy::indexing_slicing, reason = "test code")]
mod tests;