use nohash_hasher::IntMap;
use re_log_types::{DataCell, EntityPath, RowId, StoreId, TimeInt, TimePoint, Timeline};
use re_types_core::ComponentName;
use crate::StoreGeneration;
#[allow(unused_imports)]
use crate::{DataStore, StoreSubscriber};
#[derive(Debug, Clone, PartialEq)]
pub struct StoreEvent {
pub store_id: StoreId,
pub store_generation: StoreGeneration,
pub event_id: u64,
pub diff: StoreDiff,
}
impl std::ops::Deref for StoreEvent {
type Target = StoreDiff;
#[inline]
fn deref(&self) -> &Self::Target {
&self.diff
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StoreDiffKind {
Addition,
Deletion,
}
impl StoreDiffKind {
#[inline]
pub fn delta(&self) -> i64 {
match self {
StoreDiffKind::Addition => 1,
StoreDiffKind::Deletion => -1,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct StoreDiff {
pub kind: StoreDiffKind,
pub row_id: RowId,
pub times: Vec<(Timeline, TimeInt)>,
pub entity_path: EntityPath,
pub cells: IntMap<ComponentName, DataCell>,
}
impl StoreDiff {
#[inline]
pub fn addition(row_id: impl Into<RowId>, entity_path: impl Into<EntityPath>) -> Self {
Self {
kind: StoreDiffKind::Addition,
row_id: row_id.into(),
times: Default::default(),
entity_path: entity_path.into(),
cells: Default::default(),
}
}
#[inline]
pub fn deletion(row_id: impl Into<RowId>, entity_path: impl Into<EntityPath>) -> Self {
Self {
kind: StoreDiffKind::Deletion,
row_id: row_id.into(),
times: Default::default(),
entity_path: entity_path.into(),
cells: Default::default(),
}
}
#[inline]
pub fn at_timepoint(&mut self, timepoint: impl Into<TimePoint>) -> &mut Self {
self.times.extend(timepoint.into());
self
}
#[inline]
pub fn at_timestamp(
&mut self,
timeline: impl Into<Timeline>,
time: impl Into<TimeInt>,
) -> &mut Self {
self.times.push((timeline.into(), time.into()));
self
}
#[inline]
pub fn with_cells(&mut self, cells: impl IntoIterator<Item = DataCell>) -> &mut Self {
self.cells
.extend(cells.into_iter().map(|cell| (cell.component_name(), cell)));
self
}
#[inline]
pub fn timepoint(&self) -> TimePoint {
self.times.clone().into_iter().collect()
}
#[inline]
pub fn is_static(&self) -> bool {
self.times.is_empty()
}
#[inline]
pub fn delta(&self) -> i64 {
self.kind.delta()
}
#[inline]
pub fn num_components(&self) -> usize {
self.cells.len()
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use re_log_types::{
example_components::{MyColor, MyIndex, MyPoint},
DataRow, RowId, TimePoint, Timeline,
};
use re_types_core::Loggable as _;
use crate::{DataStore, GarbageCollectionOptions};
use super::*;
#[derive(Default, Debug, PartialEq, Eq)]
struct GlobalCounts {
row_ids: BTreeMap<RowId, i64>,
timelines: BTreeMap<Timeline, i64>,
entity_paths: BTreeMap<EntityPath, i64>,
component_names: BTreeMap<ComponentName, i64>,
times: BTreeMap<TimeInt, i64>,
num_static: i64,
}
impl GlobalCounts {
fn new(
row_ids: impl IntoIterator<Item = (RowId, i64)>, timelines: impl IntoIterator<Item = (Timeline, i64)>, entity_paths: impl IntoIterator<Item = (EntityPath, i64)>, component_names: impl IntoIterator<Item = (ComponentName, i64)>, times: impl IntoIterator<Item = (TimeInt, i64)>, num_static: i64,
) -> Self {
Self {
row_ids: row_ids.into_iter().collect(),
timelines: timelines.into_iter().collect(),
entity_paths: entity_paths.into_iter().collect(),
component_names: component_names.into_iter().collect(),
times: times.into_iter().collect(),
num_static,
}
}
}
impl GlobalCounts {
fn on_events(&mut self, events: &[StoreEvent]) {
for event in events {
let delta = event.delta();
*self.row_ids.entry(event.row_id).or_default() += delta;
*self
.entity_paths
.entry(event.entity_path.clone())
.or_default() += delta;
for component_name in event.cells.keys() {
*self.component_names.entry(*component_name).or_default() += delta;
}
if event.is_static() {
self.num_static += delta;
} else {
for &(timeline, time) in &event.times {
*self.timelines.entry(timeline).or_default() += delta;
*self.times.entry(time).or_default() += delta;
}
}
}
}
}
#[test]
fn store_events() -> anyhow::Result<()> {
let mut store = DataStore::new(
re_log_types::StoreId::random(re_log_types::StoreKind::Recording),
Default::default(),
);
let mut view = GlobalCounts::default();
let timeline_frame = Timeline::new_sequence("frame");
let timeline_other = Timeline::new_temporal("other");
let timeline_yet_another = Timeline::new_sequence("yet_another");
let row_id1 = RowId::new();
let timepoint1 = TimePoint::from_iter([
(timeline_frame, 42), (timeline_other, 666), (timeline_yet_another, 1), ]);
let entity_path1: EntityPath = "entity_a".into();
let row1 = DataRow::from_component_batches(
row_id1,
timepoint1.clone(),
entity_path1.clone(),
[&MyIndex::from_iter(0..10) as _],
)?;
view.on_events(&[store.insert_row(&row1)?]);
similar_asserts::assert_eq!(
GlobalCounts::new(
[
(row_id1, 1), ],
[
(timeline_frame, 1),
(timeline_other, 1),
(timeline_yet_another, 1),
],
[
(entity_path1.clone(), 1), ],
[
(MyIndex::name(), 1), ],
[
(42.try_into().unwrap(), 1), (666.try_into().unwrap(), 1),
(1.try_into().unwrap(), 1),
],
0,
),
view,
);
let row_id2 = RowId::new();
let timepoint2 = TimePoint::from_iter([
(timeline_frame, 42), (timeline_yet_another, 1), ]);
let entity_path2: EntityPath = "entity_b".into();
let row2 = {
let num_instances = 3;
let points: Vec<_> = (0..num_instances)
.map(|i| MyPoint::new(0.0, i as f32))
.collect();
let colors = vec![MyColor::from(0xFF0000FF)];
DataRow::from_component_batches(
row_id2,
timepoint2.clone(),
entity_path2.clone(),
[&points as _, &colors as _],
)?
};
view.on_events(&[store.insert_row(&row2)?]);
similar_asserts::assert_eq!(
GlobalCounts::new(
[
(row_id1, 1), (row_id2, 1),
],
[
(timeline_frame, 2),
(timeline_other, 1),
(timeline_yet_another, 2),
],
[
(entity_path1.clone(), 1), (entity_path2.clone(), 1), ],
[
(MyIndex::name(), 1), (MyPoint::name(), 1), (MyColor::name(), 1), ],
[
(42.try_into().unwrap(), 2), (666.try_into().unwrap(), 1),
(1.try_into().unwrap(), 2),
],
0,
),
view,
);
let row_id3 = RowId::new();
let timepoint3 = TimePoint::default();
let row3 = {
let num_instances = 6;
let colors = vec![MyColor::from(0x00DD00FF); num_instances];
DataRow::from_component_batches(
row_id3,
timepoint3.clone(),
entity_path2.clone(),
[
&MyIndex::from_iter(0..num_instances as _) as _,
&colors as _,
],
)?
};
view.on_events(&[store.insert_row(&row3)?]);
similar_asserts::assert_eq!(
GlobalCounts::new(
[
(row_id1, 1), (row_id2, 1),
(row_id3, 1),
],
[
(timeline_frame, 2),
(timeline_other, 1),
(timeline_yet_another, 2),
],
[
(entity_path1.clone(), 1), (entity_path2.clone(), 2), ],
[
(MyIndex::name(), 2), (MyPoint::name(), 1), (MyColor::name(), 2), ],
[
(42.try_into().unwrap(), 2), (666.try_into().unwrap(), 1),
(1.try_into().unwrap(), 2),
],
1,
),
view,
);
let events = store.gc(&GarbageCollectionOptions::gc_everything()).0;
view.on_events(&events);
similar_asserts::assert_eq!(
GlobalCounts::new(
[
(row_id1, 0), (row_id2, 0),
(row_id3, 1), ],
[
(timeline_frame, 0),
(timeline_other, 0),
(timeline_yet_another, 0),
],
[
(entity_path1.clone(), 0), (entity_path2.clone(), 1), ],
[
(MyIndex::name(), 1), (MyPoint::name(), 0), (MyColor::name(), 1), ],
[
(42.try_into().unwrap(), 0), (666.try_into().unwrap(), 0),
(1.try_into().unwrap(), 0),
],
1, ),
view,
);
Ok(())
}
}