use crate::columnar::{layout, updates};
pub struct RecordedUpdates<U: layout::ColumnarUpdate> {
pub updates: updates::Updates<U>,
pub records: usize,
pub consolidated: bool,
}
impl<U: layout::ColumnarUpdate> Default for RecordedUpdates<U> {
fn default() -> Self { Self { updates: Default::default(), records: 0, consolidated: true } }
}
impl<U: layout::ColumnarUpdate> Clone for RecordedUpdates<U> {
fn clone(&self) -> Self { Self { updates: self.updates.clone(), records: self.records, consolidated: self.consolidated } }
}
impl<U: layout::ColumnarUpdate> timely::Accountable for RecordedUpdates<U> {
#[inline] fn record_count(&self) -> i64 { self.records as i64 }
}
impl<U: layout::ColumnarUpdate> timely::dataflow::channels::ContainerBytes for RecordedUpdates<U> {
fn from_bytes(mut bytes: timely::bytes::arc::Bytes) -> Self {
let prefix = bytes.extract_to(16);
let records = u64::from_le_bytes(prefix[0..8].try_into().unwrap()) as usize;
let consolidated = u64::from_le_bytes(prefix[8..16].try_into().unwrap()) != 0;
RecordedUpdates { updates: updates::Updates::read_from(bytes), records, consolidated }
}
fn length_in_bytes(&self) -> usize {
16 + self.updates.length_in_bytes()
}
fn into_bytes<W: std::io::Write>(&self, writer: &mut W) {
writer.write_all(&(self.records as u64).to_le_bytes()).unwrap();
writer.write_all(&(self.consolidated as u64).to_le_bytes()).unwrap();
self.updates.write_to(writer);
}
}
mod container_impls {
use columnar::{Columnar, Index, Len, Push};
use timely::progress::{Timestamp, timestamp::Refines};
use crate::difference::Abelian;
use crate::collection::containers::{Negate, Enter, Leave, ResultsIn};
use crate::columnar::layout::ColumnarUpdate as Update;
use crate::columnar::updates::{self, UpdatesTyped};
use super::RecordedUpdates;
impl<U: Update<Diff: Abelian>> Negate for RecordedUpdates<U> {
fn negate(self) -> Self {
use columnar::Container;
let RecordedUpdates { mut updates, records, consolidated } = self;
let view = updates.view();
let old_diffs = view.diffs.values;
let mut new_diffs = <<U::Diff as Columnar>::Container as Container>::with_capacity_for([old_diffs].into_iter());
let mut owned = U::Diff::default();
for i in 0..old_diffs.len() {
columnar::Columnar::copy_from(&mut owned, old_diffs.get(i));
owned.negate();
new_diffs.push(&owned);
}
updates.diffs.make_typed().values = new_diffs;
RecordedUpdates { updates, records, consolidated }
}
}
impl<K, V, T1, T2, R> Enter<T1, T2> for RecordedUpdates<(K, V, T1, R)>
where
(K, V, T1, R): Update<Key=K, Val=V, Time=T1, Diff=R>,
(K, V, T2, R): Update<Key=K, Val=V, Time=T2, Diff=R>,
T1: Timestamp + Columnar + Default + Clone,
T2: Refines<T1> + Columnar + Default + Clone,
K: Columnar, V: Columnar, R: Columnar,
{
type InnerContainer = RecordedUpdates<(K, V, T2, R)>;
fn enter(self) -> Self::InnerContainer {
use columnar::bytes::stash::Stash;
let RecordedUpdates { updates, records, consolidated } = self;
let times = updates.times.borrow();
let times_values = times.values;
let mut new_times = <<T2 as Columnar>::Container as Default>::default();
let mut t1_owned = T1::default();
for i in 0..times_values.len() {
Columnar::copy_from(&mut t1_owned, times_values.get(i));
let t2 = T2::to_inner(t1_owned.clone());
new_times.push(&t2);
}
let updates::Updates { keys, vals, mut times, diffs } = updates;
times.make_typed();
let Stash::Typed(times_lists) = times else { unreachable!() };
let times = Stash::Typed(updates::Lists {
values: new_times,
bounds: times_lists.bounds,
});
RecordedUpdates {
updates: updates::Updates { keys, vals, times, diffs },
records,
consolidated,
}
}
}
impl<K, V, T1, T2, R> Leave<T1, T2> for RecordedUpdates<(K, V, T1, R)>
where
(K, V, T1, R): Update<Key=K, Val=V, Time=T1, Diff=R>,
(K, V, T2, R): Update<Key=K, Val=V, Time=T2, Diff=R>,
T1: Refines<T2> + Columnar + Default + Clone,
T2: Timestamp + Columnar + Default + Clone,
K: Columnar, V: Columnar, R: Columnar,
{
type OuterContainer = RecordedUpdates<(K, V, T2, R)>;
fn leave(self) -> Self::OuterContainer {
use columnar::bytes::stash::Stash;
let RecordedUpdates { updates, records, consolidated: _ } = self;
let times = updates.times.borrow();
let times_values = times.values;
let mut new_times = <<T2 as Columnar>::Container as Default>::default();
let mut t1_owned = T1::default();
for i in 0..times_values.len() {
Columnar::copy_from(&mut t1_owned, times_values.get(i));
let t2: T2 = t1_owned.clone().to_outer();
new_times.push(&t2);
}
let updates::Updates { keys, vals, mut times, diffs } = updates;
times.make_typed();
let Stash::Typed(times_lists) = times else { unreachable!() };
let times = Stash::Typed(updates::Lists {
values: new_times,
bounds: times_lists.bounds,
});
let mid = updates::Updates { keys, vals, times, diffs };
RecordedUpdates {
updates: mid.into_typed().consolidate().into(),
records,
consolidated: true,
}
}
}
impl<U: Update> ResultsIn<<U::Time as Timestamp>::Summary> for RecordedUpdates<U> {
fn results_in(self, step: &<U::Time as Timestamp>::Summary) -> Self {
use timely::progress::PathSummary;
let mut output = UpdatesTyped::<U>::default();
let mut time_owned = U::Time::default();
for (k, v, t, d) in self.updates.view().iter() {
Columnar::copy_from(&mut time_owned, t);
if let Some(new_time) = step.results_in(&time_owned) {
output.push((k, v, &new_time, d));
}
}
RecordedUpdates { updates: output.into(), records: self.records, consolidated: false }
}
}
}
#[cfg(test)]
mod test {
use columnar::Push;
use timely::dataflow::channels::ContainerBytes;
use crate::columnar::updates::UpdatesTyped;
use super::RecordedUpdates;
type Upd = (u64, u64, u64, i64);
#[test]
fn recorded_updates_bytes_round_trip() {
let rows: Vec<Upd> = vec![
(0, 0, 0, 1), (0, 1, 0, 1), (1, 0, 0, 2), (1, 0, 3, -1), (2, 5, 7, 4),
];
let mut trie = UpdatesTyped::<Upd>::default();
for (k, v, t, d) in &rows { trie.push((k, v, t, d)); }
let trie = trie.consolidate();
let records = trie.len();
let original = RecordedUpdates::<Upd> { updates: trie.into(), records, consolidated: true };
let want: Vec<Upd> = original.updates.view().iter().map(|(k, v, t, d)| (*k, *v, *t, *d)).collect();
let mut buf = Vec::new();
original.into_bytes(&mut buf);
assert_eq!(buf.len(), original.length_in_bytes(), "length_in_bytes disagrees with into_bytes");
let bytes = timely::bytes::arc::BytesMut::from(buf).freeze();
let decoded = RecordedUpdates::<Upd>::from_bytes(bytes);
assert_eq!(decoded.records, records);
assert!(decoded.consolidated);
let got: Vec<Upd> = decoded.updates.view().iter().map(|(k, v, t, d)| (*k, *v, *t, *d)).collect();
assert_eq!(got, want);
}
}