Skip to main content

sim_kernel/
handle_store.rs

1//! The [`HandleStore`] contract: interning values behind opaque handle ids.
2//!
3//! This is protocol the libraries implement; the kernel ships a default store
4//! and defines the handle-id identity, not the lifetime policy.
5
6use std::collections::{BTreeMap, HashMap};
7
8use crate::{
9    ref_id::{HandleId, HandleSeed, HandleSequence},
10    value::Value,
11};
12
13/// Contract for interning runtime [`Value`]s behind opaque [`HandleId`]s.
14///
15/// This is protocol the libraries implement; the kernel ships a default
16/// [`BTreeHandleStore`] and defines the handle-id identity, not the lifetime
17/// policy.
18pub trait HandleStore {
19    /// Interns `value`, returning its handle; equal values reuse one handle.
20    fn intern(&mut self, value: Value) -> HandleId;
21    /// Resolves the value for `id`, or `None` when it is not stored here.
22    fn get(&self, id: &HandleId) -> Option<&Value>;
23    /// Returns whether `id` is interned in this store.
24    fn contains(&self, id: &HandleId) -> bool;
25    /// Returns the existing handle for `value`, if one is interned.
26    fn handle_for_value(&self, value: &Value) -> Option<HandleId>;
27}
28
29/// In-memory [`HandleStore`] keyed by [`HandleId`] with a reverse value index;
30/// the kernel default.
31#[derive(Clone, Debug)]
32pub struct BTreeHandleStore {
33    sequence: HandleSequence,
34    values: BTreeMap<HandleId, Value>,
35    handles_by_value: HashMap<Value, HandleId>,
36}
37
38impl BTreeHandleStore {
39    /// Creates an empty store.
40    pub fn new(seed: HandleSeed) -> Self {
41        Self {
42            sequence: seed.sequence(),
43            values: BTreeMap::new(),
44            handles_by_value: HashMap::new(),
45        }
46    }
47
48    /// Returns the number of interned values.
49    pub fn len(&self) -> usize {
50        self.values.len()
51    }
52
53    /// Returns whether the store holds no values.
54    pub fn is_empty(&self) -> bool {
55        self.values.is_empty()
56    }
57
58    /// Allocates an identity without interning a value.
59    pub fn fresh_handle(&mut self) -> HandleId {
60        self.sequence.next_handle()
61    }
62}
63
64impl HandleStore for BTreeHandleStore {
65    fn intern(&mut self, value: Value) -> HandleId {
66        if let Some(handle) = self.handles_by_value.get(&value) {
67            return *handle;
68        }
69
70        let handle = self.fresh_handle();
71        self.handles_by_value.insert(value.clone(), handle);
72        self.values.insert(handle, value);
73        handle
74    }
75
76    fn get(&self, id: &HandleId) -> Option<&Value> {
77        self.values.get(id)
78    }
79
80    fn contains(&self, id: &HandleId) -> bool {
81        self.values.contains_key(id)
82    }
83
84    fn handle_for_value(&self, value: &Value) -> Option<HandleId> {
85        self.handles_by_value.get(value).copied()
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use crate::{DefaultFactory, Factory};
92
93    use super::*;
94
95    fn factory() -> DefaultFactory {
96        DefaultFactory
97    }
98
99    #[test]
100    fn handle_store_intern_followed_by_get_returns_original_value() {
101        let mut store = BTreeHandleStore::new(HandleSeed::new(7));
102        let value = factory().string("stored".to_owned()).unwrap();
103
104        let handle = store.intern(value.clone());
105
106        assert_eq!(store.get(&handle), Some(&value));
107        assert!(store.contains(&handle));
108    }
109
110    #[test]
111    fn handle_store_reuses_handle_for_same_value() {
112        let mut store = BTreeHandleStore::new(HandleSeed::new(7));
113        let value = factory().bool(true).unwrap();
114
115        let first = store.intern(value.clone());
116        let second = store.intern(value);
117
118        assert_eq!(first, second);
119        assert_eq!(store.len(), 1);
120    }
121
122    #[test]
123    fn handle_store_distinguishes_distinct_values() {
124        let mut store = BTreeHandleStore::new(HandleSeed::new(7));
125        let first_value = factory().bool(true).unwrap();
126        let second_value = factory().bool(true).unwrap();
127
128        let first = store.intern(first_value);
129        let second = store.intern(second_value);
130
131        assert_ne!(first, second);
132        assert_eq!(store.len(), 2);
133    }
134
135    #[test]
136    fn handle_store_reuses_handle_for_cloned_value() {
137        let mut store = BTreeHandleStore::new(HandleSeed::new(7));
138        let left = factory().string("same".to_owned()).unwrap();
139        let right = left.clone();
140
141        assert_eq!(store.intern(left), store.intern(right));
142    }
143}