1use std::{
6 borrow::Borrow,
7 fmt::{self, Debug, Display},
8 hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
9 ops::Deref,
10 sync::OnceLock,
11};
12
13use dashmap::{DashMap, SharedValue};
14use hashbrown::raw::RawTable;
15use rustc_hash::FxHasher;
16use triomphe::Arc;
17
18type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>;
19type Guard<T> = dashmap::RwLockWriteGuard<'static, RawTable<(Arc<T>, SharedValue<()>)>>;
20
21mod symbol;
22pub use self::symbol::{Symbol, symbols as sym};
23
24pub struct Interned<T: Internable + ?Sized> {
25 arc: Arc<T>,
26}
27
28impl<T: Internable> Interned<T> {
29 #[inline]
30 pub fn new(obj: T) -> Self {
31 Self::new_generic(obj)
32 }
33}
34
35impl Interned<str> {
36 #[inline]
37 pub fn new_str(s: &str) -> Self {
38 Self::new_generic(s)
39 }
40}
41
42impl<T: Internable + ?Sized> Interned<T> {
43 #[inline]
44 pub fn new_generic<U>(obj: U) -> Self
45 where
46 U: Borrow<T>,
47 Arc<T>: From<U>,
48 {
49 let storage = T::storage().get();
50 let (mut shard, hash) = Self::select(storage, obj.borrow());
51 let bucket = match shard.find_or_find_insert_slot(
58 hash,
59 |(other, _)| **other == *obj.borrow(),
60 |(x, _)| Self::hash(storage, x),
61 ) {
62 Ok(bucket) => bucket,
63 Err(insert_slot) => unsafe {
65 shard.insert_in_slot(hash, insert_slot, (Arc::from(obj), SharedValue::new(())))
66 },
67 };
68 unsafe { Self { arc: bucket.as_ref().0.clone() } }
70 }
71
72 #[inline]
73 fn select(storage: &'static InternMap<T>, obj: &T) -> (Guard<T>, u64) {
74 let hash = Self::hash(storage, obj);
75 let shard_idx = storage.determine_shard(hash as usize);
76 let shard = &storage.shards()[shard_idx];
77 (shard.write(), hash)
78 }
79
80 #[inline]
81 fn hash(storage: &'static InternMap<T>, obj: &T) -> u64 {
82 storage.hasher().hash_one(obj)
83 }
84}
85
86impl<T: Internable + ?Sized> Drop for Interned<T> {
87 #[inline]
88 fn drop(&mut self) {
89 if Arc::count(&self.arc) == 2 {
91 self.drop_slow();
94 }
95 }
96}
97
98impl<T: Internable + ?Sized> Interned<T> {
99 #[cold]
100 fn drop_slow(&mut self) {
101 let storage = T::storage().get();
102 let (mut shard, hash) = Self::select(storage, &self.arc);
103
104 if Arc::count(&self.arc) != 2 {
105 return;
107 }
108
109 shard.remove_entry(hash, |(other, _)| **other == *self.arc);
110
111 if shard.len() * 2 < shard.capacity() {
113 let len = shard.len();
114 shard.shrink_to(len, |(x, _)| Self::hash(storage, x));
115 }
116 }
117}
118
119impl<T: Internable> PartialEq for Interned<T> {
121 #[inline]
124 fn eq(&self, other: &Self) -> bool {
125 Arc::ptr_eq(&self.arc, &other.arc)
126 }
127}
128
129impl<T: Internable> Eq for Interned<T> {}
130
131impl PartialEq for Interned<str> {
132 fn eq(&self, other: &Self) -> bool {
133 Arc::ptr_eq(&self.arc, &other.arc)
134 }
135}
136
137impl Eq for Interned<str> {}
138
139impl<T: Internable + ?Sized> Hash for Interned<T> {
140 fn hash<H: Hasher>(&self, state: &mut H) {
141 state.write_usize(Arc::as_ptr(&self.arc) as *const () as usize)
143 }
144}
145
146impl<T: Internable + ?Sized> AsRef<T> for Interned<T> {
147 #[inline]
148 fn as_ref(&self) -> &T {
149 &self.arc
150 }
151}
152
153impl<T: Internable + ?Sized> Deref for Interned<T> {
154 type Target = T;
155
156 #[inline]
157 fn deref(&self) -> &Self::Target {
158 &self.arc
159 }
160}
161
162impl<T: Internable + ?Sized> Clone for Interned<T> {
163 fn clone(&self) -> Self {
164 Self { arc: self.arc.clone() }
165 }
166}
167
168impl<T: Debug + Internable + ?Sized> Debug for Interned<T> {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 (*self.arc).fmt(f)
171 }
172}
173
174impl<T: Display + Internable + ?Sized> Display for Interned<T> {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 (*self.arc).fmt(f)
177 }
178}
179
180pub struct InternStorage<T: ?Sized> {
181 map: OnceLock<InternMap<T>>,
182}
183
184#[allow(
185 clippy::new_without_default,
186 reason = "this a const fn, so it can't be default yet. See <https://github.com/rust-lang/rust/issues/63065>"
187)]
188impl<T: ?Sized> InternStorage<T> {
189 pub const fn new() -> Self {
190 Self { map: OnceLock::new() }
191 }
192}
193
194impl<T: Internable + ?Sized> InternStorage<T> {
195 fn get(&self) -> &InternMap<T> {
196 self.map.get_or_init(DashMap::default)
197 }
198}
199
200pub trait Internable: Hash + Eq + 'static {
201 fn storage() -> &'static InternStorage<Self>;
202}
203
204#[macro_export]
206#[doc(hidden)]
207macro_rules! _impl_internable {
208 ( $($t:path),+ $(,)? ) => { $(
209 impl $crate::Internable for $t {
210 fn storage() -> &'static $crate::InternStorage<Self> {
211 static STORAGE: $crate::InternStorage<$t> = $crate::InternStorage::new();
212 &STORAGE
213 }
214 }
215 )+ };
216}
217
218pub use crate::_impl_internable as impl_internable;
219
220impl_internable!(str,);