use alloc::sync::Arc;
use crate::{InternalValue, Table, table::Scanner, version::Run};
pub struct RunScanner {
tables: Arc<Run<Table>>,
lo: usize,
hi: usize,
lo_reader: Option<Scanner>,
pace: Option<crate::table::util::Pacer>,
#[cfg(feature = "columnar")]
carry: Option<crate::table::group_carry::CarryTarget>,
}
impl RunScanner {
pub fn culled(
run: Arc<Run<Table>>,
(lo, hi): (Option<usize>, Option<usize>),
pace: Option<crate::table::util::Pacer>,
) -> crate::Result<Self> {
let lo = lo.unwrap_or_default();
let hi = hi.unwrap_or(run.len() - 1);
#[expect(
clippy::expect_used,
reason = "we trust the caller to pass valid indexes"
)]
let lo_table = run.get(lo).expect("should exist");
let lo_reader = lo_table.scan_paced(pace.as_ref())?;
Ok(Self {
tables: run,
lo,
hi,
lo_reader: Some(lo_reader),
pace,
#[cfg(feature = "columnar")]
carry: None,
})
}
#[cfg(feature = "columnar")]
#[must_use]
pub(crate) fn with_carry(mut self, target: crate::table::group_carry::CarryTarget) -> Self {
#[expect(
clippy::expect_used,
reason = "culled opened the reader of table lo, which the run holds"
)]
let table = self.tables.get(self.lo).expect("lo is within the run");
self.lo_reader = self
.lo_reader
.take()
.map(|reader| carrying(reader, table, &target));
self.carry = Some(target);
self
}
fn scan_table(&self, index: usize) -> crate::Result<Scanner> {
let Some(table) = self.tables.get(index) else {
return Err(crate::Error::from(crate::io::Error::other(
"run scanner: table index past the run",
)));
};
let scanner = table.scan_paced(self.pace.as_ref())?;
#[cfg(feature = "columnar")]
if let Some(target) = &self.carry {
return Ok(carrying(scanner, table, target));
}
Ok(scanner)
}
}
#[cfg(feature = "columnar")]
fn carrying(
scanner: Scanner,
table: &Table,
target: &crate::table::group_carry::CarryTarget,
) -> Scanner {
if target.takes(table) {
scanner.with_carry(target.carry_for(table))
} else {
scanner
}
}
impl Iterator for RunScanner {
type Item = crate::Result<InternalValue>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(lo_reader) = &mut self.lo_reader {
if let Some(item) = lo_reader.next() {
return Some(item);
}
self.lo_reader = None;
self.lo += 1;
if self.lo <= self.hi {
self.lo_reader = Some(fail_iter!(self.scan_table(self.lo)));
}
} else {
return None;
}
}
}
}
#[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests;