use super::level_resolve::{LevelResolve, LevelStep};
use super::read_job::{Job, JobDone, ReadCtx, ReadSink};
use crate::InternalValue;
use alloc::vec::Vec;
pub(super) type SerialLevel = (Vec<(usize, InternalValue)>, Vec<(usize, u64)>);
enum State {
Between,
Staged,
Serial(Option<crate::Result<SerialLevel>>),
Over,
}
pub(super) struct TablesRead<'a, K> {
ctx: &'a ReadCtx<K>,
next: usize,
remaining: Vec<(usize, u64)>,
state: State,
staged: Option<LevelResolve<'a, K>>,
}
impl<'a, K: AsRef<[u8]>> TablesRead<'a, K> {
pub(super) const fn new(ctx: &'a ReadCtx<K>, remaining: Vec<(usize, u64)>) -> Self {
Self {
ctx,
next: 0,
remaining,
state: State::Between,
staged: None,
}
}
pub(super) fn pump(
&mut self,
results: &mut [Option<InternalValue>],
out: &mut impl ReadSink<'a>,
) -> Option<crate::Result<()>> {
loop {
match &mut self.state {
State::Between => {
let level = match self.ctx.version().level(self.next) {
Some(level) if !self.remaining.is_empty() => level,
_ => {
self.state = State::Over;
return Some(Ok(()));
}
};
debug_assert!(
self.remaining
.iter()
.all(|&(idx, _)| results.get(idx).is_some_and(Option::is_none))
);
self.staged = Some(LevelResolve::new(
level,
self.next,
core::mem::take(&mut self.remaining),
&self.ctx.keys,
self.ctx.comparator.as_ref(),
self.ctx.seqno,
self.ctx.metadata_budget,
));
self.next += 1;
self.state = State::Staged;
}
State::Staged => {
let Some(level) = &mut self.staged else {
unreachable!("a staged level is held while it is read");
};
let resolved = match level.pump(results, out) {
LevelStep::Pending => return None,
LevelStep::Resolved => true,
LevelStep::Serial => false,
};
let Some(level) = self.staged.take() else {
unreachable!("matched above");
};
self.state = State::Between;
self.remaining = level.into_remaining();
if !resolved {
out.job(Job::Serial {
level: self.next - 1,
remaining: core::mem::take(&mut self.remaining),
});
self.state = State::Serial(None);
return None;
}
}
State::Serial(None) => return None,
State::Serial(Some(_)) => {
let State::Serial(Some(result)) =
core::mem::replace(&mut self.state, State::Between)
else {
unreachable!("matched above");
};
let (found, remaining) = match result {
Ok(level) => level,
Err(error) => {
self.state = State::Over;
return Some(Err(error));
}
};
for (idx, item) in found {
if let Some(slot) = results.get_mut(idx) {
*slot = Some(item);
}
}
self.remaining = remaining;
}
State::Over => return Some(Ok(())),
}
}
}
pub(super) fn job_done(&mut self, done: JobDone, out: &mut impl ReadSink<'a>) {
match (&mut self.state, &mut self.staged, done) {
(State::Serial(slot @ None), _, JobDone::Serial { result }) => *slot = Some(result),
(State::Staged, Some(level), done) => level.job_done(done, out),
_ => debug_assert!(false, "a job handed back to a read that did not ask for it"),
}
}
pub(super) fn read_done(&mut self, done: crate::fs::ReadDone) {
if let Some(level) = &mut self.staged {
level.read_done(done);
} else {
debug_assert!(
false,
"a read handed back to a read that did not ask for it"
);
}
}
}