lechange_core/
interner.rs1use crate::types::InternedString;
7use parking_lot::RwLock;
8use std::collections::HashMap;
9use std::hash::{Hash, Hasher};
10
11#[inline]
13fn hash_str(s: &str) -> u64 {
14 let mut hasher = std::collections::hash_map::DefaultHasher::new();
15 s.hash(&mut hasher);
16 hasher.finish()
17}
18
19pub struct StringInterner {
26 strings: RwLock<Vec<Box<str>>>,
28 map: RwLock<HashMap<u64, Vec<InternedString>>>,
30}
31
32impl StringInterner {
33 pub fn new() -> Self {
35 Self {
36 strings: RwLock::new(Vec::new()),
37 map: RwLock::new(HashMap::new()),
38 }
39 }
40
41 pub fn with_capacity(capacity: usize) -> Self {
43 Self {
44 strings: RwLock::new(Vec::with_capacity(capacity)),
45 map: RwLock::new(HashMap::with_capacity(capacity)),
46 }
47 }
48
49 pub fn intern(&self, s: &str) -> InternedString {
54 let h = hash_str(s);
55
56 {
58 let map = self.map.read();
59 if let Some(chain) = map.get(&h) {
60 let strings = self.strings.read();
61 for &id in chain {
62 if &*strings[id.0 as usize] == s {
63 return id;
64 }
65 }
66 }
67 }
68
69 let mut map = self.map.write();
71 let mut strings = self.strings.write();
72
73 if let Some(chain) = map.get(&h) {
75 for &id in chain {
76 if &*strings[id.0 as usize] == s {
77 return id;
78 }
79 }
80 }
81
82 let id = InternedString(strings.len() as u32);
83 strings.push(Box::from(s));
84 map.entry(h).or_default().push(id);
85
86 id
87 }
88
89 #[inline]
100 pub fn resolve(&self, id: InternedString) -> Option<&str> {
101 unsafe {
102 let strings = self.strings.read();
103 let s = strings.get(id.0 as usize)?;
104 Some(std::mem::transmute::<&str, &str>(&**s))
106 }
107 }
108
109 #[inline]
111 pub fn len(&self) -> usize {
112 self.strings.read().len()
113 }
114
115 #[inline]
117 pub fn is_empty(&self) -> bool {
118 self.strings.read().is_empty()
119 }
120}
121
122impl Default for StringInterner {
123 fn default() -> Self {
124 Self::new()
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn test_intern_resolve() {
134 let interner = StringInterner::new();
135 let id1 = interner.intern("foo");
136 let id2 = interner.intern("bar");
137 let id3 = interner.intern("foo"); assert_eq!(id1, id3);
140 assert_ne!(id1, id2);
141 assert_eq!(interner.resolve(id1), Some("foo"));
142 assert_eq!(interner.resolve(id2), Some("bar"));
143 }
144
145 #[test]
146 fn test_thread_safety() {
147 use std::sync::Arc;
148 use std::thread;
149
150 let interner = Arc::new(StringInterner::new());
151 let mut handles = vec![];
152
153 for _ in 0..10 {
155 let interner = Arc::clone(&interner);
156 let handle = thread::spawn(move || {
157 for i in 0..100 {
158 interner.intern(&format!("string_{}", i));
159 }
160 });
161 handles.push(handle);
162 }
163
164 for handle in handles {
165 handle.join().unwrap();
166 }
167
168 assert_eq!(interner.len(), 100);
170 }
171
172 #[test]
173 fn test_with_capacity() {
174 let interner = StringInterner::with_capacity(1000);
175 assert_eq!(interner.len(), 0);
176
177 for i in 0..500 {
179 interner.intern(&format!("str_{}", i));
180 }
181
182 assert_eq!(interner.len(), 500);
183 }
184
185 #[test]
188 fn test_box_arena_intern_resolve() {
189 let interner = StringInterner::new();
190 let id = interner.intern("hello");
191 assert_eq!(interner.resolve(id), Some("hello"));
192 }
193
194 #[test]
195 fn test_box_arena_dedup() {
196 let interner = StringInterner::new();
197 let id1 = interner.intern("a");
198 let id2 = interner.intern("a");
199 assert_eq!(id1, id2);
200 }
201
202 #[test]
203 fn test_box_arena_distinct() {
204 let interner = StringInterner::new();
205 let id1 = interner.intern("a");
206 let id2 = interner.intern("b");
207 assert_ne!(id1, id2);
208 }
209
210 #[test]
211 fn test_no_arc_in_struct() {
212 let interner = StringInterner::new();
213 let type_name = std::any::type_name_of_val(&interner);
214 assert!(
215 !type_name.contains("Arc"),
216 "StringInterner should not contain Arc: {}",
217 type_name
218 );
219 }
220
221 #[test]
222 fn test_thread_safety_concurrent_intern() {
223 use std::sync::Arc;
224 use std::thread;
225
226 let interner = Arc::new(StringInterner::new());
227 let mut handles = vec![];
228
229 for t in 0..8 {
230 let interner = Arc::clone(&interner);
231 let handle = thread::spawn(move || {
232 for i in 0..1000 {
233 let s = format!("t{}_{}", t, i);
234 let id = interner.intern(&s);
235 assert_eq!(interner.resolve(id), Some(s.as_str()));
236 }
237 });
238 handles.push(handle);
239 }
240
241 for handle in handles {
242 handle.join().unwrap();
243 }
244
245 assert_eq!(interner.len(), 8000);
247 }
248
249 #[test]
250 fn test_capacity_growth() {
251 let interner = StringInterner::new();
252 for i in 0..10000 {
253 let s = format!("string_{}", i);
254 let id = interner.intern(&s);
255 assert_eq!(interner.resolve(id), Some(s.as_str()));
256 }
257 assert_eq!(interner.len(), 10000);
258 }
259}