simple_interner/
interner.rs1use {
2 crate::Interned,
3 std::{
4 borrow::Borrow,
5 collections::hash_map::RandomState,
6 fmt,
7 hash::{BuildHasher, Hash, Hasher},
8 marker::PhantomData,
9 ops::Deref,
10 ptr::NonNull,
11 },
12};
13
14#[cfg(feature = "raw")]
15use hashbrown::hash_map::RawEntryMut;
16#[cfg(feature = "hashbrown")]
17use hashbrown::hash_map::{Entry, HashMap};
18#[cfg(not(feature = "hashbrown"))]
19use std::collections::hash_map::{Entry, HashMap};
20
21#[cfg(not(feature = "parking_lot"))]
22use std::sync::RwLock;
23#[cfg(feature = "parking_lot")]
24use {crate::parking_lot_shim::*, parking_lot::RwLock};
25
26struct PinBox<T: ?Sized> {
31 ptr: NonNull<T>,
32 _marker: PhantomData<Box<T>>,
33}
34
35impl<T: ?Sized> PinBox<T> {
36 fn new(x: Box<T>) -> Self {
37 Self {
38 ptr: NonNull::new(Box::into_raw(x)).unwrap(),
39 _marker: PhantomData,
40 }
41 }
42
43 #[allow(unsafe_code)]
44 unsafe fn as_ref<'a>(&self) -> &'a T {
45 self.ptr.as_ref()
46 }
47}
48
49impl<T: ?Sized> Drop for PinBox<T> {
50 fn drop(&mut self) {
51 #[allow(unsafe_code)] unsafe {
53 Box::from_raw(self.ptr.as_ptr())
54 };
55 }
56}
57
58impl<T: ?Sized> Deref for PinBox<T> {
59 type Target = T;
60 #[allow(unsafe_code)] fn deref(&self) -> &T {
62 unsafe { self.as_ref() }
63 }
64}
65
66impl<T: ?Sized + fmt::Debug> fmt::Debug for PinBox<T> {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 (**self).fmt(f)
69 }
70}
71
72impl<T: ?Sized + Eq> Eq for PinBox<T> {}
73impl<T: ?Sized + PartialEq> PartialEq for PinBox<T> {
74 fn eq(&self, other: &Self) -> bool {
75 (**self).eq(&**other)
76 }
77}
78impl<T: ?Sized + PartialEq> PartialEq<T> for PinBox<T> {
79 fn eq(&self, other: &T) -> bool {
80 (**self).eq(other)
81 }
82}
83
84impl<T: ?Sized + Ord> Ord for PinBox<T> {
85 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
86 (**self).cmp(&**other)
87 }
88}
89impl<T: ?Sized + PartialOrd> PartialOrd for PinBox<T> {
90 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
91 (**self).partial_cmp(&**other)
92 }
93}
94impl<T: ?Sized + PartialOrd> PartialOrd<T> for PinBox<T> {
95 fn partial_cmp(&self, other: &T) -> Option<std::cmp::Ordering> {
96 (**self).partial_cmp(other)
97 }
98}
99
100impl<T: ?Sized + Hash> Hash for PinBox<T> {
101 fn hash<H: Hasher>(&self, state: &mut H) {
102 (**self).hash(state)
103 }
104}
105
106impl<T: ?Sized> Borrow<T> for PinBox<T> {
107 fn borrow(&self) -> &T {
108 self
109 }
110}
111
112#[allow(unsafe_code)] unsafe impl<T: ?Sized> Send for PinBox<T> where Box<T>: Send {}
114
115#[allow(unsafe_code)] unsafe impl<T: ?Sized> Sync for PinBox<T> where Box<T>: Sync {}
117
118#[derive(Debug)]
120pub struct Interner<T: ?Sized, S = RandomState> {
121 arena: RwLock<HashMap<PinBox<T>, (), S>>,
122}
123
124impl<T: ?Sized, S: Default> Default for Interner<T, S> {
125 fn default() -> Self {
126 Interner {
127 arena: RwLock::default(),
128 }
129 }
130}
131
132impl<T: Eq + Hash + ?Sized, S: BuildHasher> Interner<T, S> {
133 pub fn intern<R>(&self, t: R) -> Interned<'_, T>
150 where
151 R: Borrow<T> + Into<Box<T>>,
152 {
153 let borrowed = t.borrow();
154 if let Some(interned) = self.get(borrowed) {
155 return interned;
156 }
157
158 let mut arena = self
159 .arena
160 .write()
161 .expect("interner lock should not be poisoned");
162
163 let entry = arena.entry(PinBox::new(t.into()));
171 #[allow(unsafe_code)] match entry {
173 Entry::Occupied(entry) => Interned(unsafe { entry.key().as_ref() }),
174 Entry::Vacant(entry) => {
175 let interned = Interned(unsafe { entry.key().as_ref() });
176 entry.insert(());
177 interned
178 },
179 }
180 }
181
182 pub fn get(&self, t: &T) -> Option<Interned<'_, T>> {
188 #[allow(unsafe_code)] self.arena
190 .read()
191 .expect("interner lock should not be poisoned")
192 .get_key_value(t)
193 .map(|(t, _)| Interned(unsafe { t.as_ref() }))
194 }
195}
196
197#[allow(unsafe_code)]
198#[cfg(feature = "raw")]
199impl<T: ?Sized, S> Interner<T, S> {
200 pub fn intern_raw<Q>(
202 &self,
203 it: Q,
204 hash: u64,
205 mut is_match: impl FnMut(&Q, &T) -> bool,
206 do_hash: impl Fn(&T) -> u64,
207 commit: impl FnOnce(Q) -> Box<T>,
208 ) -> Interned<'_, T> {
209 if let Some(interned) = self.get_raw(hash, |t| is_match(&it, t)) {
210 return interned;
211 }
212
213 let mut arena = self
214 .arena
215 .write()
216 .expect("interner lock should not be poisoned");
217
218 match arena.raw_entry_mut().from_hash(hash, |t| is_match(&it, t)) {
219 RawEntryMut::Occupied(entry) => Interned(unsafe { entry.key().as_ref() }),
220 RawEntryMut::Vacant(entry) => {
221 let boxed = PinBox::new(commit(it));
222 let interned = Interned(unsafe { boxed.as_ref() });
223 entry.insert_with_hasher(hash, boxed, (), |t| do_hash(t));
224 interned
225 },
226 }
227 }
228
229 pub fn get_raw(
231 &self,
232 hash: u64,
233 mut is_match: impl FnMut(&T) -> bool,
234 ) -> Option<Interned<'_, T>> {
235 self.arena
236 .read()
237 .expect("interner lock should not be poisoned")
238 .raw_entry()
239 .from_hash(hash, |t| is_match(t))
240 .map(|(t, _)| Interned(unsafe { t.as_ref() }))
241 }
242}
243
244impl<T: ?Sized> Interner<T> {
245 pub fn new() -> Self {
250 Interner {
251 arena: RwLock::new(HashMap::default()),
252 }
253 }
254
255 pub fn with_capacity(capacity: usize) -> Self {
260 Interner {
261 arena: RwLock::new(HashMap::with_capacity_and_hasher(
262 capacity,
263 RandomState::default(),
264 )),
265 }
266 }
267}
268
269impl<T: ?Sized, H: BuildHasher> Interner<T, H> {
271 #[cfg(not(feature = "hashbrown"))]
272 pub fn with_hasher(hasher: H) -> Self {
276 Interner {
277 arena: RwLock::new(HashMap::with_hasher(hasher)),
278 }
279 }
280
281 #[cfg(feature = "hashbrown")]
282 pub const fn with_hasher(hasher: H) -> Self {
286 Interner {
287 arena: RwLock::new(HashMap::with_hasher(hasher)),
288 }
289 }
290
291 pub fn with_capacity_and_hasher(capacity: usize, hasher: H) -> Self {
296 Interner {
297 arena: RwLock::new(HashMap::with_capacity_and_hasher(capacity, hasher)),
298 }
299 }
300}