Skip to main content

champ_trie/
map.rs

1//! Single-threaded CHAMP map.
2
3use std::fmt;
4use std::hash::Hash;
5use std::ops;
6
7use safe_bump::Idx;
8
9use crate::ChampCheckpoint;
10use crate::adhash;
11use crate::arena::ChampArena;
12use crate::iter::Iter;
13use crate::node::{self, Entry, Node};
14use crate::ops::get::get_recursive;
15use crate::ops::insert::insert_recursive;
16use crate::ops::remove::{RemoveOutcome, remove_recursive};
17use crate::store::ChampStore;
18
19/// Persistent hash map based on a CHAMP trie, single-threaded.
20///
21/// Same set of key-value pairs always produces the same trie structure
22/// (canonical form), enabling O(1) structural equality via [`adhash`](Self::adhash).
23pub struct ChampMap<K, V> {
24    store: ChampArena<K, V>,
25    root: Option<safe_bump::Idx<crate::node::Node<K, V>>>,
26    size: usize,
27    adhash: u64,
28}
29
30// ---------------------------------------------------------------------------
31// Construction & accessors — no trait bounds
32// ---------------------------------------------------------------------------
33
34impl<K, V> ChampMap<K, V> {
35    /// Creates an empty map.
36    #[must_use]
37    pub const fn new() -> Self {
38        Self {
39            store: ChampArena::new(),
40            root: None,
41            size: 0,
42            adhash: 0,
43        }
44    }
45
46    /// Returns the number of key-value pairs.
47    #[must_use]
48    pub const fn len(&self) -> usize {
49        self.size
50    }
51
52    /// Returns `true` if the map contains no entries.
53    #[must_use]
54    pub const fn is_empty(&self) -> bool {
55        self.size == 0
56    }
57
58    /// Returns the current `AdHash` value.
59    ///
60    /// Two maps with the same `AdHash` and the same length contain the same
61    /// entries with overwhelming probability (2⁻⁶⁴ collision chance).
62    #[must_use]
63    pub const fn adhash(&self) -> u64 {
64        self.adhash
65    }
66
67    /// Saves the current map state for later rollback.
68    #[must_use]
69    pub fn checkpoint(&self) -> ChampCheckpoint<K, V> {
70        ChampCheckpoint {
71            store: self.store.checkpoint(),
72            root: self.root,
73            size: self.size,
74            adhash: self.adhash,
75        }
76    }
77
78    /// Returns the total number of allocated items in each arena:
79    /// `(nodes, entries, children)`.
80    ///
81    /// Includes dead COW copies — reflects true memory footprint.
82    #[must_use]
83    pub fn arena_len(&self) -> (usize, usize, usize) {
84        self.store.arena_len()
85    }
86
87    /// Restores the map to a previously saved checkpoint.
88    ///
89    /// All changes made after the checkpoint are discarded.
90    pub fn rollback(&mut self, cp: ChampCheckpoint<K, V>) {
91        self.store.rollback(cp.store);
92        self.root = cp.root;
93        self.size = cp.size;
94        self.adhash = cp.adhash;
95    }
96}
97
98// ---------------------------------------------------------------------------
99// Read operations — K: Hash + Eq
100// ---------------------------------------------------------------------------
101
102impl<K: Hash + Eq, V> ChampMap<K, V> {
103    /// Returns a reference to the value associated with `key`.
104    #[must_use]
105    pub fn get(&self, key: &K) -> Option<&V> {
106        let root = self.root?;
107        get_recursive(&self.store, root, adhash::hash_one(key), key, 0)
108    }
109
110    /// Returns `true` if the map contains the given key.
111    #[must_use]
112    pub fn contains_key(&self, key: &K) -> bool {
113        self.get(key).is_some()
114    }
115}
116
117// ---------------------------------------------------------------------------
118// Write operations — K: Hash + Eq + Clone, V: Hash + Clone
119// ---------------------------------------------------------------------------
120
121impl<K: Hash + Eq + Clone, V: Hash + Clone> ChampMap<K, V> {
122    /// Inserts a key-value pair into the map.
123    ///
124    /// Returns `None` if the key was new, or `Some(old_value)` if an existing
125    /// value was replaced.
126    ///
127    /// # Panics
128    ///
129    /// Panics if internal arena allocation returns an unexpected `None`.
130    pub fn insert(&mut self, key: K, value: V) -> Option<V> {
131        let hash = adhash::hash_one(&key);
132        let entry = Entry { hash, key, value };
133
134        if let Some(root) = self.root {
135            let outcome = insert_recursive(&mut self.store, root, entry, 0);
136            self.root = Some(outcome.node);
137            self.adhash = self.adhash.wrapping_add(outcome.adhash_delta);
138            if outcome.old_value.is_none() {
139                self.size += 1;
140            }
141            outcome.old_value
142        } else {
143            let value_hash = adhash::hash_one(&entry.value);
144            let contribution = adhash::entry_adhash(hash, value_hash);
145            let frag = node::fragment(hash, 0);
146            let bit = node::mask(frag);
147            let data_start = self
148                .store
149                .alloc_entries(std::iter::once(entry))
150                .expect("single entry");
151            let new_node = self.store.alloc_node(Node::Inner {
152                data_map: bit,
153                node_map: 0,
154                data_start,
155                children_start: Idx::from_raw(0),
156                adhash: contribution,
157            });
158            self.root = Some(new_node);
159            self.size = 1;
160            self.adhash = contribution;
161            None
162        }
163    }
164
165    /// Removes a key from the map. Returns the removed value, or `None` if
166    /// the key was not present.
167    pub fn remove(&mut self, key: &K) -> Option<V> {
168        let root = self.root?;
169        let hash = adhash::hash_one(key);
170        match remove_recursive(&mut self.store, root, hash, key, 0) {
171            RemoveOutcome::NotFound => None,
172            RemoveOutcome::Removed {
173                node,
174                adhash_delta,
175                removed_value,
176            } => {
177                self.root = node;
178                self.size -= 1;
179                self.adhash = self.adhash.wrapping_sub(adhash_delta);
180                Some(removed_value)
181            }
182        }
183    }
184}
185
186// ---------------------------------------------------------------------------
187// Iterator stubs
188// ---------------------------------------------------------------------------
189
190impl<K, V> ChampMap<K, V> {
191    /// Returns an iterator over `(&K, &V)` pairs.
192    #[must_use]
193    pub fn iter(&self) -> Iter<'_, K, V> {
194        Iter::new(&self.store, self.root)
195    }
196}
197
198// ---------------------------------------------------------------------------
199// Trait impls
200// ---------------------------------------------------------------------------
201
202impl<K, V> Default for ChampMap<K, V> {
203    fn default() -> Self {
204        Self::new()
205    }
206}
207
208impl<K, V> fmt::Debug for ChampMap<K, V> {
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210        f.debug_struct("ChampMap")
211            .field("len", &self.size)
212            .field("adhash", &format_args!("{:#018x}", self.adhash))
213            .finish_non_exhaustive()
214    }
215}
216
217impl<K: Hash + Eq + Clone, V: Hash + Clone> Extend<(K, V)> for ChampMap<K, V> {
218    fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
219        for (k, v) in iter {
220            self.insert(k, v);
221        }
222    }
223}
224
225impl<K: Hash + Eq + Clone, V: Hash + Clone> FromIterator<(K, V)> for ChampMap<K, V> {
226    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
227        let mut map = Self::new();
228        map.extend(iter);
229        map
230    }
231}
232
233impl<K: Hash + Eq, V> ops::Index<&K> for ChampMap<K, V> {
234    type Output = V;
235
236    fn index(&self, key: &K) -> &V {
237        self.get(key).expect("key not found")
238    }
239}
240
241impl<'a, K, V> IntoIterator for &'a ChampMap<K, V> {
242    type Item = (&'a K, &'a V);
243    type IntoIter = Iter<'a, K, V>;
244
245    fn into_iter(self) -> Iter<'a, K, V> {
246        self.iter()
247    }
248}