use gnitz_wire::PkKeys;
use std::ops::ControlFlow;
use super::{empty_cursor, from_runs_at, ReadCursor, SkeletonKeys};
use crate::repr::batch::Batch;
use crate::repr::run::Run;
use crate::repr::scatter::gather_rows;
use crate::schema::{project_schema, ColumnLocator, SchemaDescriptor, SchemaFacts};
use gnitz_wire::PkBuf;
pub struct PkSetGather {
cursor: ReadCursor,
keys: PkKeys,
next: usize,
}
impl PkSetGather {
pub(crate) fn new(cursor: ReadCursor, keys: PkKeys) -> Self {
debug_assert_eq!(keys.stride(), cursor.schema.pk_stride());
let mut gather = PkSetGather { cursor, keys, next: 0 };
gather.position();
gather
}
pub fn over_runs(runs: impl IntoIterator<Item = Run>, schema: SchemaDescriptor, cap: usize, keys: PkKeys) -> Self {
debug_assert!(keys.stride() <= schema.pk_stride());
let cursor = match keys.iter().next() {
Some(first) => {
let first = PkBuf::from_bytes(first).widened(schema.pk_stride());
from_runs_at(runs, schema, cap, first.pk_bytes())
}
None => empty_cursor(schema),
};
PkSetGather { cursor, keys, next: 0 }
}
pub(crate) fn reload(&mut self, keys: PkKeys) {
self.keys = keys;
self.next = 0;
self.position();
}
fn position(&mut self) {
if let Some(first) = self.keys.iter().next() {
self.cursor.advance_to(first);
}
}
pub(super) fn schema(&self) -> &SchemaDescriptor {
&self.cursor.schema
}
fn remaining_keys(&self) -> usize {
self.keys.len() - self.next
}
fn for_each_live_row(&mut self, max_rows: usize, mut f: impl FnMut(&ReadCursor)) -> usize {
assert!(max_rows > 0, "for_each_live_row: max_rows must be positive");
let stride = self.keys.stride();
let mut visited = 0;
while visited < max_rows && self.next * stride < self.keys.as_bytes().len() {
let key = &self.keys.as_bytes()[self.next * stride..(self.next + 1) * stride];
self.next += 1;
if self.cursor.seek_pk_group_ascending(key) {
self.cursor.for_each_pk_group_row(key, |c| {
visited += 1;
f(c);
});
}
}
visited
}
pub fn for_each_positive_capped(&mut self, cap: usize, mut f: impl FnMut(&ReadCursor)) {
for key in self.keys.iter().skip(self.next) {
if cap == 0 || !self.cursor.seek_pk_group_ascending(key) {
continue;
}
let mut left = cap;
self.cursor.walk_positive_with_prefix_until(key, |c| {
f(c);
left -= 1;
match left {
0 => ControlFlow::Break(()),
_ => ControlFlow::Continue(()),
}
});
}
self.next = self.keys.len();
}
pub(super) fn drain_live_chunk(&mut self, max_rows: usize, skeletons: &mut SkeletonKeys) -> Option<Batch> {
if self.remaining_keys() == 0 {
return None;
}
let split = self.cursor.any_skeleton;
let mut picks: Vec<(u32, u32, i64)> = Vec::with_capacity(self.remaining_keys().min(max_rows));
let visited = self.for_each_live_row(max_rows, |c| {
if split && c.current_is_skeleton() {
skeletons.push(c.current_pk_bytes(), c.current_weight);
} else {
let (src, row) = c.current_position();
picks.push((src as u32, row as u32, c.current_weight));
}
});
let mut out = gather_rows(&self.cursor.sources, &self.cursor.schema, &picks);
out.certify_consolidated();
(visited > 0).then_some(out)
}
pub fn project_live(&mut self, cols: &[u32]) -> Result<Batch, String> {
let schema = self.cursor.schema;
let out_schema = project_schema(&schema, cols)?;
let locs: Vec<ColumnLocator> = cols.iter().map(|&c| schema.locate(c as usize)).collect();
let mut out = Batch::with_capacity(&out_schema, self.remaining_keys());
self.for_each_live_row(usize::MAX, |c| {
let (src, row) = c.current_row_source();
out.begin_row(c.current_pk_bytes(), 1);
out.append_cells_from(0, &locs, src, row);
out.commit_row();
});
Ok(out)
}
pub fn drain_chunk(&mut self, max_rows: usize) -> Option<Batch> {
let mut skeletons = SkeletonKeys::default();
let chunk = self.drain_live_chunk(max_rows, &mut skeletons);
skeletons.assert_none();
chunk
}
}