Skip to main content

core_storage/
interner.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4// Deliberate near-duplicate of IdMap: IdMap gains node-specific behavior in
5// later plans (free-list on delete) while Interner never deletes. Do not unify.
6#[derive(Debug, Default, Clone, Serialize, Deserialize)]
7pub struct Interner {
8    to_sym: HashMap<String, u32>,
9    to_str: Vec<String>,
10}
11
12impl Interner {
13    pub fn new() -> Self {
14        Self::default()
15    }
16
17    pub fn intern(&mut self, s: &str) -> u32 {
18        if let Some(&sym) = self.to_sym.get(s) {
19            return sym;
20        }
21        let sym = self.to_str.len() as u32;
22        self.to_sym.insert(s.to_string(), sym);
23        self.to_str.push(s.to_string());
24        sym
25    }
26
27    pub fn resolve(&self, sym: u32) -> Option<&str> {
28        self.to_str.get(sym as usize).map(|s| s.as_str())
29    }
30
31    pub fn get(&self, s: &str) -> Option<u32> {
32        self.to_sym.get(s).copied()
33    }
34
35    /// Number of interned symbols (also the id `intern` assigns next).
36    pub fn len(&self) -> usize {
37        self.to_str.len()
38    }
39
40    pub fn is_empty(&self) -> bool {
41        self.to_str.is_empty()
42    }
43
44    /// Roll back to the first `len` symbols. Ids are assigned densely and
45    /// never persisted before their WAL record, so dropping the tail is the
46    /// exact inverse of the speculative `intern` calls made since `len` was
47    /// captured. No-op if `len >= self.len()`.
48    pub fn truncate(&mut self, len: usize) {
49        if len >= self.to_str.len() {
50            return;
51        }
52        for s in self.to_str.drain(len..) {
53            self.to_sym.remove(&s);
54        }
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn interning_is_idempotent_and_resolvable() {
64        let mut i = Interner::new();
65        let a = i.intern("Person");
66        let b = i.intern("Company");
67        assert_eq!(i.intern("Person"), a);
68        assert_ne!(a, b);
69        assert_eq!(i.resolve(b), Some("Company"));
70        assert_eq!(i.get("Person"), Some(a));
71        assert_eq!(i.get("Nope"), None);
72    }
73}