1use std::borrow::Borrow;
2use std::hash::{Hash, Hasher};
3use std::iter;
4
5use either::Either;
6use hashbrown::hash_table::{self, Entry, HashTable};
7
8use crate::fx::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<hash_table::HashTable<(K, V)>>;
144
145impl<K: Eq, V> ShardedHashMap<K, V> {
146 pub fn with_capacity(cap: usize) -> Self {
147 let per_shard_cap = cap.div_ceil(shards());
148 Self::new(|| HashTable::with_capacity(per_shard_cap))
149 }
150 pub fn len(&self) -> usize {
151 self.lock_shards().map(|shard| shard.len()).sum()
152 }
153}
154
155impl<K: Eq + Hash, V> ShardedHashMap<K, V> {
156 #[inline]
157 pub fn get<Q>(&self, key: &Q) -> Option<V>
158 where
159 K: Borrow<Q>,
160 Q: Hash + Eq,
161 V: Clone,
162 {
163 let hash = make_hash(key);
164 let shard = self.lock_shard_by_hash(hash);
165 let (_, value) = shard.find(hash, |(k, _)| k.borrow() == key)?;
166 Some(value.clone())
167 }
168
169 #[inline]
170 pub fn get_or_insert_with(&self, key: K, default: impl FnOnce() -> V) -> V
171 where
172 V: Copy,
173 {
174 let hash = make_hash(&key);
175 let mut shard = self.lock_shard_by_hash(hash);
176
177 match table_entry(&mut shard, hash, &key) {
178 Entry::Occupied(e) => e.get().1,
179 Entry::Vacant(e) => {
180 let value = default();
181 e.insert((key, value));
182 value
183 }
184 }
185 }
186
187 #[inline]
193 pub fn insert_unique(&self, key: K, value: V) {
194 let hash = make_hash(&key);
195 let mut shard = self.lock_shard_by_hash(hash);
196
197 cfg_select! {
198 debug_assertions => match table_entry(&mut shard, hash, &key) {
199 Entry::Occupied(_) => {
200 {
::core::panicking::panic_fmt(format_args!("tried to insert key that\'s already present"));
};panic!("tried to insert key that's already present");
201 }
202 Entry::Vacant(e) => {
203 e.insert((key, value));
204 }
205 },
206 _ => {
207 shard.insert_unique(hash, (key, value), |(k, _)| make_hash(k));
208 }
209 }
210 }
211}
212
213impl<K: Eq + Hash + Copy> ShardedHashMap<K, ()> {
214 #[inline]
215 pub fn intern_ref<Q: ?Sized>(&self, value: &Q, make: impl FnOnce() -> K) -> K
216 where
217 K: Borrow<Q>,
218 Q: Hash + Eq,
219 {
220 let hash = make_hash(value);
221 let mut shard = self.lock_shard_by_hash(hash);
222
223 match table_entry(&mut shard, hash, value) {
224 Entry::Occupied(e) => e.get().0,
225 Entry::Vacant(e) => {
226 let v = make();
227 e.insert((v, ()));
228 v
229 }
230 }
231 }
232
233 #[inline]
234 pub fn intern<Q>(&self, value: Q, make: impl FnOnce(Q) -> K) -> K
235 where
236 K: Borrow<Q>,
237 Q: Hash + Eq,
238 {
239 let hash = make_hash(&value);
240 let mut shard = self.lock_shard_by_hash(hash);
241
242 match table_entry(&mut shard, hash, &value) {
243 Entry::Occupied(e) => e.get().0,
244 Entry::Vacant(e) => {
245 let v = make(value);
246 e.insert((v, ()));
247 v
248 }
249 }
250 }
251}
252
253pub trait IntoPointer {
254 fn into_pointer(&self) -> *const ();
256}
257
258impl<K: Eq + Hash + Copy + IntoPointer> ShardedHashMap<K, ()> {
259 pub fn contains_pointer_to<T: Hash + IntoPointer>(&self, value: &T) -> bool {
260 let hash = make_hash(&value);
261 let shard = self.lock_shard_by_hash(hash);
262 let value = value.into_pointer();
263 shard.find(hash, |(k, ())| k.into_pointer() == value).is_some()
264 }
265}
266
267#[inline]
268pub fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 {
269 let mut state = FxHasher::default();
270 val.hash(&mut state);
271 state.finish()
272}
273
274#[inline]
275fn table_entry<'a, K, V, Q>(
276 table: &'a mut HashTable<(K, V)>,
277 hash: u64,
278 key: &Q,
279) -> Entry<'a, (K, V)>
280where
281 K: Hash + Borrow<Q>,
282 Q: ?Sized + Eq,
283{
284 table.entry(hash, move |(k, _)| k.borrow() == key, |(k, _)| make_hash(k))
285}
286
287#[inline]
293fn get_shard_hash(hash: u64) -> usize {
294 let hash_len = size_of::<usize>();
295 (hash >> (hash_len * 8 - 7 - SHARD_BITS)) as usize
298}