1use parking_lot::RwLock;
4use rustc_hash::FxHashMap;
5use smol_str::SmolStr;
6use std::fmt;
7
8#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
18pub struct Name(u32);
19
20impl Name {
21 #[inline]
23 pub(crate) const fn from_raw(index: u32) -> Self {
24 Self(index)
25 }
26
27 #[inline]
29 pub const fn index(self) -> u32 {
30 self.0
31 }
32}
33
34impl fmt::Debug for Name {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 write!(f, "Name({})", self.0)
37 }
38}
39
40#[derive(Default)]
44pub struct Interner {
45 inner: RwLock<InternerInner>,
46}
47
48#[derive(Default)]
49struct InternerInner {
50 map: FxHashMap<SmolStr, u32>,
52 strings: Vec<SmolStr>,
54}
55
56impl Interner {
57 pub fn new() -> Self {
59 Self::default()
60 }
61
62 pub fn intern(&self, s: &str) -> Name {
66 {
68 let inner = self.inner.read();
69 if let Some(&index) = inner.map.get(s) {
70 return Name::from_raw(index);
71 }
72 }
73
74 let mut inner = self.inner.write();
76
77 if let Some(&index) = inner.map.get(s) {
79 return Name::from_raw(index);
80 }
81
82 let smol = SmolStr::new(s);
83 let index = inner.strings.len() as u32;
84 inner.strings.push(smol.clone());
85 inner.map.insert(smol, index);
86
87 Name::from_raw(index)
88 }
89
90 pub fn lookup(&self, name: Name) -> Option<SmolStr> {
94 let inner = self.inner.read();
95 inner.strings.get(name.0 as usize).cloned()
96 }
97
98 pub fn get(&self, name: Name) -> SmolStr {
103 self.lookup(name).expect("Name not found in interner")
104 }
105
106 pub fn len(&self) -> usize {
108 self.inner.read().strings.len()
109 }
110
111 pub fn is_empty(&self) -> bool {
113 self.len() == 0
114 }
115}
116
117impl fmt::Debug for Interner {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 let inner = self.inner.read();
120 f.debug_struct("Interner")
121 .field("count", &inner.strings.len())
122 .finish()
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_intern_same_string() {
132 let interner = Interner::new();
133
134 let a = interner.intern("hello");
135 let b = interner.intern("hello");
136
137 assert_eq!(a, b);
138 assert_eq!(interner.len(), 1);
139 }
140
141 #[test]
142 fn test_intern_different_strings() {
143 let interner = Interner::new();
144
145 let a = interner.intern("hello");
146 let b = interner.intern("world");
147
148 assert_ne!(a, b);
149 assert_eq!(interner.len(), 2);
150 }
151
152 #[test]
153 fn test_lookup() {
154 let interner = Interner::new();
155
156 let name = interner.intern("test");
157 let s = interner.get(name);
158
159 assert_eq!(s.as_str(), "test");
160 }
161
162 #[test]
163 fn test_name_size() {
164 assert_eq!(std::mem::size_of::<Name>(), 4);
165 }
166}