Skip to main content

jstd/
intern.rs

1use rustc_hash::FxHashSet as HashSet;
2
3/// Interns strings, returning 'static lifetime strings
4#[derive(Default)]
5pub struct StringPool(HashSet<&'static str>);
6
7impl StringPool {
8    /// Move a string into the pool
9    pub fn intern(&mut self, s: &str) -> &'static str {
10        if let Some(existing) = self.0.get(s) {
11            return existing;
12        }
13
14        let boxed = s.to_owned().into_boxed_str();
15        let leaked: &'static str = Box::leak(boxed);
16
17        self.0.insert(leaked);
18        leaked
19    }
20}
21
22/// Moves a string into the pool, giving it a 'static lifetime
23pub trait Intern {
24    type Static;
25
26    fn intern(self, pool: &mut StringPool) -> Self::Static;
27}
28
29impl Intern for &str {
30    type Static = &'static str;
31
32    fn intern(self, pool: &mut StringPool) -> Self::Static {
33        pool.intern(self)
34    }
35}
36
37impl<T> Intern for Vec<T>
38where
39    T: Intern,
40{
41    type Static = Vec<T::Static>;
42
43    fn intern(self, pool: &mut StringPool) -> Self::Static {
44        self.into_iter().map(|e| Intern::intern(e, pool)).collect()
45    }
46}
47
48impl<T> Intern for Option<T>
49where
50    T: Intern,
51{
52    type Static = Option<T::Static>;
53
54    fn intern(self, pool: &mut StringPool) -> Self::Static {
55        self.map(|e| Intern::intern(e, pool))
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62    use crate::registry::Registry;
63
64    #[test]
65    fn pool_deduplicates_strings_and_interns_nested_values() {
66        let mut pool = StringPool::default();
67        let first = pool.intern("shared");
68        let second = pool.intern("shared");
69        assert!(std::ptr::eq(first, second));
70
71        let values = vec![Some("shared"), None, Some("unique")].intern(&mut pool);
72        assert!(std::ptr::eq(values[0].unwrap(), first));
73        assert_eq!(values[1], None);
74        assert_eq!(values[2], Some("unique"));
75    }
76
77    #[test]
78    fn registry_interning_preserves_ids_and_values() {
79        let mut pool = StringPool::default();
80        let source: Registry<usize, &str> = ["left", "right"].into_iter().collect();
81        let interned = source.intern(&mut pool);
82
83        assert_eq!(interned.len(), 2);
84        assert_eq!(interned[0].to_string(), "left");
85        assert_eq!(interned[1].to_string(), "right");
86    }
87}