1use std::cell::Cell;
12use std::cell::UnsafeCell;
13use std::collections::HashMap;
14use std::fmt::Formatter;
15use std::ptr::null;
16
17type NumIndex = u32;
19
20#[derive(Debug, Default)]
25pub struct AtomTable(UnsafeCell<Inner>);
26
27#[derive(Default)]
30struct Inner {
31 strings: Vec<String>,
33 map: HashMap<&'static str, NumIndex>,
37
38 strings_u16: Vec<Vec<u16>>,
40 map_u16: HashMap<&'static [u16], NumIndex>,
44
45 strings_bytes: Vec<Vec<u8>>,
49 map_bytes: HashMap<&'static [u8], NumIndex>,
53}
54
55#[derive(Copy, Clone, Eq, PartialEq, Hash)]
57pub struct Atom(NumIndex);
58
59#[derive(Copy, Clone, Eq, PartialEq, Hash)]
61pub struct AtomU16(NumIndex);
62
63#[derive(Copy, Clone, Eq, PartialEq, Hash)]
66pub struct AtomBytes(NumIndex);
67
68thread_local! {
69 static DEBUG_TABLE: Cell<* const AtomTable> = Cell::new(null());
71}
72
73impl std::fmt::Debug for Atom {
76 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
77 let mut t = f.debug_tuple("Atom");
78 t.field(&self.0);
79
80 DEBUG_TABLE.with(|debug_table| {
82 let p = debug_table.get();
83 if let Some(r) = unsafe { p.as_ref() } {
84 if let Some(value) = r.try_str(*self) {
85 t.field(&value);
86 }
87 }
88 });
89 t.finish()
90 }
91}
92
93impl std::fmt::Debug for AtomU16 {
96 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
97 let mut t = f.debug_tuple("Atom");
98 t.field(&self.0);
99
100 DEBUG_TABLE.with(|debug_table| {
102 let p = debug_table.get();
103 if let Some(r) = unsafe { p.as_ref() } {
104 if let Some(value) = r.try_str_u16(*self) {
105 t.field(&value);
106 }
107 }
108 });
109 t.finish()
110 }
111}
112
113impl std::fmt::Debug for AtomBytes {
116 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
117 let mut t = f.debug_tuple("AtomBytes");
118 t.field(&self.0);
119
120 DEBUG_TABLE.with(|debug_table| {
122 let p = debug_table.get();
123 if let Some(r) = unsafe { p.as_ref() } {
124 if let Some(value) = r.try_bytes(*self) {
125 t.field(&value);
126 }
127 }
128 });
129 t.finish()
130 }
131}
132
133pub const INVALID_ATOM: Atom = Atom(NumIndex::MAX);
135
136pub const INVALID_ATOM_BYTES: AtomBytes = AtomBytes(NumIndex::MAX);
138
139impl Inner {
140 fn add_atom<V: Into<String> + AsRef<str>>(&mut self, value: V) -> Atom {
143 if let Some(index) = self.map.get(value.as_ref()) {
144 return Atom(*index);
145 }
146 self.add(value.into())
147 }
148
149 fn add(&mut self, owned: String) -> Atom {
151 let index = self.strings.len();
153 assert!(index < INVALID_ATOM.0 as usize, "More than 4GB atoms?");
154
155 let key: *const str = owned.as_str();
158
159 self.strings.push(owned);
161
162 self.map.insert(unsafe { &*key }, index as NumIndex);
163 Atom(index as NumIndex)
164 }
165
166 #[inline]
168 fn str(&self, ident: Atom) -> &str {
169 self.strings[ident.0 as usize].as_str()
170 }
171
172 fn try_str(&self, ident: Atom) -> Option<&str> {
173 if (ident.0 as usize) < self.strings.len() {
174 Some(self.str(ident))
175 } else {
176 None
177 }
178 }
179
180 fn add_atom_u16<V: Into<Vec<u16>> + AsRef<[u16]>>(&mut self, value: V) -> AtomU16 {
183 if let Some(index) = self.map_u16.get(value.as_ref()) {
184 return AtomU16(*index);
185 }
186 self.add_u16(value.into())
187 }
188
189 fn add_u16(&mut self, owned: Vec<u16>) -> AtomU16 {
191 let index = self.strings_u16.len();
193 assert!(index < INVALID_ATOM.0 as usize, "More than 4GB atoms?");
194
195 let key: *const [u16] = owned.as_slice();
198
199 self.strings_u16.push(owned);
201
202 self.map_u16.insert(unsafe { &*key }, index as NumIndex);
203 AtomU16(index as NumIndex)
204 }
205
206 #[inline]
208 fn str_u16(&self, ident: AtomU16) -> &[u16] {
209 self.strings_u16[ident.0 as usize].as_slice()
210 }
211
212 fn try_str_u16(&self, ident: AtomU16) -> Option<&[u16]> {
213 if (ident.0 as usize) < self.strings_u16.len() {
214 Some(self.str_u16(ident))
215 } else {
216 None
217 }
218 }
219
220 fn add_atom_bytes<V: Into<Vec<u8>> + AsRef<[u8]>>(&mut self, value: V) -> AtomBytes {
224 if let Some(index) = self.map_bytes.get(value.as_ref()) {
225 return AtomBytes(*index);
226 }
227 self.add_bytes(value.into())
228 }
229
230 fn add_bytes(&mut self, owned: Vec<u8>) -> AtomBytes {
232 let index = self.strings_bytes.len();
234 assert!(index < INVALID_ATOM_BYTES.0 as usize, "More than 4GB atoms?");
235
236 let key: *const [u8] = owned.as_slice();
242
243 self.strings_bytes.push(owned);
245
246 self.map_bytes.insert(unsafe { &*key }, index as NumIndex);
247 AtomBytes(index as NumIndex)
248 }
249
250 #[inline]
252 fn bytes(&self, ident: AtomBytes) -> &[u8] {
253 self.strings_bytes[ident.0 as usize].as_slice()
254 }
255
256 fn try_bytes(&self, ident: AtomBytes) -> Option<&[u8]> {
257 if (ident.0 as usize) < self.strings_bytes.len() {
258 Some(self.bytes(ident))
259 } else {
260 None
261 }
262 }
263}
264
265impl AtomTable {
266 pub fn new() -> AtomTable {
268 Default::default()
269 }
270
271 pub fn atom<V: Into<String> + AsRef<str>>(&self, value: V) -> Atom {
274 unsafe { &mut *self.0.get() }.add_atom(value)
275 }
276
277 #[inline]
279 pub fn str(&self, ident: Atom) -> &str {
280 unsafe { &*self.0.get() }.str(ident)
281 }
282
283 #[inline]
284 pub fn try_str(&self, ident: Atom) -> Option<&str> {
285 unsafe { &*self.0.get() }.try_str(ident)
286 }
287
288 pub fn atom_u16<V: Into<Vec<u16>> + AsRef<[u16]>>(&self, value: V) -> AtomU16 {
291 unsafe { &mut *self.0.get() }.add_atom_u16(value)
292 }
293
294 #[inline]
296 pub fn str_u16(&self, ident: AtomU16) -> &[u16] {
297 unsafe { &*self.0.get() }.str_u16(ident)
298 }
299
300 #[inline]
301 pub fn try_str_u16(&self, ident: AtomU16) -> Option<&[u16]> {
302 unsafe { &*self.0.get() }.try_str_u16(ident)
303 }
304
305 pub fn atom_bytes<V: Into<Vec<u8>> + AsRef<[u8]>>(&self, value: V) -> AtomBytes {
310 unsafe { &mut *self.0.get() }.add_atom_bytes(value)
311 }
312
313 #[inline]
315 pub fn bytes(&self, ident: AtomBytes) -> &[u8] {
316 unsafe { &*self.0.get() }.bytes(ident)
317 }
318
319 #[inline]
320 pub fn try_bytes(&self, ident: AtomBytes) -> Option<&[u8]> {
321 unsafe { &*self.0.get() }.try_bytes(ident)
322 }
323
324 pub fn in_debug_context<R, F: FnOnce() -> R>(&self, f: F) -> R {
327 DEBUG_TABLE.with(|debug_table| {
328 let prev_table = debug_table.replace(self);
329 let res = f();
330 debug_assert!(
331 debug_table.get() == self,
332 "debug context unexpectedly changed"
333 );
334 debug_table.set(prev_table);
335 res
336 })
337 }
338
339 pub unsafe fn unsafe_set_debug_context(ptr: *const Self) -> *const Self {
345 DEBUG_TABLE.with(|debug_table| debug_table.replace(ptr))
346 }
347}
348
349impl std::ops::Index<Atom> for AtomTable {
350 type Output = str;
351
352 fn index(&self, index: Atom) -> &Self::Output {
353 self.str(index)
354 }
355}
356
357impl std::ops::Index<AtomU16> for AtomTable {
358 type Output = [u16];
359
360 fn index(&self, index: AtomU16) -> &Self::Output {
361 self.str_u16(index)
362 }
363}
364
365impl std::ops::Index<AtomBytes> for AtomTable {
366 type Output = [u8];
367
368 fn index(&self, index: AtomBytes) -> &Self::Output {
369 self.bytes(index)
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376
377 #[test]
378 fn test_tab() {
379 let idtab = AtomTable::new();
380
381 let id_foo = idtab.atom("foo");
382 let p_foo: *const str = idtab.str(id_foo);
383 let id_bar = idtab.atom("bar");
384 assert_ne!(id_foo, id_bar);
385
386 assert_eq!(idtab.atom("foo"), id_foo);
387 assert_eq!(idtab.atom("bar"), id_bar);
388
389 assert_eq!(idtab.atom(String::from("foo")), id_foo);
390 assert_eq!(idtab.atom(String::from("bar")), id_bar);
391
392 assert_eq!(idtab.str(id_foo), "foo");
393 assert_eq!(idtab.str(id_bar), "bar");
394
395 assert_eq!(idtab.str(id_foo) as *const str, p_foo);
396 }
397
398 #[test]
399 fn test_bytes() {
400 let tab = AtomTable::new();
401 let foo = tab.atom_bytes(b"foo".as_slice());
402 let bar = tab.atom_bytes(b"bar".as_slice());
403 assert_ne!(foo, bar);
404 assert_eq!(tab.atom_bytes(b"foo".as_slice()), foo);
405 assert_eq!(tab.atom_bytes(Vec::from(*b"bar")), bar);
406 assert_eq!(tab.bytes(foo), b"foo");
407 assert_eq!(&tab[bar], b"bar");
408 let p_foo: *const [u8] = tab.bytes(foo);
409 let _ = tab.atom_bytes(b"baz".as_slice());
410 assert_eq!(tab.bytes(foo) as *const [u8], p_foo);
411 }
412
413 #[test]
414 fn test_bytes_ill_formed_utf8() {
415 let tab = AtomTable::new();
416 let lone_surrogate: &[u8] = &[0xed, 0xa0, 0x80];
417 let a = tab.atom_bytes(lone_surrogate);
418 assert_eq!(tab.bytes(a), lone_surrogate);
419 assert_eq!(tab.atom_bytes(lone_surrogate), a);
420 let s = tab.atom("foo");
421 let b = tab.atom_bytes(b"foo".as_slice());
422 assert_eq!(tab.str(s), "foo");
423 assert_eq!(tab.bytes(b), b"foo");
424 }
425
426 #[test]
427 fn test_bytes_try_and_invalid() {
428 let tab = AtomTable::new();
429 let a = tab.atom_bytes(b"x".as_slice());
430 assert_eq!(tab.try_bytes(a), Some(b"x".as_slice()));
431 assert_eq!(tab.try_bytes(INVALID_ATOM_BYTES), None);
432 }
433}