use std::sync::Arc;
use super::Snapshot;
pub(crate) struct SnapMemo<K: PartialEq, V> {
slot: Option<(Arc<Snapshot>, K, V)>,
}
impl<K: PartialEq, V> Default for SnapMemo<K, V> {
fn default() -> Self {
Self { slot: None }
}
}
impl<K: PartialEq, V> SnapMemo<K, V> {
pub(crate) fn read(
&mut self,
snap: &Arc<Snapshot>,
key: K,
build: &mut dyn FnMut() -> V,
) -> &V {
let hit = match &self.slot {
Some((held, k, _)) => Arc::ptr_eq(held, snap) && *k == key,
None => false,
};
if !hit {
self.slot = None;
}
let fill = || (Arc::clone(snap), key, build());
&self.slot.get_or_insert_with(fill).2
}
}
#[cfg(test)]
mod tests {
use super::*;
fn snap() -> Arc<Snapshot> {
Arc::new(Snapshot::empty(0))
}
#[test]
fn a_second_frame_on_the_same_snapshot_and_key_builds_nothing() {
let mut memo: SnapMemo<&str, u32> = SnapMemo::default();
let snap = snap();
let mut builds = 0;
for _ in 0..3 {
let v = *memo.read(&snap, "agent-1", &mut || {
builds += 1;
7
});
assert_eq!(v, 7);
}
assert_eq!(builds, 1, "same snapshot, same key: one build");
}
#[test]
fn a_new_snapshot_rebuilds_even_under_the_same_key() {
let mut memo: SnapMemo<&str, u32> = SnapMemo::default();
let (a, b) = (snap(), snap());
let mut builds = 0;
let mut bump = || {
builds += 1;
builds
};
assert_eq!(*memo.read(&a, "agent-1", &mut bump), 1);
assert_eq!(*memo.read(&b, "agent-1", &mut bump), 2, "new snapshot");
}
#[test]
fn the_shells_live_memo_shape_builds_once_per_snapshot() {
use crate::fork::Choices;
use std::path::PathBuf;
let snap = snap();
let key = || (PathBuf::from("/ws"), "agent-1".to_string());
let mut builds = 0;
let mut fork: SnapMemo<(PathBuf, String), Choices> = SnapMemo::default();
for _ in 0..2 {
let _ = fork.read(&snap, key(), &mut || {
builds += 1;
Choices::default()
});
}
assert_eq!(builds, 1, "one derivation of the fork's choices");
}
#[test]
fn a_new_key_rebuilds_and_evicts_the_old_answer() {
let mut memo: SnapMemo<&str, u32> = SnapMemo::default();
let snap = snap();
let mut builds = 0;
let mut bump = || {
builds += 1;
builds
};
assert_eq!(*memo.read(&snap, "agent-1", &mut bump), 1);
assert_eq!(*memo.read(&snap, "agent-2", &mut bump), 2, "new key");
assert_eq!(*memo.read(&snap, "agent-1", &mut bump), 3);
}
}