rustc_data_structures/
sharded.rs1use std::borrow::Borrow;
2use std::collections::hash_map::RawEntryMut;
3use std::hash::{Hash, Hasher};
4use std::{iter, mem};
5
6use either::Either;
7
8use crate::fx::{FxHashMap, FxHasher};
9use crate::sync::{CacheAligned, Lock, LockGuard, Mode, is_dyn_thread_safe};
10
11const SHARD_BITS: usize = 5;
15
16const SHARDS: usize = 1 << SHARD_BITS;
17
18pub enum Sharded<T> {
21 Single(Lock<T>),
22 Shards(Box<[CacheAligned<Lock<T>>; SHARDS]>),
23}
24
25impl<T: Default> Default for Sharded<T> {
26 #[inline]
27 fn default() -> Self {
28 Self::new(T::default)
29 }
30}
31
32impl<T> Sharded<T> {
33 #[inline]
34 pub fn new(mut value: impl FnMut() -> T) -> Self {
35 if is_dyn_thread_safe() {
36 return Sharded::Shards(Box::new(
37 [(); SHARDS].map(|()| CacheAligned(Lock::new(value()))),
38 ));
39 }
40
41 Sharded::Single(Lock::new(value()))
42 }
43
44 #[inline]
46 pub fn get_shard_by_value<K: Hash + ?Sized>(&self, _val: &K) -> &Lock<T> {
47 match self {
48 Self::Single(single) => single,
49 Self::Shards(..) => self.get_shard_by_hash(make_hash(_val)),
50 }
51 }
52
53 #[inline]
54 pub fn get_shard_by_hash(&self, hash: u64) -> &Lock<T> {
55 self.get_shard_by_index(get_shard_hash(hash))
56 }
57
58 #[inline]
59 pub fn get_shard_by_index(&self, _i: usize) -> &Lock<T> {
60 match self {
61 Self::Single(single) => single,
62 Self::Shards(shards) => {
63 unsafe { &shards.get_unchecked(_i & (SHARDS - 1)).0 }
65 }
66 }
67 }
68
69 #[inline]
71 #[track_caller]
72 pub fn lock_shard_by_value<K: Hash + ?Sized>(&self, _val: &K) -> LockGuard<'_, T> {
73 match self {
74 Self::Single(single) => {
75 unsafe { single.lock_assume(Mode::NoSync) }
81 }
82 Self::Shards(..) => self.lock_shard_by_hash(make_hash(_val)),
83 }
84 }
85
86 #[inline]
87 #[track_caller]
88 pub fn lock_shard_by_hash(&self, hash: u64) -> LockGuard<'_, T> {
89 self.lock_shard_by_index(get_shard_hash(hash))
90 }
91
92 #[inline]
93 #[track_caller]
94 pub fn lock_shard_by_index(&self, _i: usize) -> LockGuard<'_, T> {
95 match self {
96 Self::Single(single) => {
97 unsafe { single.lock_assume(Mode::NoSync) }
103 }
104 Self::Shards(shards) => {
105 unsafe { shards.get_unchecked(_i & (SHARDS - 1)).0.lock_assume(Mode::Sync) }
113 }
114 }
115 }
116
117 #[inline]
118 pub fn lock_shards(&self) -> impl Iterator<Item = LockGuard<'_, T>> {
119 match self {
120 Self::Single(single) => Either::Left(iter::once(single.lock())),
121 Self::Shards(shards) => Either::Right(shards.iter().map(|shard| shard.0.lock())),
122 }
123 }
124
125 #[inline]
126 pub fn try_lock_shards(&self) -> impl Iterator<Item = Option<LockGuard<'_, T>>> {
127 match self {
128 Self::Single(single) => Either::Left(iter::once(single.try_lock())),
129 Self::Shards(shards) => Either::Right(shards.iter().map(|shard| shard.0.try_lock())),
130 }
131 }
132}
133
134#[inline]
135pub fn shards() -> usize {
136 if is_dyn_thread_safe() {
137 return SHARDS;
138 }
139
140 1
141}
142
143pub type ShardedHashMap<K, V> = Sharded<FxHashMap<K, V>>;
144
145impl<K: Eq, V> ShardedHashMap<K, V> {
146 pub fn len(&self) -> usize {
147 self.lock_shards().map(|shard| shard.len()).sum()
148 }
149}
150
151impl<K: Eq + Hash + Copy> ShardedHashMap<K, ()> {
152 #[inline]
153 pub fn intern_ref<Q: ?Sized>(&self, value: &Q, make: impl FnOnce() -> K) -> K
154 where
155 K: Borrow<Q>,
156 Q: Hash + Eq,
157 {
158 let hash = make_hash(value);
159 let mut shard = self.lock_shard_by_hash(hash);
160 let entry = shard.raw_entry_mut().from_key_hashed_nocheck(hash, value);
161
162 match entry {
163 RawEntryMut::Occupied(e) => *e.key(),
164 RawEntryMut::Vacant(e) => {
165 let v = make();
166 e.insert_hashed_nocheck(hash, v, ());
167 v
168 }
169 }
170 }
171
172 #[inline]
173 pub fn intern<Q>(&self, value: Q, make: impl FnOnce(Q) -> K) -> K
174 where
175 K: Borrow<Q>,
176 Q: Hash + Eq,
177 {
178 let hash = make_hash(&value);
179 let mut shard = self.lock_shard_by_hash(hash);
180 let entry = shard.raw_entry_mut().from_key_hashed_nocheck(hash, &value);
181
182 match entry {
183 RawEntryMut::Occupied(e) => *e.key(),
184 RawEntryMut::Vacant(e) => {
185 let v = make(value);
186 e.insert_hashed_nocheck(hash, v, ());
187 v
188 }
189 }
190 }
191}
192
193pub trait IntoPointer {
194 fn into_pointer(&self) -> *const ();
196}
197
198impl<K: Eq + Hash + Copy + IntoPointer> ShardedHashMap<K, ()> {
199 pub fn contains_pointer_to<T: Hash + IntoPointer>(&self, value: &T) -> bool {
200 let hash = make_hash(&value);
201 let shard = self.lock_shard_by_hash(hash);
202 let value = value.into_pointer();
203 shard.raw_entry().from_hash(hash, |entry| entry.into_pointer() == value).is_some()
204 }
205}
206
207#[inline]
208pub fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 {
209 let mut state = FxHasher::default();
210 val.hash(&mut state);
211 state.finish()
212}
213
214#[inline]
220fn get_shard_hash(hash: u64) -> usize {
221 let hash_len = mem::size_of::<usize>();
222 (hash >> (hash_len * 8 - 7 - SHARD_BITS)) as usize
225}