1use rustc_hash::FxHashSet as HashSet;
2
3#[derive(Default)]
5pub struct StringPool(HashSet<&'static str>);
6
7impl StringPool {
8 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
22pub 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}