1use std::sync::{Arc, RwLock};
2
3use dashmap::DashMap;
4
5#[derive(Debug, Default)]
18pub struct Interner {
19 to_id: DashMap<Arc<str>, u32>,
21 to_str: RwLock<Vec<Arc<str>>>,
23}
24
25impl Interner {
26 pub fn intern(&self, s: Arc<str>) -> u32 {
29 if let Some(id) = self.to_id.get(s.as_ref()) {
31 return *id;
32 }
33 let mut vec = self.to_str.write().expect("interner lock poisoned");
35 if let Some(id) = self.to_id.get(s.as_ref()) {
38 return *id;
39 }
40 let id = vec.len() as u32;
41 vec.push(s.clone());
42 self.to_id.insert(s, id);
46 id
47 }
48
49 pub fn intern_str(&self, s: &str) -> u32 {
52 if let Some(id) = self.to_id.get(s) {
54 return *id;
55 }
56 self.intern(Arc::from(s))
57 }
58
59 pub fn get(&self, id: u32) -> Arc<str> {
61 self.to_str.read().expect("interner lock poisoned")[id as usize].clone()
62 }
63
64 pub fn get_id(&self, s: &str) -> Option<u32> {
66 self.to_id.get(s).map(|id| *id)
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn same_string_gives_same_id() {
76 let interner = Interner::default();
77 let a = interner.intern_str("Foo::bar");
78 let b = interner.intern_str("Foo::bar");
79 assert_eq!(a, b);
80 }
81
82 #[test]
83 fn different_strings_give_different_ids() {
84 let interner = Interner::default();
85 let a = interner.intern_str("Foo::bar");
86 let b = interner.intern_str("Foo::baz");
87 assert_ne!(a, b);
88 }
89
90 #[test]
91 fn get_roundtrips_id_to_string() {
92 let interner = Interner::default();
93 let id = interner.intern_str("App\\Service");
94 assert_eq!(interner.get(id).as_ref(), "App\\Service");
95 }
96
97 #[test]
98 fn get_id_returns_none_for_unknown_string() {
99 let interner = Interner::default();
100 assert!(interner.get_id("unknown").is_none());
101 }
102
103 #[test]
104 fn intern_and_intern_str_agree() {
105 let interner = Interner::default();
106 let id_arc = interner.intern(Arc::from("hello"));
107 let id_str = interner.intern_str("hello");
108 assert_eq!(id_arc, id_str);
109 }
110
111 #[test]
112 fn concurrent_intern_is_consistent() {
113 use std::sync::Arc as StdArc;
114 let interner = StdArc::new(Interner::default());
115 let handles: Vec<_> = (0..8)
116 .map(|_| {
117 let i = interner.clone();
118 std::thread::spawn(move || i.intern_str("shared"))
119 })
120 .collect();
121 let ids: Vec<u32> = handles.into_iter().map(|h| h.join().unwrap()).collect();
122 assert!(
123 ids.iter().all(|&id| id == ids[0]),
124 "all threads must see the same ID"
125 );
126 }
127}