use crate::{
SeqNo,
comparator::SharedComparator,
double_ended_peekable::{DoubleEndedPeekable, DoubleEndedPeekableExt},
table::{
KeyedBlockHandle,
block::{Decoder, ParsedItem},
index_block::IndexBlockParsedItem,
},
};
pub struct Iter<'a> {
decoder: DoubleEndedPeekable<
IndexBlockParsedItem,
Decoder<'a, KeyedBlockHandle, IndexBlockParsedItem>,
>,
comparator: SharedComparator,
}
impl<'a> Iter<'a> {
#[must_use]
pub fn new(
decoder: Decoder<'a, KeyedBlockHandle, IndexBlockParsedItem>,
comparator: SharedComparator,
) -> Self {
let decoder = decoder.double_ended_peekable();
Self {
decoder,
comparator,
}
}
fn seek_with_cache_resets(
&mut self,
needle: &[u8],
seqno: SeqNo,
reset_front: bool,
reset_back: bool,
) -> bool {
let cmp = &self.comparator;
if reset_front {
self.decoder.reset_front_peeked();
}
if reset_back {
self.decoder.reset_back_peeked();
self.decoder.inner_mut().reset_back_cursor();
}
let landed = if cmp.is_lexicographic() {
self.decoder.inner_mut().seek(
|end_key, s| match end_key.cmp(needle) {
core::cmp::Ordering::Greater => false,
core::cmp::Ordering::Less => true,
core::cmp::Ordering::Equal => s >= seqno,
},
true,
)
} else {
self.decoder.inner_mut().seek(
|end_key, s| match cmp.compare(end_key, needle) {
core::cmp::Ordering::Greater => false,
core::cmp::Ordering::Less => true,
core::cmp::Ordering::Equal => s >= seqno,
},
true,
)
};
if !landed {
return false;
}
if self.decoder.inner_mut().restart_interval() > 1 {
self.decoder.inner_mut().advance_while(|item, bytes| {
match item.compare_key(needle, bytes, cmp.as_ref()) {
core::cmp::Ordering::Greater => false,
core::cmp::Ordering::Less => true,
core::cmp::Ordering::Equal => item.seqno() >= seqno,
}
});
}
self.decoder.peek().is_some()
}
pub fn seek(&mut self, needle: &[u8], seqno: SeqNo) -> bool {
self.seek_with_cache_resets(needle, seqno, true, true)
}
pub fn seek_upper(&mut self, needle: &[u8], _seqno: SeqNo) -> bool {
self.seek_upper_impl(needle, true, true, true)
.unwrap_or(false)
}
pub(crate) fn seek_upper_impl(
&mut self,
needle: &[u8],
reset_front: bool,
reset_back: bool,
check_back_cache: bool,
) -> crate::Result<bool> {
let cmp = &self.comparator;
if reset_front {
self.decoder.reset_front_peeked();
}
if reset_back {
self.decoder.reset_back_peeked();
}
let restart_interval = self.decoder.inner_mut().restart_interval();
let lex = cmp.is_lexicographic();
let found = if restart_interval == 1 {
if check_back_cache {
if lex {
self.decoder
.inner_mut()
.seek_upper(|end_key, _s| end_key < needle, true)
} else {
self.decoder.inner_mut().seek_upper(
|end_key, _s| cmp.compare(end_key, needle) == core::cmp::Ordering::Less,
true,
)
}
} else {
if lex {
self.decoder
.inner_mut()
.seek_upper(|end_key, _s| end_key <= needle, true)
} else {
self.decoder.inner_mut().seek_upper(
|end_key, _s| cmp.compare(end_key, needle) != core::cmp::Ordering::Greater,
true,
)
}
}
} else if lex {
self.decoder
.inner_mut()
.seek_upper(|end_key, _s| end_key <= needle, true)
} else {
self.decoder.inner_mut().seek_upper(
|end_key, _s| cmp.compare(end_key, needle) != core::cmp::Ordering::Greater,
true,
)
};
if !found {
return Ok(false);
}
if restart_interval > 1 {
self.decoder
.inner_mut()
.trim_back_to_upper_bound(|item, bytes| {
item.compare_key(needle, bytes, cmp.as_ref())
});
while self
.decoder
.inner_mut()
.upper_stack_tail_cmp(|item, bytes| item.compare_key(needle, bytes, cmp.as_ref()))
== Some(core::cmp::Ordering::Less)
{
if !self.decoder.inner_mut().advance_upper_restart_interval() {
break;
}
self.decoder
.inner_mut()
.trim_back_to_upper_bound(|item, bytes| {
item.compare_key(needle, bytes, cmp.as_ref())
});
}
if self
.decoder
.inner_mut()
.upper_stack_tail_cmp(|item, bytes| item.compare_key(needle, bytes, cmp.as_ref()))
.is_none()
{
return Err(crate::Error::InvalidTrailer);
}
}
if check_back_cache {
Ok(self.decoder.peek_back().is_some())
} else {
Ok(true)
}
}
#[expect(
clippy::unnecessary_wraps,
reason = "API plumbing: inner seek_with_cache_resets will become fallible \
when Decoder binary-index parsing surfaces errors"
)]
pub(crate) fn seek_lower_bound_cursor(
&mut self,
needle: &[u8],
seqno: SeqNo,
) -> crate::Result<bool> {
Ok(self.seek_with_cache_resets(needle, seqno, true, false))
}
pub(crate) fn seek_upper_bound_cursor(
&mut self,
needle: &[u8],
_seqno: SeqNo,
) -> crate::Result<bool> {
self.seek_upper_impl(needle, false, true, false)
}
}
impl Iterator for Iter<'_> {
type Item = IndexBlockParsedItem;
fn next(&mut self) -> Option<Self::Item> {
self.decoder.next()
}
}
impl DoubleEndedIterator for Iter<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.decoder.next_back()
}
}
#[cfg(test)]
#[expect(
clippy::unwrap_used,
clippy::indexing_slicing,
reason = "corruption tests intentionally mutate encoded bytes via direct indexing"
)]
mod tests;