Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/intern/src/lib.rs')
| -rw-r--r-- | crates/intern/src/lib.rs | 223 |
1 files changed, 9 insertions, 214 deletions
diff --git a/crates/intern/src/lib.rs b/crates/intern/src/lib.rs index 398d224c07..0c0b12427d 100644 --- a/crates/intern/src/lib.rs +++ b/crates/intern/src/lib.rs @@ -2,219 +2,14 @@ //! //! Eventually this should probably be replaced with salsa-based interning. -use std::{ - borrow::Borrow, - fmt::{self, Debug, Display}, - hash::{BuildHasher, BuildHasherDefault, Hash, Hasher}, - ops::Deref, - sync::OnceLock, -}; - -use dashmap::{DashMap, SharedValue}; -use hashbrown::raw::RawTable; -use rustc_hash::FxHasher; -use triomphe::Arc; - -type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>; -type Guard<T> = dashmap::RwLockWriteGuard<'static, RawTable<(Arc<T>, SharedValue<()>)>>; - +mod gc; +mod intern; +mod intern_slice; mod symbol; -pub use self::symbol::{Symbol, symbols as sym}; - -pub struct Interned<T: Internable + ?Sized> { - arc: Arc<T>, -} - -impl<T: Internable> Interned<T> { - #[inline] - pub fn new(obj: T) -> Self { - Self::new_generic(obj) - } -} - -impl Interned<str> { - #[inline] - pub fn new_str(s: &str) -> Self { - Self::new_generic(s) - } -} - -impl<T: Internable + ?Sized> Interned<T> { - #[inline] - pub fn new_generic<U>(obj: U) -> Self - where - U: Borrow<T>, - Arc<T>: From<U>, - { - let storage = T::storage().get(); - let (mut shard, hash) = Self::select(storage, obj.borrow()); - // Atomically, - // - check if `obj` is already in the map - // - if so, clone its `Arc` and return it - // - if not, box it up, insert it, and return a clone - // This needs to be atomic (locking the shard) to avoid races with other thread, which could - // insert the same object between us looking it up and inserting it. - let bucket = match shard.find_or_find_insert_slot( - hash, - |(other, _)| **other == *obj.borrow(), - |(x, _)| Self::hash(storage, x), - ) { - Ok(bucket) => bucket, - // SAFETY: The slot came from `find_or_find_insert_slot()`, and the table wasn't modified since then. - Err(insert_slot) => unsafe { - shard.insert_in_slot(hash, insert_slot, (Arc::from(obj), SharedValue::new(()))) - }, - }; - // SAFETY: We just retrieved/inserted this bucket. - unsafe { Self { arc: bucket.as_ref().0.clone() } } - } - - #[inline] - fn select(storage: &'static InternMap<T>, obj: &T) -> (Guard<T>, u64) { - let hash = Self::hash(storage, obj); - let shard_idx = storage.determine_shard(hash as usize); - let shard = &storage.shards()[shard_idx]; - (shard.write(), hash) - } - - #[inline] - fn hash(storage: &'static InternMap<T>, obj: &T) -> u64 { - storage.hasher().hash_one(obj) - } -} - -impl<T: Internable + ?Sized> Drop for Interned<T> { - #[inline] - fn drop(&mut self) { - // When the last `Ref` is dropped, remove the object from the global map. - if Arc::count(&self.arc) == 2 { - // Only `self` and the global map point to the object. - - self.drop_slow(); - } - } -} - -impl<T: Internable + ?Sized> Interned<T> { - #[cold] - fn drop_slow(&mut self) { - let storage = T::storage().get(); - let (mut shard, hash) = Self::select(storage, &self.arc); - - if Arc::count(&self.arc) != 2 { - // Another thread has interned another copy - return; - } - - shard.remove_entry(hash, |(other, _)| **other == *self.arc); - - // Shrink the backing storage if the shard is less than 50% occupied. - if shard.len() * 2 < shard.capacity() { - let len = shard.len(); - shard.shrink_to(len, |(x, _)| Self::hash(storage, x)); - } - } -} - -/// Compares interned `Ref`s using pointer equality. -impl<T: Internable> PartialEq for Interned<T> { - // NOTE: No `?Sized` because `ptr_eq` doesn't work right with trait objects. - - #[inline] - fn eq(&self, other: &Self) -> bool { - Arc::ptr_eq(&self.arc, &other.arc) - } -} -impl<T: Internable> Eq for Interned<T> {} - -impl PartialEq for Interned<str> { - fn eq(&self, other: &Self) -> bool { - Arc::ptr_eq(&self.arc, &other.arc) - } -} - -impl Eq for Interned<str> {} - -impl<T: Internable + ?Sized> Hash for Interned<T> { - fn hash<H: Hasher>(&self, state: &mut H) { - // NOTE: Cast disposes vtable pointer / slice/str length. - state.write_usize(Arc::as_ptr(&self.arc) as *const () as usize) - } -} - -impl<T: Internable + ?Sized> AsRef<T> for Interned<T> { - #[inline] - fn as_ref(&self) -> &T { - &self.arc - } -} - -impl<T: Internable + ?Sized> Deref for Interned<T> { - type Target = T; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.arc - } -} - -impl<T: Internable + ?Sized> Clone for Interned<T> { - fn clone(&self) -> Self { - Self { arc: self.arc.clone() } - } -} - -impl<T: Debug + Internable + ?Sized> Debug for Interned<T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (*self.arc).fmt(f) - } -} - -impl<T: Display + Internable + ?Sized> Display for Interned<T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (*self.arc).fmt(f) - } -} - -pub struct InternStorage<T: ?Sized> { - map: OnceLock<InternMap<T>>, -} - -#[allow( - clippy::new_without_default, - reason = "this a const fn, so it can't be default yet. See <https://github.com/rust-lang/rust/issues/63065>" -)] -impl<T: ?Sized> InternStorage<T> { - pub const fn new() -> Self { - Self { map: OnceLock::new() } - } -} - -impl<T: Internable + ?Sized> InternStorage<T> { - fn get(&self) -> &InternMap<T> { - self.map.get_or_init(DashMap::default) - } -} - -pub trait Internable: Hash + Eq + 'static { - fn storage() -> &'static InternStorage<Self>; -} - -/// Implements `Internable` for a given list of types, making them usable with `Interned`. -#[macro_export] -#[doc(hidden)] -macro_rules! _impl_internable { - ( $($t:path),+ $(,)? ) => { $( - impl $crate::Internable for $t { - fn storage() -> &'static $crate::InternStorage<Self> { - static STORAGE: $crate::InternStorage<$t> = $crate::InternStorage::new(); - &STORAGE - } - } - )+ }; -} - -pub use crate::_impl_internable as impl_internable; - -impl_internable!(str,); +pub use self::gc::{GarbageCollector, GcInternedSliceVisit, GcInternedVisit}; +pub use self::intern::{InternStorage, Internable, Interned, InternedRef, impl_internable}; +pub use self::intern_slice::{ + InternSliceStorage, InternedSlice, InternedSliceRef, SliceInternable, impl_slice_internable, +}; +pub use self::symbol::{Symbol, symbols as sym}; |