use std::collections::VecDeque;
use timely::progress::Antichain;
use timely::progress::frontier::AntichainRef;
use crate::lattice::Lattice;
use crate::trace::{Batch, BatchReader, Description, Navigable};
use crate::trace::cursor::Cursor;
use crate::trace::implementations::BatchContainer;
pub mod vec;
pub trait Chunk: Sized + Clone {
type Time: Lattice + timely::progress::Timestamp;
const TARGET: usize;
fn len(&self) -> usize;
fn merge(in1: &mut VecDeque<Self>, in2: &mut VecDeque<Self>, out: &mut VecDeque<Self>);
fn extract(
input: &mut VecDeque<Self>,
frontier: AntichainRef<Self::Time>,
residual: &mut Antichain<Self::Time>,
keep: &mut VecDeque<Self>,
ship: &mut VecDeque<Self>,
);
fn advance(
input: &mut VecDeque<Self>,
frontier: AntichainRef<Self::Time>,
done: bool,
out: &mut VecDeque<Self>,
);
fn settle(input: &mut VecDeque<Self>, done: bool, out: &mut VecDeque<Self>);
}
pub trait NavigableChunk: Chunk + Navigable<Cursor: Cursor<Time = <Self as Chunk>::Time>> {
fn bounds(&self) -> (
(<Self::Cursor as Cursor>::Key<'_>, <Self::Cursor as Cursor>::Val<'_>, <Self::Cursor as Cursor>::TimeGat<'_>),
(<Self::Cursor as Cursor>::Key<'_>, <Self::Cursor as Cursor>::Val<'_>, <Self::Cursor as Cursor>::TimeGat<'_>),
);
}
pub fn pack<C: Chunk>(
input: &mut VecDeque<C>,
done: bool,
out: &mut VecDeque<C>,
mut combine: impl FnMut(&mut C, C),
mut split: impl FnMut(C, usize) -> (C, C),
mut seal: impl FnMut(C) -> C,
) {
let mut carry: Option<C> = None;
while let Some(chunk) = input.pop_front() {
match carry.take() {
None => pack_absorb(chunk, &mut carry, out, &mut split, &mut seal),
Some(mut c) if c.len() + chunk.len() <= C::TARGET => {
combine(&mut c, chunk);
if c.len() == C::TARGET { out.push_back(seal(c)); } else { carry = Some(c); }
}
Some(c) => {
out.push_back(seal(c));
pack_absorb(chunk, &mut carry, out, &mut split, &mut seal);
}
}
}
if let Some(c) = carry {
if done { out.push_back(seal(c)); } else { input.push_front(c); }
}
}
fn pack_absorb<C, S, L>(chunk: C, carry: &mut Option<C>, out: &mut VecDeque<C>, split: &mut S, seal: &mut L)
where
C: Chunk,
S: FnMut(C, usize) -> (C, C),
L: FnMut(C) -> C,
{
match chunk.len().cmp(&C::TARGET) {
std::cmp::Ordering::Equal => out.push_back(seal(chunk)),
std::cmp::Ordering::Less => *carry = Some(chunk),
std::cmp::Ordering::Greater => {
let mut rest = chunk;
loop {
let (head, tail) = split(rest, C::TARGET);
out.push_back(seal(head));
if tail.len() >= C::TARGET { rest = tail; }
else { if tail.len() > 0 { *carry = Some(tail); } break; }
}
}
}
}
type KeyCon<C> = <<C as Navigable>::Cursor as Cursor>::KeyContainer;
type ValCon<C> = <<C as Navigable>::Cursor as Cursor>::ValContainer;
pub struct ChunkBatch<C: Chunk> {
pub chunks: Vec<C>,
pub description: Description<C::Time>,
}
impl<C: Chunk> ChunkBatch<C> {
pub fn new(chunks: Vec<C>, description: Description<C::Time>) -> Self {
for chunk in &chunks {
assert!(chunk.len() > 0, "ChunkBatch chunks must be non-empty");
}
ChunkBatch { chunks, description }
}
}
impl<C: NavigableChunk> crate::trace::Navigable for ChunkBatch<C> {
type Cursor = ChunkBatchCursor<C>;
fn cursor(&self) -> Self::Cursor {
ChunkBatchCursor { key_chunk: 0, chunk: 0, inner: self.chunks.first().map(C::cursor) }
}
}
impl<C: Chunk> BatchReader for ChunkBatch<C> {
type Time = C::Time;
fn len(&self) -> usize { self.chunks.iter().map(C::len).sum() }
fn description(&self) -> &Description<Self::Time> { &self.description }
}
impl<C: Chunk + Default + 'static> Batch for ChunkBatch<C>
where
C::Time: timely::progress::Timestamp + Lattice + Ord,
{
type Merger = ChunkBatchMerger<C>;
fn empty(lower: Antichain<Self::Time>, upper: Antichain<Self::Time>) -> Self {
use timely::progress::Timestamp;
let since = Antichain::from_elem(Self::Time::minimum());
ChunkBatch::new(Vec::new(), Description::new(lower, upper, since))
}
}
pub type ChunkBatcher<C> = crate::trace::implementations::merge_batcher::MergeBatcher<ChunkMerger<C>>;
pub type ChunkSpine<C> = crate::trace::implementations::spine_fueled::Spine<std::rc::Rc<ChunkBatch<C>>>;
pub type ChunkBuilder<C> = crate::trace::rc_blanket_impls::RcBuilder<ChunkBatchBuilder<C>>;
pub struct ChunkBatchCursor<C: NavigableChunk> {
key_chunk: usize,
chunk: usize,
inner: Option<C::Cursor>,
}
impl<C: NavigableChunk> ChunkBatchCursor<C> {
fn goto(&mut self, c: usize, storage: &ChunkBatch<C>) {
self.chunk = c;
self.inner = storage.chunks.get(c).map(C::cursor);
}
fn key_spills(s: &ChunkBatch<C>, c: usize, k: <C::Cursor as Cursor>::Key<'_>) -> bool {
<KeyCon<C> as BatchContainer>::reborrow(s.chunks[c].bounds().1.0) == <KeyCon<C> as BatchContainer>::reborrow(k)
&& <KeyCon<C> as BatchContainer>::reborrow(s.chunks[c + 1].bounds().0.0) == <KeyCon<C> as BatchContainer>::reborrow(k)
}
fn val_spills(s: &ChunkBatch<C>, c: usize, k: <C::Cursor as Cursor>::Key<'_>, v: <C::Cursor as Cursor>::Val<'_>) -> bool {
Self::key_spills(s, c, k)
&& <ValCon<C> as BatchContainer>::reborrow(s.chunks[c].bounds().1.1) == <ValCon<C> as BatchContainer>::reborrow(v)
&& <ValCon<C> as BatchContainer>::reborrow(s.chunks[c + 1].bounds().0.1) == <ValCon<C> as BatchContainer>::reborrow(v)
}
fn locate_key(s: &ChunkBatch<C>, hint: usize, key: <C::Cursor as Cursor>::Key<'_>) -> usize {
let n = s.chunks.len();
let lt = |i: usize| <KeyCon<C> as BatchContainer>::reborrow(s.chunks[i].bounds().1.0)
.lt(&<KeyCon<C> as BatchContainer>::reborrow(key));
let hint = hint.min(n);
let lo = if hint == 0 || lt(hint - 1) { hint } else { 0 };
if lo >= n || !lt(lo) { return lo; }
let (mut prev, mut step) = (lo, 1usize);
while prev + step < n && lt(prev + step) { prev += step; step <<= 1; }
let (mut a, mut b) = (prev + 1, (prev + step).min(n));
while a < b { let m = a + (b - a) / 2; if lt(m) { a = m + 1; } else { b = m; } }
a
}
}
impl<C: NavigableChunk> Cursor for ChunkBatchCursor<C> {
type Storage = ChunkBatch<C>;
type KeyContainer = <C::Cursor as Cursor>::KeyContainer;
type Key<'a> = <C::Cursor as Cursor>::Key<'a>;
type ValContainer = <C::Cursor as Cursor>::ValContainer;
type Val<'a> = <C::Cursor as Cursor>::Val<'a>;
type ValOwn = <C::Cursor as Cursor>::ValOwn;
type TimeContainer = <C::Cursor as Cursor>::TimeContainer;
type TimeGat<'a> = <C::Cursor as Cursor>::TimeGat<'a>;
type Time = <C::Cursor as Cursor>::Time;
type DiffContainer = <C::Cursor as Cursor>::DiffContainer;
type DiffGat<'a> = <C::Cursor as Cursor>::DiffGat<'a>;
type Diff = <C::Cursor as Cursor>::Diff;
fn key_valid(&self, s: &Self::Storage) -> bool { self.chunk < s.chunks.len() && self.inner.as_ref().is_some_and(|i| i.key_valid(&s.chunks[self.chunk])) }
fn val_valid(&self, s: &Self::Storage) -> bool { self.chunk < s.chunks.len() && self.inner.as_ref().is_some_and(|i| i.val_valid(&s.chunks[self.chunk])) }
fn key<'a>(&self, s: &'a Self::Storage) -> Self::Key<'a> { self.inner.as_ref().unwrap().key(&s.chunks[self.chunk]) }
fn val<'a>(&self, s: &'a Self::Storage) -> Self::Val<'a> { self.inner.as_ref().unwrap().val(&s.chunks[self.chunk]) }
fn get_key<'a>(&self, s: &'a Self::Storage) -> Option<Self::Key<'a>> { if self.key_valid(s) { Some(self.key(s)) } else { None } }
fn get_val<'a>(&self, s: &'a Self::Storage) -> Option<Self::Val<'a>> { if self.val_valid(s) { Some(self.val(s)) } else { None } }
fn map_times<L: FnMut(Self::TimeGat<'_>, Self::DiffGat<'_>)>(&mut self, s: &Self::Storage, mut logic: L) {
if !self.val_valid(s) { return; }
let (k, v) = (self.key(s), self.val(s));
self.inner.as_mut().unwrap().map_times(&s.chunks[self.chunk], &mut logic);
let mut c = self.chunk;
while c + 1 < s.chunks.len() && Self::val_spills(s, c, k, v) {
c += 1;
s.chunks[c].cursor().map_times(&s.chunks[c], &mut logic);
}
}
fn step_key(&mut self, s: &Self::Storage) {
if !self.key_valid(s) { return; }
let n = s.chunks.len();
let k = self.key(s);
while self.chunk + 1 < n && Self::key_spills(s, self.chunk, k) {
self.goto(self.chunk + 1, s);
}
{
let inner = self.inner.as_mut().unwrap();
inner.seek_key(&s.chunks[self.chunk], k);
inner.step_key(&s.chunks[self.chunk]);
}
if !self.inner.as_ref().unwrap().key_valid(&s.chunks[self.chunk]) && self.chunk + 1 < n {
self.goto(self.chunk + 1, s);
}
self.key_chunk = self.chunk;
}
fn seek_key(&mut self, s: &Self::Storage, key: Self::Key<'_>) {
let n = s.chunks.len();
let lo = Self::locate_key(s, self.key_chunk, key);
self.goto(lo, s);
self.key_chunk = lo;
if lo < n { self.inner.as_mut().unwrap().seek_key(&s.chunks[lo], key); }
}
fn step_val(&mut self, s: &Self::Storage) {
if !self.val_valid(s) { return; }
let n = s.chunks.len();
let (k, v) = (self.key(s), self.val(s));
while self.chunk + 1 < n && Self::val_spills(s, self.chunk, k, v) {
self.goto(self.chunk + 1, s);
}
self.inner.as_mut().unwrap().step_val(&s.chunks[self.chunk]);
if !self.inner.as_ref().unwrap().val_valid(&s.chunks[self.chunk])
&& self.chunk + 1 < n && Self::key_spills(s, self.chunk, k)
{
self.goto(self.chunk + 1, s);
self.inner.as_mut().unwrap().seek_key(&s.chunks[self.chunk], k);
}
}
fn seek_val(&mut self, s: &Self::Storage, val: Self::Val<'_>) {
if !self.key_valid(s) { return; }
let n = s.chunks.len();
let k = self.key(s);
loop {
self.inner.as_mut().unwrap().seek_val(&s.chunks[self.chunk], val);
if self.inner.as_ref().unwrap().val_valid(&s.chunks[self.chunk]) { return; }
if self.chunk + 1 < n && Self::key_spills(s, self.chunk, k) {
self.goto(self.chunk + 1, s);
self.inner.as_mut().unwrap().seek_key(&s.chunks[self.chunk], k);
} else {
return;
}
}
}
fn rewind_keys(&mut self, s: &Self::Storage) {
self.key_chunk = 0;
self.goto(0, s);
}
fn rewind_vals(&mut self, s: &Self::Storage) {
if !self.key_valid(s) { return; }
let k = self.key(s);
let kc = self.key_chunk;
self.goto(kc, s);
self.inner.as_mut().unwrap().seek_key(&s.chunks[kc], k);
}
}
pub struct ChunkMerger<C> {
_marker: std::marker::PhantomData<C>,
}
impl<C> Default for ChunkMerger<C> {
fn default() -> Self { Self { _marker: std::marker::PhantomData } }
}
impl<C> crate::trace::implementations::merge_batcher::Merger for ChunkMerger<C>
where
C: Chunk + Default + 'static,
C::Time: Clone + timely::PartialOrder + 'static,
{
type Chunk = C;
type Time = C::Time;
fn merge(
&mut self,
list1: Vec<C>,
list2: Vec<C>,
output: &mut Vec<C>,
_stash: &mut Vec<C>,
) {
let mut in1: VecDeque<C> = list1.into();
let mut in2: VecDeque<C> = list2.into();
let (mut staged, mut settled) = (VecDeque::new(), VecDeque::new());
while !in1.is_empty() && !in2.is_empty() {
C::merge(&mut in1, &mut in2, &mut staged);
C::settle(&mut staged, false, &mut settled);
}
for tail in in1.drain(..).chain(in2.drain(..)) {
staged.push_back(tail);
C::settle(&mut staged, false, &mut settled);
}
C::settle(&mut staged, true, &mut settled);
output.extend(settled);
}
fn extract(
&mut self,
merged: Vec<C>,
upper: AntichainRef<C::Time>,
frontier: &mut Antichain<C::Time>,
ship: &mut Vec<C>,
kept: &mut Vec<C>,
_stash: &mut Vec<C>,
) {
let mut input: VecDeque<C> = merged.into();
let (mut keep, mut shipped) = (VecDeque::new(), VecDeque::new());
let (mut kept_q, mut shipped_q) = (VecDeque::new(), VecDeque::new());
while !input.is_empty() {
C::extract(&mut input, upper, frontier, &mut keep, &mut shipped);
C::settle(&mut keep, false, &mut kept_q);
C::settle(&mut shipped, false, &mut shipped_q);
}
C::settle(&mut keep, true, &mut kept_q);
C::settle(&mut shipped, true, &mut shipped_q);
kept.extend(kept_q);
ship.extend(shipped_q);
}
fn len(chunk: &C) -> usize { chunk.len() }
}
pub struct ChunkBatchMerger<C: Chunk> {
frontier: Antichain<C::Time>,
lower: Antichain<C::Time>,
upper: Antichain<C::Time>,
in1: VecDeque<C>,
in2: VecDeque<C>,
idx1: usize,
idx2: usize,
merged: VecDeque<C>,
advanced: VecDeque<C>,
settled: VecDeque<C>,
complete: bool,
}
impl<C> crate::trace::Merger<ChunkBatch<C>> for ChunkBatchMerger<C>
where
C: Chunk + Default + 'static,
C::Time: timely::progress::Timestamp + Lattice + Ord + 'static,
{
fn new(source1: &ChunkBatch<C>, source2: &ChunkBatch<C>, frontier: AntichainRef<C::Time>) -> Self {
let lower = source1.description.lower().meet(source2.description.lower());
let upper = source1.description.upper().join(source2.description.upper());
Self {
frontier: frontier.to_owned(),
lower,
upper,
in1: VecDeque::new(),
in2: VecDeque::new(),
idx1: 0,
idx2: 0,
merged: VecDeque::new(),
advanced: VecDeque::new(),
settled: VecDeque::new(),
complete: false,
}
}
fn work(&mut self, source1: &ChunkBatch<C>, source2: &ChunkBatch<C>, fuel: &mut isize) {
if self.complete { return; }
while *fuel > 0 {
const BURST: usize = 8;
while self.in1.len() < BURST && self.idx1 < source1.chunks.len() {
self.in1.push_back(source1.chunks[self.idx1].clone());
self.idx1 += 1;
}
while self.in2.len() < BURST && self.idx2 < source2.chunks.len() {
self.in2.push_back(source2.chunks[self.idx2].clone());
self.idx2 += 1;
}
let mut produced = VecDeque::new();
if !self.in1.is_empty() && !self.in2.is_empty() {
C::merge(&mut self.in1, &mut self.in2, &mut produced);
} else if let Some(chunk) = self.in1.pop_front().or_else(|| self.in2.pop_front()) {
produced.push_back(chunk);
} else {
C::advance(&mut self.merged, self.frontier.borrow(), true, &mut self.advanced);
C::settle(&mut self.advanced, true, &mut self.settled);
self.complete = true;
break;
}
let work: usize = produced.iter().map(C::len).sum();
self.merged.extend(produced);
C::advance(&mut self.merged, self.frontier.borrow(), false, &mut self.advanced);
C::settle(&mut self.advanced, false, &mut self.settled);
*fuel -= work as isize;
}
}
fn done(self) -> ChunkBatch<C> {
debug_assert!(self.merged.is_empty() && self.advanced.is_empty());
let description = Description::new(self.lower, self.upper, self.frontier);
ChunkBatch::new(self.settled.into(), description)
}
}
pub struct ChunkBatchBuilder<C: Chunk> {
input: VecDeque<C>,
output: VecDeque<C>,
}
impl<C> crate::trace::Builder for ChunkBatchBuilder<C>
where
C: Chunk + Default + 'static,
C::Time: timely::progress::Timestamp,
{
type Input = C;
type Time = C::Time;
type Output = ChunkBatch<C>;
fn with_capacity(_keys: usize, _vals: usize, _upds: usize) -> Self {
Self { input: VecDeque::new(), output: VecDeque::new() }
}
fn push(&mut self, chunk: &mut C) {
let chunk = std::mem::take(chunk);
if chunk.len() > 0 {
self.input.push_back(chunk);
C::settle(&mut self.input, false, &mut self.output);
}
}
fn done(self, description: Description<C::Time>) -> ChunkBatch<C> {
let ChunkBatchBuilder { mut input, mut output } = self;
C::settle(&mut input, true, &mut output);
ChunkBatch::new(output.into(), description)
}
fn seal(chain: &mut Vec<C>, description: Description<C::Time>) -> ChunkBatch<C> {
ChunkBatch::new(settle_all(std::mem::take(chain)), description)
}
}
pub fn is_graded<C: Chunk>(chunks: &[C]) -> bool {
chunks.iter().all(|c| c.len() <= C::TARGET)
&& chunks.windows(2).all(|w| w[0].len() + w[1].len() > C::TARGET)
}
pub fn settle_all<C: Chunk>(input: impl IntoIterator<Item = C>) -> Vec<C> {
let mut input: VecDeque<C> = input.into_iter().collect();
let mut out = VecDeque::new();
C::settle(&mut input, true, &mut out);
debug_assert!(input.is_empty());
out.into()
}
pub fn merge_chains<C: Chunk>(
chain1: Vec<C>,
chain2: Vec<C>,
out: &mut VecDeque<C>,
) {
let mut in1: VecDeque<C> = chain1.into();
let mut in2: VecDeque<C> = chain2.into();
while !in1.is_empty() && !in2.is_empty() {
C::merge(&mut in1, &mut in2, out);
}
out.extend(in1.drain(..));
out.extend(in2.drain(..));
}