use super::ColChunk;
use crate::columnar::updates::UpdatesTyped;
use crate::columnar::collection::RecordedUpdates;
pub struct TrieChunker<U: crate::columnar::layout::ColumnarUpdate> {
blobs: Vec<(UpdatesTyped<U>, bool)>,
blob_records: usize,
ready: std::collections::VecDeque<UpdatesTyped<U>>,
stage: Option<ColChunk<U>>,
}
impl<U: crate::columnar::layout::ColumnarUpdate> Default for TrieChunker<U> {
fn default() -> Self {
Self {
blobs: Default::default(),
blob_records: 0,
ready: Default::default(),
stage: None,
}
}
}
impl<U: crate::columnar::layout::ColumnarUpdate> TrieChunker<U> {
fn consolidate_blobs(&mut self) -> UpdatesTyped<U> {
if self.blobs.len() == 1 && self.blobs[0].1 {
let (result, _) = self.blobs.pop().unwrap();
self.blob_records = 0;
return result;
}
let result = UpdatesTyped::<U>::form_unsorted(self.blobs.iter().flat_map(|(u, _)| u.iter()));
self.blobs.clear();
self.blob_records = 0;
result
}
fn absorb(&mut self, updates: UpdatesTyped<U>, consolidated: bool) {
self.blob_records += updates.len();
self.blobs.push((updates, consolidated));
}
}
impl<'a, U: crate::columnar::layout::ColumnarUpdate> timely::container::PushInto<&'a mut RecordedUpdates<U>> for TrieChunker<U> {
fn push_into(&mut self, container: &'a mut RecordedUpdates<U>) {
if container.updates.len() == 0 { return; }
let updates = std::mem::take(&mut container.updates).into_typed();
let consolidated = container.consolidated;
let len = updates.len();
if consolidated && len >= crate::columnar::LINK_TARGET { self.ready.push_back(updates); }
else if self.blob_records + len < 2 * crate::columnar::LINK_TARGET { self.absorb(updates, consolidated); }
else {
let input_residual = if len >= crate::columnar::LINK_TARGET {
let cons = if consolidated { updates } else { updates.consolidate() };
if cons.len() >= crate::columnar::LINK_TARGET { self.ready.push_back(cons); None }
else if cons.len() > 0 { Some((cons, true)) }
else { None }
}
else { Some((updates, consolidated)) };
let blobs_residual = if self.blob_records >= crate::columnar::LINK_TARGET {
let cons = self.consolidate_blobs();
if cons.len() >= crate::columnar::LINK_TARGET { self.ready.push_back(cons); None }
else if cons.len() > 0 { Some((cons, true)) }
else { None }
}
else { None };
if let Some((r, c)) = input_residual { self.absorb(r, c); }
if let Some((r, c)) = blobs_residual { self.absorb(r, c); }
}
}
}
impl<U: crate::columnar::layout::ColumnarUpdate> timely::container::ContainerBuilder for TrieChunker<U> {
type Container = ColChunk<U>;
fn extract(&mut self) -> Option<&mut Self::Container> {
self.stage = self.ready.pop_front().map(ColChunk::from_trie);
self.stage.as_mut()
}
fn finish(&mut self) -> Option<&mut Self::Container> {
if !self.blobs.is_empty() {
let cons = self.consolidate_blobs();
if cons.len() > 0 { self.ready.push_back(cons); }
}
self.extract()
}
}