1use {
2 bumpalo::Bump,
3 hashbrown::HashTable,
4 rustc_hash::FxBuildHasher,
5 std::{
6 borrow::Borrow,
7 cell::{OnceCell, UnsafeCell},
8 fmt,
9 hash::{BuildHasher, Hash},
10 marker::PhantomData,
11 ptr::NonNull,
12 },
13};
14
15pub struct Interner<T> {
16 __marker: PhantomData<T>,
18 set: UnsafeCell<HashTable<NonNull<u8>>>,
21 arena: OnceCell<Bump>,
22}
23
24impl<T> Default for Interner<T> {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl<T> Interner<T> {
31 #[must_use]
34 pub const fn new() -> Self {
35 Self {
36 __marker: PhantomData,
37 set: UnsafeCell::new(HashTable::new()),
38 arena: OnceCell::new(),
39 }
40 }
41
42 pub fn len(&self) -> usize {
44 self.set().len()
45 }
46
47 pub fn is_empty(&self) -> bool {
49 self.len() == 0
50 }
51
52 fn set(&self) -> &HashTable<NonNull<u8>> {
53 unsafe { self.set.get().as_ref().unwrap() }
55 }
56
57 #[expect(clippy::mut_from_ref)]
58 fn set_mut(&self) -> &mut HashTable<NonNull<u8>> {
59 unsafe { self.set.get().as_mut().unwrap() }
61 }
62}
63
64impl<T: Hash + Eq> Interner<T> {
65 pub(crate) fn try_resolve_with<Q>(&self, value: &Q, hash: u64) -> Option<&T>
66 where
67 T: Borrow<Q>,
68 Q: ?Sized + Eq,
69 {
70 self.set()
71 .find(hash, |cached| T::borrow(unsafe { cached.cast().as_ref() }) == value)
72 .map(|ptr| unsafe { ptr.cast().as_ref() })
73 }
74
75 pub(crate) fn insert(&self, hash: u64, value: T) -> &T {
76 let arena = self.arena.get_or_init(Bump::new);
77
78 let cached = NonNull::from(arena.alloc(value)).cast();
79 self.set_mut().insert_unique(hash, cached, |t| FxBuildHasher.hash_one(t));
80 unsafe { cached.cast().as_ref() }
81 }
82
83 #[must_use]
85 pub fn try_resolve<Q>(&self, value: &Q) -> Option<&T>
86 where
87 T: Borrow<Q>,
88 Q: ?Sized + Hash + Eq,
89 {
90 let hash = FxBuildHasher.hash_one(value);
91 self.try_resolve_with(value, hash)
92 }
93
94 pub fn intern(&self, value: T) -> &T {
96 let hash = FxBuildHasher.hash_one(&value);
97
98 if let Some(cached) = self.try_resolve_with(&value, hash) {
99 return cached;
100 }
101
102 self.insert(hash, value)
103 }
104
105 pub fn intern_new(&self, value: T) -> &T {
107 let hash = FxBuildHasher.hash_one(&value);
108 self.insert(hash, value)
109 }
110}
111
112impl<T: fmt::Debug> fmt::Debug for Interner<T> {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 self.set().fmt(f)
115 }
116}
117
118unsafe impl<T> Send for Interner<T> where T: Send {}