lady_deirdre/sync/table.rs
1////////////////////////////////////////////////////////////////////////////////
2// This file is part of "Lady Deirdre", a compiler front-end foundation //
3// technology. //
4// //
5// This work is proprietary software with source-available code. //
6// //
7// To copy, use, distribute, or contribute to this work, you must agree to //
8// the terms of the General License Agreement: //
9// //
10// https://github.com/Eliah-Lakhin/lady-deirdre/blob/master/EULA.md //
11// //
12// The agreement grants a Basic Commercial License, allowing you to use //
13// this work in non-commercial and limited commercial products with a total //
14// gross revenue cap. To remove this commercial limit for one of your //
15// products, you must acquire a Full Commercial License. //
16// //
17// If you contribute to the source code, documentation, or related materials, //
18// you must grant me an exclusive license to these contributions. //
19// Contributions are governed by the "Contributions" section of the General //
20// License Agreement. //
21// //
22// Copying the work in parts is strictly forbidden, except as permitted //
23// under the General License Agreement. //
24// //
25// If you do not or cannot agree to the terms of this Agreement, //
26// do not use this work. //
27// //
28// This work is provided "as is", without any warranties, express or implied, //
29// except where such disclaimers are legally invalid. //
30// //
31// Copyright (c) 2024 Ilya Lakhin (Илья Александрович Лахин). //
32// All rights reserved. //
33////////////////////////////////////////////////////////////////////////////////
34
35///////////////////////////////////////////////////////////////////////////////////////
36// A part of this file's source code is an adaptation of Joel Wejdenstål's and //
37// the authors' "DashMap" work. //
38// //
39// The original work by Joel Wejdenstål and the authors is available here: //
40// https://github.com/xacrimon/dashmap/tree/626b98dab3c124cd9cd4960d0306da5d65918dfc //
41// //
42// Joel Wejdenstål and the authors provided their work under the following terms: //
43// //
44// MIT License //
45// //
46// Copyright (c) 2019 Acrimon //
47// //
48// Permission is hereby granted, free of charge, to any person obtaining a copy //
49// of this software and associated documentation files (the "Software"), to deal //
50// in the Software without restriction, including without limitation the rights //
51// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell //
52// copies of the Software, and to permit persons to whom the Software is //
53// furnished to do so, subject to the following conditions: //
54// //
55// The above copyright notice and this permission notice shall be included in all //
56// copies or substantial portions of the Software. //
57// //
58// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
59// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
60// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE //
61// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
62// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, //
63// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE //
64// SOFTWARE. //
65// //
66// Kindly be advised that the terms governing the distribution of my work are //
67// distinct from those pertaining to the original "DashMap" work. //
68///////////////////////////////////////////////////////////////////////////////////////
69
70use std::{
71 borrow::Borrow,
72 collections::{
73 hash_map,
74 hash_map::{Drain, Entry, OccupiedEntry, VacantEntry},
75 HashMap,
76 },
77 fmt::{Debug, Formatter},
78 hash::{BuildHasher, Hash, Hasher, RandomState},
79 iter::FusedIterator,
80 mem::{size_of, transmute},
81 ops::{Deref, DerefMut},
82 sync::{RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockError},
83 vec,
84};
85
86use crate::report::ld_unreachable;
87
88/// A sharded read-write lock of the HashMap.
89///
90/// This object provides concurrent read-write access to the HashMap entries.
91///
92/// The concurrent read and write access to the **distinct** entries of
93/// the Table are likely will not block each other if the entry keys are well
94/// distributed by the hasher.
95///
96/// The underlying implementation achieves this feature by distributing the
97/// hash-map entries between the prepared array of elements of fixed
98/// size (referred to as the "shards amount") that depends on the available
99/// parallelism.
100///
101/// By default, the shards amount estimated automatically based on the number of
102/// CPUs, but could be overridden manually in
103/// the [with_capacity_and_hasher_and_shards](Self::with_capacity_and_hasher_and_shards)
104/// constructor.
105///
106/// The `K` generic parameter specifies a type of the entry key. This type
107/// is assumed to implement a [Hash] interface.
108///
109/// The `V` generic parameter specifies a type of the entry value.
110///
111/// The `S` generic parameter specifies a hasher algorithm. By default,
112/// the Table uses standard [RandomState].
113///
114/// If you are familiar with
115/// the [dashmap](https://github.com/xacrimon/dashmap/tree/626b98dab3c124cd9cd4960d0306da5d65918dfc)
116/// crate, Lady Deirdre's Table provides almost the same set of features with a
117/// few differences:
118///
119/// - The Table interface allows one-shard configuration, reduces the Table
120/// to a simple `RwLock<HashMap<K, V, S>>`. In particular, under
121/// the `wasm` targets, the amount of shards is 1.
122/// - The Table is fully built on top of the standard library features without
123/// any third-party dependencies. In particular, the Table implementation
124/// uses [RwLock] instead of the lock_api's RwLock in the DashMap.
125/// - There are some opinionated differences in the API between these two
126/// implementations, but, in general, both of them are trying to mimic the
127/// standard's HashMap API for end-user convenience.
128pub struct Table<K, V, S = RandomState> {
129 shift: usize,
130 shards: Box<[RwLock<HashMap<K, V, S>>]>,
131 hasher: S,
132}
133
134impl<K, V, S> IntoIterator for Table<K, V, S> {
135 type Item = (K, V);
136 type IntoIter = TableIntoIter<K, V, S>;
137
138 fn into_iter(self) -> Self::IntoIter {
139 let mut shards = Vec::from(self.shards).into_iter();
140
141 let probe = match shards.next() {
142 Some(probe) => probe
143 .into_inner()
144 .unwrap_or_else(|poison| poison.into_inner())
145 .into_iter(),
146
147 // Safety: `shards` array is never empty.
148 None => unsafe { ld_unreachable!("Empty shards array.") },
149 };
150
151 TableIntoIter { probe, shards }
152 }
153}
154
155impl<K: Hash + Eq, V, S: BuildHasher + Default + Clone> Default for Table<K, V, S> {
156 #[inline(always)]
157 fn default() -> Self {
158 Self::new()
159 }
160}
161
162impl<K: Hash + Eq, V, S: BuildHasher> Table<K, V, S> {
163 /// A default Table constructor.
164 #[inline(always)]
165 pub fn new() -> Self
166 where
167 S: Default + Clone,
168 {
169 Self::with_capacity(0)
170 }
171
172 /// A Table constructor with a specified preallocated `capacity` of entries.
173 #[inline(always)]
174 pub fn with_capacity(capacity: usize) -> Self
175 where
176 S: Default + Clone,
177 {
178 Self::with_capacity_and_hasher(capacity, S::default())
179 }
180
181 /// A Table constructor with a specified preallocated `capacity` of entries,
182 /// and the key `hasher` instance.
183 #[inline(always)]
184 pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> Self
185 where
186 S: Clone,
187 {
188 Self::with_capacity_and_hasher_and_shards(capacity, hasher, shards_amount())
189 }
190
191 /// A Table constructor with a specified preallocated `capacity` of entries,
192 /// the key `hasher` instance, and the amount of `shards`.
193 ///
194 /// The `shards` amount must me a positive number and a power of two.
195 ///
196 /// The shards equal to one is a valid argument, which makes the Table
197 /// similar to `RwLock<HashMap>`.
198 ///
199 /// **Panic**
200 ///
201 /// Panics, if the `shards` value is zero or is not a power of two.
202 pub fn with_capacity_and_hasher_and_shards(capacity: usize, hasher: S, shards: usize) -> Self
203 where
204 S: Clone,
205 {
206 if !shards.is_power_of_two() {
207 panic!("Table shards amount {shards} is not a power of two.");
208 }
209
210 let shard_capacity = ((capacity + shards - 1) & !(shards - 1)) / shards;
211
212 let shift = match shards > 1 {
213 true => size_of::<usize>() * 8 - shards.trailing_zeros() as usize,
214 false => 0,
215 };
216
217 let shards = (0..shards)
218 .map(|_| {
219 RwLock::new(HashMap::with_capacity_and_hasher(
220 shard_capacity,
221 hasher.clone(),
222 ))
223 })
224 .collect();
225
226 Self {
227 shift,
228 shards,
229 hasher,
230 }
231 }
232
233 /// Returns true if the Table has an entry with the specified `key`.
234 ///
235 /// Blocks the current thread if the entry or its shard is locked for write.
236 pub fn contains_key<Q>(&self, key: &Q) -> bool
237 where
238 K: Borrow<Q>,
239 Q: Hash + Eq + ?Sized,
240 {
241 let shard = self.shard_of(key);
242
243 let guard = shard.read().unwrap_or_else(|poison| poison.into_inner());
244
245 guard.contains_key(key)
246 }
247
248 /// Grants read access to the entry's value by `key`.
249 ///
250 /// Returns None if the Table does not have an entry with specified key.
251 ///
252 /// Blocks the current thread if the entry or its shard is locked for write.
253 ///
254 /// The returning guard object locks the entry's shard for read.
255 pub fn get<Q>(&self, key: &Q) -> Option<TableReadGuard<K, V, S>>
256 where
257 K: Borrow<Q>,
258 Q: Hash + Eq + ?Sized,
259 {
260 let shard = self.shard_of(key);
261
262 let guard = shard.read().unwrap_or_else(|poison| poison.into_inner());
263
264 let value = guard.get(key)?;
265
266 // Safety:
267 // Prolongs reference lifetime to `self` lifetime.
268 // The value will be valid for as long as the guard is held.
269 let value = unsafe { transmute::<&V, &V>(value) };
270
271 Some(TableReadGuard {
272 value,
273 _guard: guard,
274 })
275 }
276
277 /// Grants read access to the entry's value by `key`.
278 ///
279 /// Returns None if the Table does not have an entry with specified key.
280 ///
281 /// Returns None if the entry or its shard is locked for write.
282 ///
283 /// This function does not block the current thread.
284 ///
285 /// The returning guard object locks the entry's shard for read.
286 pub fn try_get<Q>(&self, key: &Q) -> Option<TableReadGuard<K, V, S>>
287 where
288 K: Borrow<Q>,
289 Q: Hash + Eq + ?Sized,
290 {
291 let shard = self.shard_of(key);
292
293 let guard = match shard.try_read() {
294 Ok(guard) => guard,
295 Err(TryLockError::Poisoned(poison)) => poison.into_inner(),
296 Err(TryLockError::WouldBlock) => return None,
297 };
298
299 let value = guard.get(key)?;
300
301 // Safety:
302 // Prolongs reference lifetime to `self` lifetime.
303 // The value will be valid for as long as the guard is held.
304 let value = unsafe { transmute::<&V, &V>(value) };
305
306 Some(TableReadGuard {
307 value,
308 _guard: guard,
309 })
310 }
311
312 /// Grants read-write access to the entry's value by `key`.
313 ///
314 /// Returns None if the Table does not have an entry with specified key.
315 ///
316 /// Blocks the current thread if the entry or its shard is locked for read
317 /// or write.
318 ///
319 /// The returning guard object locks the entry's shard for write.
320 pub fn get_mut<Q>(&self, key: &Q) -> Option<TableWriteGuard<K, V, S>>
321 where
322 K: Borrow<Q>,
323 Q: Hash + Eq + ?Sized,
324 {
325 let shard = self.shard_of(key);
326
327 let mut guard = shard.write().unwrap_or_else(|poison| poison.into_inner());
328
329 let value = guard.get_mut(key)?;
330
331 // Safety:
332 // Prolongs reference lifetime to `self` lifetime.
333 // The value will be valid for as long as the guard is held.
334 let value = unsafe { transmute::<&mut V, &mut V>(value) };
335
336 Some(TableWriteGuard {
337 value,
338 _guard: guard,
339 })
340 }
341
342 /// Grants read-write access to the table entry by `key` for in-place
343 /// manipulation.
344 ///
345 /// The meaning of this function is similar to the [HashMap::entry] function.
346 ///
347 /// The function blocks the current thread if the shard of entries
348 /// determined by `key` is locked for read or write.
349 ///
350 /// The returning guard object locks the shard for write.
351 pub fn entry(&self, key: K) -> TableEntry<K, V, S> {
352 let shard = self.shard_of(&key);
353
354 let mut guard = shard.write().unwrap_or_else(|poison| poison.into_inner());
355
356 let entry = guard.entry(key);
357
358 // Safety:
359 // Prolongs reference lifetime to `self` lifetime.
360 // The value will be valid for as long as the guard is held.
361 let entry = unsafe { transmute::<Entry<'_, K, V>, Entry<'_, K, V>>(entry) };
362
363 match entry {
364 Entry::Occupied(entry) => TableEntry::Occupied(TableOccupiedEntry { entry, guard }),
365 Entry::Vacant(entry) => TableEntry::Vacant(TableVacantEntry { entry, guard }),
366 }
367 }
368
369 /// Inserts a key-value entry into this Table.
370 ///
371 /// Returns the previous value mapped to the `key`.
372 ///
373 /// Returns None if there is no entry that belongs to the `key`.
374 ///
375 /// Blocks the current thread if the entry or its shard is locked for read
376 /// or write.
377 pub fn insert(&self, key: K, value: V) -> Option<V> {
378 let shard = self.shard_of(&key);
379
380 let mut guard = shard.write().unwrap_or_else(|poison| poison.into_inner());
381
382 guard.insert(key, value)
383 }
384
385 /// Removes a key-value entry from this Table.
386 ///
387 /// Returns the value of the removed entry.
388 ///
389 /// Returns None if there is no entry that belongs to the `key`.
390 ///
391 /// Blocks the current thread if the entry or its shard is locked for read
392 /// or write.
393 pub fn remove<Q>(&self, key: &Q) -> Option<V>
394 where
395 K: Borrow<Q>,
396 Q: Hash + Eq + ?Sized,
397 {
398 let shard = self.shard_of(key);
399
400 let mut guard = shard.write().unwrap_or_else(|poison| poison.into_inner());
401
402 guard.remove(key)
403 }
404
405 /// Removes a key-value entry from this Table and returns the removed
406 /// key-value pair.
407 ///
408 /// Returns None if there is no entry that belongs to the `key`.
409 ///
410 /// Blocks the current thread if the entry or its shard is locked for read
411 /// or write.
412 pub fn remove_entry<Q>(&self, key: &Q) -> Option<(K, V)>
413 where
414 K: Borrow<Q>,
415 Q: Hash + Eq + ?Sized,
416 {
417 let shard = self.shard_of(key);
418
419 let mut guard = shard.write().unwrap_or_else(|poison| poison.into_inner());
420
421 guard.remove_entry(key)
422 }
423
424 /// Clears the Table, returning an iterator over removed
425 /// key-value entry pairs.
426 ///
427 /// This function keeps allocated memory for reuse.
428 ///
429 /// Under the hood, the function sequentially locks each shard for write,
430 /// and calls the [HashMap::drain] function on each of them.
431 ///
432 /// The returning iterator locks each Table shard one by one for write,
433 /// and it **does not unlock** them until the iterator fully consumed or
434 /// dropped.
435 ///
436 /// Hence, concurrent access to the Table will be in sync with `drain`.
437 pub fn drain(&self) -> TableDrain<'_, K, V, S> {
438 let mut guard = match self.shards.first() {
439 Some(shard) => shard.write().unwrap_or_else(|poison| poison.into_inner()),
440
441 // Safety: `shards` array is never empty.
442 None => unsafe { ld_unreachable!("Empty shards array.") },
443 };
444
445 let mut probes = Vec::with_capacity(self.shards.len());
446
447 let drain = guard.drain();
448
449 // Safety:
450 // Prolongs reference lifetime to `self` lifetime.
451 // The value will be valid for as long as the guard is held.
452 let drain = unsafe { transmute::<Drain<'_, K, V>, Drain<'_, K, V>>(drain) };
453
454 probes.push(ProbeDrain {
455 drain: Some(drain),
456 _guard: guard,
457 });
458
459 TableDrain {
460 probes,
461 table: self,
462 }
463 }
464
465 /// Retains only the key-value entries specified by predicate.
466 ///
467 /// The `f` predicate parameter tests each Table key-value pair, and if the
468 /// predicate returns false, the retain function removes this entry.
469 ///
470 /// Under the hood, the function sequentially locks each shard one by one
471 /// for write, and calls the [HashMap::retain] function with this predicate
472 /// on each of them.
473 ///
474 /// The retain function **does not unlock** previously locked shards until
475 /// finishes.
476 ///
477 /// Hence, the concurrent access to the Table will be in sync with `retain`.
478 pub fn retain<F>(&self, mut f: F)
479 where
480 F: FnMut(&K, &mut V) -> bool,
481 {
482 let mut guards = Vec::with_capacity(self.shards.len());
483
484 for shard in self.shards.iter() {
485 let mut guard = shard.write().unwrap_or_else(|poison| poison.into_inner());
486
487 guard.retain(&mut f);
488
489 guards.push(guard);
490 }
491 }
492
493 /// Clears the Table, removing all key-value entries.
494 ///
495 /// This function keeps allocated memory for reuse.
496 ///
497 /// Under the hood, the function sequentially locks each shard one by one
498 /// for write, and calls the [HashMap::clear] function on each of them.
499 ///
500 /// The clear function **does not unlock** previously locked shards until
501 /// finishes.
502 ///
503 /// Hence, the concurrent access to the Table will be in sync with `clear`.
504 pub fn clear(&self) {
505 let mut guards = Vec::with_capacity(self.shards.len());
506
507 for shard in self.shards.iter() {
508 let mut guard = shard.write().unwrap_or_else(|poison| poison.into_inner());
509
510 guard.clear();
511
512 guards.push(guard);
513 }
514 }
515
516 /// Shrinks the capacity of the Table as much as possible by locking each
517 /// shard one by one for write and calling the [HashMap::shrink_to_fit]
518 /// function on each of them.
519 ///
520 /// The shrink_to_fit function **unlocks** previously locked shard
521 /// immediately after shrinking.
522 pub fn shrink_to_fit(&self) {
523 for shard in self.shards.iter() {
524 let mut guard = shard.write().unwrap_or_else(|poison| poison.into_inner());
525
526 guard.shrink_to_fit();
527 }
528 }
529
530 /// Provides access to the inner hasher.
531 ///
532 /// The returning hasher is the hasher used for the shards index
533 /// computations, and the hasher of each shard's HashMap.
534 pub fn hasher(&self) -> &S {
535 &self.hasher
536 }
537
538 /// Computes an index of the shard within the [shards](Self::shards) array
539 /// for the specified `key`.
540 ///
541 /// The returning value is **guaranteed** to be within the shards array
542 /// bounds.
543 #[inline(always)]
544 pub fn shard_index_of<Q>(&self, key: &Q) -> usize
545 where
546 K: Borrow<Q>,
547 Q: Hash + Eq + ?Sized,
548 {
549 if self.shards.len() == 1 {
550 return 0;
551 }
552
553 let mut hasher = self.hasher.build_hasher();
554
555 key.hash(&mut hasher);
556
557 let hash = hasher.finish() as usize;
558
559 let shard = (hash << 7) >> self.shift;
560
561 if shard >= self.shards.len() {
562 // Safety: Hash is uniform in the shards space.
563 unsafe {
564 ld_unreachable!("Table shard index out of bounds.");
565 }
566 }
567
568 shard
569 }
570
571 /// Provides access to the shard by `key`.
572 ///
573 /// This function does not lock the shard.
574 ///
575 /// Calling to this function is similar
576 /// to `self.shards()[self.shard_index_of(key)]`, but is slightly faster
577 /// because the underlying implementation avoids unnecessary checks of the
578 /// bounds.
579 #[inline(always)]
580 pub fn shard_of<Q>(&self, key: &Q) -> &RwLock<HashMap<K, V, S>>
581 where
582 K: Borrow<Q>,
583 Q: Hash + Eq + ?Sized,
584 {
585 shard_of(self, key)
586 }
587
588 /// Provides access to the underlying shards array.
589 #[inline(always)]
590 pub fn shards(&self) -> &[RwLock<HashMap<K, V, S>>] {
591 &self.shards
592 }
593}
594
595/// A RAII guard, that provides read access to the [Table] entry's value.
596///
597/// Created by the [Table::get] or [Table::try_get] methods.
598///
599/// The guard keeps the corresponding Table shard locked for read until
600/// the guard is dropped.
601// Safety: Entries order reflects guards drop semantics.
602pub struct TableReadGuard<'a, K, V, S = RandomState> {
603 value: &'a V,
604 _guard: RwLockReadGuard<'a, HashMap<K, V, S>>,
605}
606
607impl<'a, K, V, S> Deref for TableReadGuard<'a, K, V, S> {
608 type Target = V;
609
610 #[inline(always)]
611 fn deref(&self) -> &Self::Target {
612 self.value
613 }
614}
615
616/// A RAII guard, that provides read and write access to the [Table] entry's
617/// value.
618///
619/// Created by the [Table::get_mut] method.
620///
621/// The guard keeps the corresponding Table shard locked for write until
622/// the guard is dropped.
623// Safety: Entries order reflects guards drop semantics.
624pub struct TableWriteGuard<'a, K, V, S = RandomState> {
625 value: &'a mut V,
626 _guard: RwLockWriteGuard<'a, HashMap<K, V, S>>,
627}
628
629impl<'a, K, V, S> Deref for TableWriteGuard<'a, K, V, S> {
630 type Target = V;
631
632 #[inline(always)]
633 fn deref(&self) -> &Self::Target {
634 self.value
635 }
636}
637
638impl<'a, K, V, S> DerefMut for TableWriteGuard<'a, K, V, S> {
639 #[inline(always)]
640 fn deref_mut(&mut self) -> &mut Self::Target {
641 self.value
642 }
643}
644
645/// A RAII guard, which is a view into a single entry in a [Table].
646///
647/// The entry may either be vacant or occupied.
648///
649/// Created by the [Table::entry] method.
650///
651/// An API of this object is similar to the HashMap's [Entry] API.
652///
653/// The guard keeps the corresponding Table shard locked for write until
654/// the guard is dropped.
655pub enum TableEntry<'a, K: 'a, V: 'a, S = RandomState> {
656 /// An occupied entry.
657 Occupied(TableOccupiedEntry<'a, K, V, S>),
658
659 /// A vacant entry.
660 Vacant(TableVacantEntry<'a, K, V, S>),
661}
662
663impl<'a, K, V: Default, S> TableEntry<'a, K, V, S> {
664 /// Ensures a value is in the entry by inserting the default value if empty,
665 /// and returns a read-write access guard to the value.
666 ///
667 /// This function is similar to the [Entry::or_default] function.
668 #[inline(always)]
669 pub fn or_default(self) -> TableWriteGuard<'a, K, V, S> {
670 match self {
671 Self::Occupied(entry) => entry.into_mut(),
672 Self::Vacant(entry) => entry.insert(V::default()),
673 }
674 }
675}
676
677impl<'a, K, V, S> TableEntry<'a, K, V, S> {
678 /// Ensures a value is in the entry by inserting the `default` if empty,
679 /// and returns a read-write access guard to the value.
680 ///
681 /// This function is similar to the [Entry::or_insert] function.
682 #[inline(always)]
683 pub fn or_insert(self, default: V) -> TableWriteGuard<'a, K, V, S> {
684 match self {
685 Self::Occupied(entry) => entry.into_mut(),
686 Self::Vacant(entry) => entry.insert(default),
687 }
688 }
689
690 /// Ensures a value is in the entry by inserting the result of
691 /// the `default` function if empty, and returns a read-write access guard
692 /// to the value.
693 ///
694 /// This function is similar to the [Entry::or_insert_with] function.
695 #[inline(always)]
696 pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> TableWriteGuard<'a, K, V, S> {
697 match self {
698 Self::Occupied(entry) => entry.into_mut(),
699 Self::Vacant(entry) => entry.insert(default()),
700 }
701 }
702
703 /// Ensures a value is in the entry by inserting the result of
704 /// the `default` function if empty, and returns a read-write access guard
705 /// to the value.
706 ///
707 /// The `default` function receives a key of the entry.
708 ///
709 /// This function is similar to the [Entry::or_insert_with_key] function.
710 #[inline(always)]
711 pub fn or_insert_with_key<F: FnOnce(&K) -> V>(
712 self,
713 default: F,
714 ) -> TableWriteGuard<'a, K, V, S> {
715 match self {
716 Self::Occupied(entry) => entry.into_mut(),
717 Self::Vacant(entry) => {
718 let value = default(entry.key());
719 entry.insert(value)
720 }
721 }
722 }
723
724 /// Returns a reference to this entry’s key.
725 ///
726 /// This function is similar to the [Entry::key] function.
727 #[inline(always)]
728 pub fn key(&self) -> &K {
729 match self {
730 Self::Occupied(entry) => entry.key(),
731 Self::Vacant(entry) => entry.key(),
732 }
733 }
734
735 /// Provides in-place mutable access to an occupied entry before any
736 /// potential inserts into the Table.
737 ///
738 /// This function is similar to the [Entry::and_modify] function.
739 #[inline(always)]
740 pub fn and_modify<F: FnOnce(&mut V)>(self, f: F) -> Self {
741 match self {
742 Self::Occupied(mut entry) => {
743 f(entry.get_mut());
744 Self::Occupied(entry)
745 }
746 Self::Vacant(entry) => Self::Vacant(entry),
747 }
748 }
749}
750
751/// A RAII guard, which is a view into an occupied entry in a [Table].
752///
753/// It is part of the [TableEntry] enum.
754///
755/// An API of this object is similar to the HashMap's [OccupiedEntry] API.
756///
757/// The guard keeps the corresponding Table shard locked for write until
758/// the guard is dropped.
759// Safety: Entries order reflects guards drop semantics.
760pub struct TableOccupiedEntry<'a, K: 'a, V: 'a, S = RandomState> {
761 entry: OccupiedEntry<'a, K, V>,
762 guard: RwLockWriteGuard<'a, HashMap<K, V, S>>,
763}
764
765impl<K: Debug, V: Debug, S> Debug for TableOccupiedEntry<'_, K, V, S> {
766 #[inline(always)]
767 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
768 Debug::fmt(&self.entry, formatter)
769 }
770}
771
772impl<'a, K, V, S> TableOccupiedEntry<'a, K, V, S> {
773 /// Returns a reference to the entry's key.
774 ///
775 /// This function is similar to the [OccupiedEntry::key] function.
776 #[inline(always)]
777 pub fn key(&self) -> &K {
778 self.entry.key()
779 }
780
781 /// Takes the ownership of the key-value pair of the entry from the Table.
782 ///
783 /// This function is similar to the [OccupiedEntry::remove_entry] function.
784 #[inline(always)]
785 pub fn remove_entry(self) -> (K, V) {
786 self.entry.remove_entry()
787 }
788
789 /// Returns a reference to the entry's value.
790 ///
791 /// This function is similar to the [OccupiedEntry::get] function.
792 #[inline(always)]
793 pub fn get(&self) -> &V {
794 self.entry.get()
795 }
796
797 /// Returns a mutable reference to the entry's value.
798 ///
799 /// This function is similar to the [OccupiedEntry::get_mut] function.
800 #[inline(always)]
801 pub fn get_mut(&mut self) -> &mut V {
802 self.entry.get_mut()
803 }
804
805 /// Converts this RAII guard into a [TableWriteGuard] RAII guard that grants
806 /// read-write access to the entry value.
807 ///
808 /// This function keeps the corresponding shard locked.
809 ///
810 /// This function is similar to the [OccupiedEntry::into_mut] function.
811 #[inline(always)]
812 pub fn into_mut(self) -> TableWriteGuard<'a, K, V, S> {
813 let value = self.entry.into_mut();
814
815 TableWriteGuard {
816 value,
817 _guard: self.guard,
818 }
819 }
820
821 /// Sets the value of the entry, and returns the entry’s old value.
822 ///
823 /// This function is similar to the [OccupiedEntry::insert] function.
824 #[inline(always)]
825 pub fn insert(&mut self, value: V) -> V {
826 self.entry.insert(value)
827 }
828
829 /// Takes the value out of the entry, and returns it.
830 ///
831 /// This function is similar to the [OccupiedEntry::remove] function.
832 #[inline(always)]
833 pub fn remove(self) -> V {
834 self.entry.remove()
835 }
836}
837
838/// A RAII guard, which is a view into a vacant entry in a [Table].
839///
840/// It is part of the [TableEntry] enum.
841///
842/// An API of this object is similar to the HashMap's [VacantEntry] API.
843///
844/// The guard keeps the corresponding Table shard locked for write until
845/// the guard is dropped.
846// Safety: Entries order reflects guards drop semantics.
847pub struct TableVacantEntry<'a, K: 'a, V: 'a, S = RandomState> {
848 entry: VacantEntry<'a, K, V>,
849 guard: RwLockWriteGuard<'a, HashMap<K, V, S>>,
850}
851
852impl<K: Debug, V: Debug, S> Debug for TableVacantEntry<'_, K, V, S> {
853 #[inline(always)]
854 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
855 Debug::fmt(&self.entry, formatter)
856 }
857}
858
859impl<'a, K: 'a, V: 'a, S> TableVacantEntry<'a, K, V, S> {
860 /// Returns a reference to the key that would be used when inserting a value
861 /// through the TableVacantEntry.
862 ///
863 /// This function is similar to the [VacantEntry::key] function.
864 #[inline(always)]
865 pub fn key(&self) -> &K {
866 self.entry.key()
867 }
868
869 /// Takes ownership of the key.
870 ///
871 /// This function is similar to the [VacantEntry::into_key] function.
872 #[inline(always)]
873 pub fn into_key(self) -> K {
874 self.entry.into_key()
875 }
876
877 /// Sets the value of the entry with the VacantEntry’s key, and returns
878 /// a [TableWriteGuard] RAII guard that grants read-write access to
879 /// the entry value.
880 ///
881 /// This function is similar to the [VacantEntry::insert] function.
882 #[inline(always)]
883 pub fn insert(self, value: V) -> TableWriteGuard<'a, K, V, S> {
884 let value = self.entry.insert(value);
885
886 TableWriteGuard {
887 value,
888 _guard: self.guard,
889 }
890 }
891}
892
893/// A draining iterator over the entries of a [Table].
894///
895/// Created by the [Table::drain] method.
896///
897/// This object behaves similarly to the HashMap's [Drain] iterator.
898pub struct TableDrain<'a, K: 'a, V: 'a, S = RandomState> {
899 probes: Vec<ProbeDrain<'a, K, V, S>>,
900 table: &'a Table<K, V, S>,
901}
902
903impl<'a, K, V, S> Drop for TableDrain<'a, K, V, S> {
904 fn drop(&mut self) {
905 loop {
906 let index = self.probes.len();
907
908 let mut guard = match self.table.shards.get(index) {
909 Some(lock) => lock.write().unwrap_or_else(|poison| poison.into_inner()),
910 None => break,
911 };
912
913 guard.clear();
914
915 self.probes.push(ProbeDrain {
916 drain: None,
917 _guard: guard,
918 });
919 }
920 }
921}
922
923impl<'a, K, V, S> FusedIterator for TableDrain<'a, K, V, S> {}
924
925impl<'a, K, V, S> Iterator for TableDrain<'a, K, V, S> {
926 type Item = (K, V);
927
928 fn next(&mut self) -> Option<Self::Item> {
929 loop {
930 if let Some(probe) = self.probes.last_mut() {
931 if let Some(drain) = &mut probe.drain {
932 if let Some(key_value) = drain.next() {
933 return Some(key_value);
934 }
935 }
936 }
937
938 let index = self.probes.len();
939
940 let mut guard = self
941 .table
942 .shards
943 .get(index)?
944 .write()
945 .unwrap_or_else(|poison| poison.into_inner());
946
947 let drain = guard.drain();
948
949 // Safety:
950 // Prolongs reference lifetime to `self` lifetime.
951 // The value will be valid for as long as the guard is held.
952 let drain = unsafe { transmute::<Drain<'_, K, V>, Drain<'_, K, V>>(drain) };
953
954 self.probes.push(ProbeDrain {
955 drain: Some(drain),
956 _guard: guard,
957 });
958 }
959 }
960}
961
962/// An owning iterator over the entries of a [Table].
963pub struct TableIntoIter<K, V, S = RandomState> {
964 probe: hash_map::IntoIter<K, V>,
965 shards: vec::IntoIter<RwLock<HashMap<K, V, S>>>,
966}
967
968impl<K, V, S> FusedIterator for TableIntoIter<K, V, S> {}
969
970impl<K, V, S> Iterator for TableIntoIter<K, V, S> {
971 type Item = (K, V);
972
973 fn next(&mut self) -> Option<Self::Item> {
974 loop {
975 if let Some(next) = self.probe.next() {
976 return Some(next);
977 }
978
979 self.probe = self
980 .shards
981 .next()?
982 .into_inner()
983 .unwrap_or_else(|poison| poison.into_inner())
984 .into_iter();
985 }
986 }
987}
988
989// Safety: Entries order reflects guards drop semantics.
990struct ProbeDrain<'a, K: 'a, V: 'a, S> {
991 drain: Option<Drain<'a, K, V>>,
992 _guard: RwLockWriteGuard<'a, HashMap<K, V, S>>,
993}
994
995#[inline(always)]
996fn shard_of<'a, K, V, S, Q>(table: &'a Table<K, V, S>, key: &Q) -> &'a RwLock<HashMap<K, V, S>>
997where
998 K: Hash + Eq + Borrow<Q>,
999 Q: Hash + Eq + ?Sized,
1000 S: BuildHasher,
1001{
1002 let shard_index = table.shard_index_of(key);
1003
1004 match table.shards.get(shard_index) {
1005 Some(shard) => shard,
1006
1007 // Safety:
1008 // 1. `shard_index_of` always returns a shard index
1009 // within a `shards` length.
1010 // 2. `shards` is never empty.
1011 None => unsafe { ld_unreachable!("Table shard index out of bounds.") },
1012 }
1013}
1014
1015#[inline(always)]
1016fn shards_amount() -> usize {
1017 #[cfg(not(target_family = "wasm"))]
1018 {
1019 std::thread::available_parallelism()
1020 .map_or(1usize, |parallelism| 4 * usize::from(parallelism))
1021 .next_power_of_two()
1022 }
1023
1024 #[cfg(target_family = "wasm")]
1025 {
1026 1
1027 }
1028}