Skip to main content

suture_driver/
interner.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2use std::collections::HashMap;
3
4/// String interner for key paths to avoid repeated allocations.
5///
6/// Useful when the same JSON/YAML key paths are compared many times
7/// during diff or merge operations on large files.
8///
9/// # Example
10///
11/// ```
12/// use suture_driver::KeyInterner;
13///
14/// let mut interner = KeyInterner::new();
15/// let a = interner.intern("config/server/host");
16/// let b = interner.intern("config/server/host");
17/// assert_eq!(a, b);
18/// assert_eq!(interner.resolve(a), "config/server/host");
19/// ```
20pub struct KeyInterner {
21    strings: HashMap<String, u32>,
22    values: Vec<String>,
23}
24
25impl KeyInterner {
26    pub fn new() -> Self {
27        Self {
28            strings: HashMap::new(),
29            values: Vec::new(),
30        }
31    }
32
33    pub fn intern(&mut self, s: &str) -> u32 {
34        if let Some(&id) = self.strings.get(s) {
35            return id;
36        }
37        let id = self.values.len() as u32;
38        self.values.push(s.to_string());
39        self.strings.insert(s.to_string(), id);
40        id
41    }
42
43    pub fn resolve(&self, id: u32) -> &str {
44        &self.values[id as usize]
45    }
46
47    pub fn len(&self) -> usize {
48        self.values.len()
49    }
50
51    pub fn is_empty(&self) -> bool {
52        self.values.is_empty()
53    }
54
55    pub fn contains(&self, s: &str) -> bool {
56        self.strings.contains_key(s)
57    }
58}
59
60impl Default for KeyInterner {
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn test_intern_returns_same_id() {
72        let mut interner = KeyInterner::new();
73        let a = interner.intern("hello");
74        let b = interner.intern("hello");
75        assert_eq!(a, b);
76    }
77
78    #[test]
79    fn test_intern_different_strings() {
80        let mut interner = KeyInterner::new();
81        let a = interner.intern("alpha");
82        let b = interner.intern("beta");
83        assert_ne!(a, b);
84    }
85
86    #[test]
87    fn test_resolve() {
88        let mut interner = KeyInterner::new();
89        let id = interner.intern("path/to/key");
90        assert_eq!(interner.resolve(id), "path/to/key");
91    }
92
93    #[test]
94    fn test_len_and_empty() {
95        let mut interner = KeyInterner::new();
96        assert!(interner.is_empty());
97        interner.intern("a");
98        interner.intern("b");
99        assert_eq!(interner.len(), 2);
100    }
101
102    #[test]
103    fn test_contains() {
104        let mut interner = KeyInterner::new();
105        interner.intern("exists");
106        assert!(interner.contains("exists"));
107        assert!(!interner.contains("missing"));
108    }
109}