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.rs223
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};