Merge #8303
8303: Allow interning strings r=jonas-schievink a=jonas-schievink We don't use it yet, that's a bit more complicated. bors r+ Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
commit
eb264fb819
@ -3,17 +3,20 @@
|
|||||||
//! Eventually this should probably be replaced with salsa-based interning.
|
//! Eventually this should probably be replaced with salsa-based interning.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
fmt::{self, Debug},
|
fmt::{self, Debug},
|
||||||
hash::{BuildHasherDefault, Hash},
|
hash::{BuildHasherDefault, Hash},
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use dashmap::{DashMap, SharedValue};
|
use dashmap::{lock::RwLockWriteGuard, DashMap, SharedValue};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use rustc_hash::FxHasher;
|
use rustc_hash::FxHasher;
|
||||||
|
|
||||||
type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>;
|
type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>;
|
||||||
|
type Guard<T> =
|
||||||
|
RwLockWriteGuard<'static, HashMap<Arc<T>, SharedValue<()>, BuildHasherDefault<FxHasher>>>;
|
||||||
|
|
||||||
#[derive(Hash)]
|
#[derive(Hash)]
|
||||||
pub struct Interned<T: Internable + ?Sized> {
|
pub struct Interned<T: Internable + ?Sized> {
|
||||||
@ -22,10 +25,22 @@ pub struct Interned<T: Internable + ?Sized> {
|
|||||||
|
|
||||||
impl<T: Internable> Interned<T> {
|
impl<T: Internable> Interned<T> {
|
||||||
pub fn new(obj: T) -> Self {
|
pub fn new(obj: T) -> Self {
|
||||||
|
match Interned::lookup(&obj) {
|
||||||
|
Ok(this) => this,
|
||||||
|
Err(shard) => {
|
||||||
|
let arc = Arc::new(obj);
|
||||||
|
Self::alloc(arc, shard)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Internable + ?Sized> Interned<T> {
|
||||||
|
fn lookup(obj: &T) -> Result<Self, Guard<T>> {
|
||||||
let storage = T::storage().get();
|
let storage = T::storage().get();
|
||||||
let shard_idx = storage.determine_map(&obj);
|
let shard_idx = storage.determine_map(obj);
|
||||||
let shard = &storage.shards()[shard_idx];
|
let shard = &storage.shards()[shard_idx];
|
||||||
let mut shard = shard.write();
|
let shard = shard.write();
|
||||||
|
|
||||||
// Atomically,
|
// Atomically,
|
||||||
// - check if `obj` is already in the map
|
// - check if `obj` is already in the map
|
||||||
@ -34,13 +49,15 @@ pub fn new(obj: T) -> Self {
|
|||||||
// This needs to be atomic (locking the shard) to avoid races with other thread, which could
|
// 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.
|
// insert the same object between us looking it up and inserting it.
|
||||||
|
|
||||||
// FIXME: avoid double lookup by using raw entry API (once stable, or when hashbrown can be
|
// FIXME: avoid double lookup/hashing by using raw entry API (once stable, or when
|
||||||
// plugged into dashmap)
|
// hashbrown can be plugged into dashmap)
|
||||||
if let Some((arc, _)) = shard.get_key_value(&obj) {
|
match shard.get_key_value(obj) {
|
||||||
return Self { arc: arc.clone() };
|
Some((arc, _)) => Ok(Self { arc: arc.clone() }),
|
||||||
|
None => Err(shard),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let arc = Arc::new(obj);
|
fn alloc(arc: Arc<T>, mut shard: Guard<T>) -> Self {
|
||||||
let arc2 = arc.clone();
|
let arc2 = arc.clone();
|
||||||
|
|
||||||
shard.insert(arc2, SharedValue::new(()));
|
shard.insert(arc2, SharedValue::new(()));
|
||||||
@ -49,6 +66,18 @@ pub fn new(obj: T) -> Self {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Interned<str> {
|
||||||
|
pub fn new_str(s: &str) -> Self {
|
||||||
|
match Interned::lookup(s) {
|
||||||
|
Ok(this) => this,
|
||||||
|
Err(shard) => {
|
||||||
|
let arc = Arc::<str>::from(s);
|
||||||
|
Self::alloc(arc, shard)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Internable + ?Sized> Drop for Interned<T> {
|
impl<T: Internable + ?Sized> Drop for Interned<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
@ -98,6 +127,14 @@ fn eq(&self, other: &Self) -> bool {
|
|||||||
|
|
||||||
impl<T: Internable> Eq for Interned<T> {}
|
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> AsRef<T> for Interned<T> {
|
impl<T: Internable + ?Sized> AsRef<T> for Interned<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn as_ref(&self) -> &T {
|
fn as_ref(&self) -> &T {
|
||||||
@ -157,4 +194,4 @@ fn storage() -> &'static InternStorage<Self> {
|
|||||||
)+ };
|
)+ };
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_internable!(crate::type_ref::TypeRef, crate::type_ref::TraitRef, crate::path::ModPath);
|
impl_internable!(crate::type_ref::TypeRef, crate::type_ref::TraitRef, crate::path::ModPath, str);
|
||||||
|
Loading…
Reference in New Issue
Block a user