Skip to main content

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}