4303af5a1f
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
89 lines
2.7 KiB
Rust
89 lines
2.7 KiB
Rust
//! The trace interface: operator state behind look up, read a range, apply a delta.
|
|
|
|
use crate::ZSet;
|
|
use std::collections::BTreeMap;
|
|
use std::ops::Bound;
|
|
|
|
/// An integrated `ZSet<(K, V)>`, indexed by `K`. Reads return owned values, so an
|
|
/// implementation may page or compress what it holds. See `context/code/zset.md`.
|
|
pub trait Trace<K, V> {
|
|
/// Adds `w` to the weight of `(k, v)`. A panic must leave the trace as it was: a
|
|
/// transaction (`Circuit::begin`) logs an apply only once it has returned.
|
|
fn apply(&mut self, k: &K, v: &V, w: i64);
|
|
/// The values at `k` with their non-zero weights, ascending by value.
|
|
fn lookup(&self, k: &K) -> Vec<(V, i64)>;
|
|
/// Every `(key, value, weight)` with a key in the range, ascending.
|
|
fn range(&self, lo: Bound<&K>, hi: Bound<&K>) -> Vec<(K, V, i64)>;
|
|
}
|
|
|
|
/// The in-memory trace: a sorted map from key to a Z-set of values.
|
|
#[derive(Clone, Debug)]
|
|
pub struct MemTrace<K, V> {
|
|
groups: BTreeMap<K, ZSet<V>>,
|
|
}
|
|
|
|
impl<K, V> Default for MemTrace<K, V> {
|
|
fn default() -> Self {
|
|
MemTrace {
|
|
groups: BTreeMap::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<K: Ord, V: Ord> MemTrace<K, V> {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
/// The number of keys with at least one value.
|
|
pub fn keys(&self) -> usize {
|
|
self.groups.len()
|
|
}
|
|
}
|
|
|
|
fn is_empty_range<K: Ord>(lo: Bound<&K>, hi: Bound<&K>) -> bool {
|
|
match (lo, hi) {
|
|
(Bound::Included(a), Bound::Included(b)) => a > b,
|
|
(Bound::Included(a) | Bound::Excluded(a), Bound::Included(b) | Bound::Excluded(b)) => {
|
|
a >= b
|
|
}
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
impl<K: Ord + Clone, V: Ord + Clone> Trace<K, V> for MemTrace<K, V> {
|
|
fn apply(&mut self, k: &K, v: &V, w: i64) {
|
|
// Every clone is made before the map changes, so a panicking one changes nothing.
|
|
match self.groups.get_mut(k) {
|
|
Some(group) => {
|
|
group.insert(v.clone(), w);
|
|
if group.is_zero() {
|
|
self.groups.remove(k);
|
|
}
|
|
}
|
|
None if w != 0 => {
|
|
let group = ZSet::singleton(v.clone(), w);
|
|
self.groups.insert(k.clone(), group);
|
|
}
|
|
None => {}
|
|
}
|
|
}
|
|
|
|
fn lookup(&self, k: &K) -> Vec<(V, i64)> {
|
|
self.groups
|
|
.get(k)
|
|
.map(|g| g.iter().map(|(v, w)| (v.clone(), w)).collect())
|
|
.unwrap_or_default()
|
|
}
|
|
|
|
fn range(&self, lo: Bound<&K>, hi: Bound<&K>) -> Vec<(K, V, i64)> {
|
|
if is_empty_range(lo, hi) {
|
|
return Vec::new();
|
|
}
|
|
self.groups
|
|
.range::<K, _>((lo, hi))
|
|
.flat_map(|(k, g)| g.iter().map(move |(v, w)| (k.clone(), v.clone(), w)))
|
|
.collect()
|
|
}
|
|
}
|