Skip to main content

sim_kernel/
datum_store.rs

1//! The [`DatumStore`] contract: interning and resolving content-addressed data.
2//!
3//! This is protocol the libraries implement; the kernel ships a simple
4//! `BTreeMap`-backed store but defines no persistent storage policy.
5
6use std::collections::BTreeMap;
7
8use crate::{datum::Datum, error::Result, ref_id::ContentId};
9
10/// Contract for interning and resolving content-addressed [`Datum`]s.
11///
12/// This is protocol the libraries implement; the kernel ships a simple
13/// [`BTreeDatumStore`] but defines no persistent storage policy.
14pub trait DatumStore {
15    /// Interns `datum`, returning its [`ContentId`]; re-interning equal data is
16    /// idempotent.
17    fn intern(&mut self, datum: Datum) -> Result<ContentId>;
18    /// Resolves the datum for `id`, or `None` when it is not stored here.
19    fn get(&self, id: &ContentId) -> Result<Option<&Datum>>;
20    /// Returns whether `id` is interned in this store.
21    fn contains(&self, id: &ContentId) -> bool;
22}
23
24/// In-memory [`DatumStore`] backed by a [`BTreeMap`]; the kernel default.
25#[derive(Clone, Debug, Default)]
26pub struct BTreeDatumStore {
27    data: BTreeMap<ContentId, Datum>,
28}
29
30impl BTreeDatumStore {
31    /// Creates an empty store.
32    pub fn new() -> Self {
33        Self::default()
34    }
35
36    pub(crate) fn insert_known(&mut self, id: ContentId, datum: Datum) -> Option<Datum> {
37        self.data.insert(id, datum)
38    }
39
40    /// Returns the number of interned datums.
41    pub fn len(&self) -> usize {
42        self.data.len()
43    }
44
45    /// Returns whether the store holds no datums.
46    pub fn is_empty(&self) -> bool {
47        self.data.is_empty()
48    }
49}
50
51impl DatumStore for BTreeDatumStore {
52    fn intern(&mut self, datum: Datum) -> Result<ContentId> {
53        let id = datum.content_id()?;
54        self.data.entry(id.clone()).or_insert(datum);
55        Ok(id)
56    }
57
58    fn get(&self, id: &ContentId) -> Result<Option<&Datum>> {
59        Ok(self.data.get(id))
60    }
61
62    fn contains(&self, id: &ContentId) -> bool {
63        self.data.contains_key(id)
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use crate::{Datum, Symbol, datum_content_algorithm};
70
71    use super::*;
72
73    #[test]
74    fn datum_store_intern_followed_by_get_returns_original_datum() {
75        let mut store = BTreeDatumStore::new();
76        let datum = Datum::String("stored".to_owned());
77
78        let id = store.intern(datum.clone()).unwrap();
79
80        assert_eq!(store.get(&id).unwrap(), Some(&datum));
81        assert!(store.contains(&id));
82    }
83
84    #[test]
85    fn datum_store_unresolved_external_content_ids_return_none() {
86        let store = BTreeDatumStore::new();
87        let id = ContentId::from_bytes(datum_content_algorithm(), [9; 32]);
88
89        assert_eq!(store.get(&id).unwrap(), None);
90        assert!(!store.contains(&id));
91    }
92
93    #[test]
94    fn datum_store_intern_reuses_equal_content_id() {
95        let mut store = BTreeDatumStore::new();
96        let left = Datum::Symbol(Symbol::new("same"));
97        let right = Datum::Symbol(Symbol::new("same"));
98
99        let left_id = store.intern(left).unwrap();
100        let right_id = store.intern(right).unwrap();
101
102        assert_eq!(left_id, right_id);
103        assert_eq!(store.len(), 1);
104    }
105}