Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/intern/src/symbol.rs')
-rw-r--r--crates/intern/src/symbol.rs99
1 files changed, 42 insertions, 57 deletions
diff --git a/crates/intern/src/symbol.rs b/crates/intern/src/symbol.rs
index 0fa6701ca3..89c3be96fc 100644
--- a/crates/intern/src/symbol.rs
+++ b/crates/intern/src/symbol.rs
@@ -2,16 +2,15 @@
//! supporting compile time declaration of symbols that will never be freed.
use std::{
- borrow::Borrow,
fmt,
- hash::{BuildHasherDefault, Hash, Hasher},
+ hash::{BuildHasher, BuildHasherDefault, Hash},
mem::{self, ManuallyDrop},
ptr::NonNull,
sync::OnceLock,
};
use dashmap::{DashMap, SharedValue};
-use hashbrown::{hash_map::RawEntryMut, HashMap};
+use hashbrown::raw::RawTable;
use rustc_hash::FxHasher;
use triomphe::Arc;
@@ -127,31 +126,39 @@ impl fmt::Debug for Symbol {
const _: () = assert!(size_of::<Symbol>() == size_of::<NonNull<()>>());
const _: () = assert!(align_of::<Symbol>() == align_of::<NonNull<()>>());
-static MAP: OnceLock<DashMap<SymbolProxy, (), BuildHasherDefault<FxHasher>>> = OnceLock::new();
+type Map = DashMap<Symbol, (), BuildHasherDefault<FxHasher>>;
+static MAP: OnceLock<Map> = OnceLock::new();
impl Symbol {
pub fn intern(s: &str) -> Self {
- let (mut shard, hash) = Self::select_shard(s);
+ let storage = MAP.get_or_init(symbols::prefill);
+ let (mut shard, hash) = Self::select_shard(storage, s);
// Atomically,
// - check if `obj` is already in the map
// - if so, copy out its entry, conditionally bumping the backing Arc and return it
// - if not, put it into a box and then into an Arc, insert it, bump the ref-count and return the copy
// 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.
- match shard.raw_entry_mut().from_key_hashed_nocheck(hash, s) {
- RawEntryMut::Occupied(occ) => Self { repr: increase_arc_refcount(occ.key().0) },
- RawEntryMut::Vacant(vac) => Self {
- repr: increase_arc_refcount(
- vac.insert_hashed_nocheck(
- hash,
- SymbolProxy(TaggedArcPtr::arc(Arc::new(Box::<str>::from(s)))),
+ let bucket = match shard.find_or_find_insert_slot(
+ hash,
+ |(other, _)| other.as_str() == s,
+ |(x, _)| Self::hash(storage, x.as_str()),
+ ) {
+ 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,
+ (
+ Symbol { repr: TaggedArcPtr::arc(Arc::new(Box::<str>::from(s))) },
SharedValue::new(()),
- )
- .0
- .0,
- ),
+ ),
+ )
},
- }
+ };
+ // SAFETY: We just retrieved/inserted this bucket.
+ unsafe { bucket.as_ref().0.clone() }
}
pub fn integer(i: usize) -> Self {
@@ -180,38 +187,34 @@ impl Symbol {
symbols::__empty.clone()
}
+ #[inline]
pub fn as_str(&self) -> &str {
self.repr.as_str()
}
#[inline]
fn select_shard(
+ storage: &'static Map,
s: &str,
- ) -> (
- dashmap::RwLockWriteGuard<
- 'static,
- HashMap<SymbolProxy, SharedValue<()>, BuildHasherDefault<FxHasher>>,
- >,
- u64,
- ) {
- let storage = MAP.get_or_init(symbols::prefill);
- let hash = {
- let mut hasher = std::hash::BuildHasher::build_hasher(storage.hasher());
- s.hash(&mut hasher);
- hasher.finish()
- };
+ ) -> (dashmap::RwLockWriteGuard<'static, RawTable<(Symbol, SharedValue<()>)>>, u64) {
+ let hash = Self::hash(storage, s);
let shard_idx = storage.determine_shard(hash as usize);
let shard = &storage.shards()[shard_idx];
(shard.write(), hash)
}
+ #[inline]
+ fn hash(storage: &'static Map, s: &str) -> u64 {
+ storage.hasher().hash_one(s)
+ }
+
#[cold]
fn drop_slow(arc: &Arc<Box<str>>) {
- let (mut shard, hash) = Self::select_shard(arc);
+ let storage = MAP.get_or_init(symbols::prefill);
+ let (mut shard, hash) = Self::select_shard(storage, arc);
match Arc::count(arc) {
- 0 => unreachable!(),
- 1 => unreachable!(),
+ 0 | 1 => unreachable!(),
2 => (),
_ => {
// Another thread has interned another copy
@@ -219,19 +222,17 @@ impl Symbol {
}
}
- let ptr = match shard.raw_entry_mut().from_key_hashed_nocheck::<str>(hash, arc.as_ref()) {
- RawEntryMut::Occupied(occ) => occ.remove_entry(),
- RawEntryMut::Vacant(_) => unreachable!(),
- }
- .0
- .0;
+ let s = &***arc;
+ let (ptr, _) = shard.remove_entry(hash, |(x, _)| x.as_str() == s).unwrap();
+ let ptr = ManuallyDrop::new(ptr);
// SAFETY: We're dropping, we have ownership.
- ManuallyDrop::into_inner(unsafe { ptr.try_as_arc_owned().unwrap() });
+ ManuallyDrop::into_inner(unsafe { ptr.repr.try_as_arc_owned().unwrap() });
debug_assert_eq!(Arc::count(arc), 1);
// Shrink the backing storage if the shard is less than 50% occupied.
if shard.len() * 2 < shard.capacity() {
- shard.shrink_to_fit();
+ let len = shard.len();
+ shard.shrink_to(len, |(x, _)| Self::hash(storage, x.as_str()));
}
}
}
@@ -276,22 +277,6 @@ impl fmt::Display for Symbol {
}
}
-// only exists so we can use `from_key_hashed_nocheck` with a &str
-#[derive(Debug, PartialEq, Eq)]
-struct SymbolProxy(TaggedArcPtr);
-
-impl Hash for SymbolProxy {
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.0.as_str().hash(state);
- }
-}
-
-impl Borrow<str> for SymbolProxy {
- fn borrow(&self) -> &str {
- self.0.as_str()
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;