moka_cht/
map.rs

1//! A lock-free hash map implemented with bucket pointer arrays, open addressing, and
2//! linear probing.
3
4pub(crate) mod bucket;
5pub(crate) mod bucket_array_ref;
6
7use bucket::BucketArray;
8use bucket_array_ref::BucketArrayRef;
9
10use std::{
11    borrow::Borrow,
12    collections::hash_map::RandomState,
13    hash::{BuildHasher, Hash},
14    sync::atomic::{self, AtomicUsize, Ordering},
15};
16
17use crossbeam_epoch::{self, Atomic};
18
19/// Default hasher for `HashMap`.
20pub type DefaultHashBuilder = RandomState;
21
22/// A lock-free hash map implemented with bucket pointer arrays, open addressing, and
23/// linear probing.
24///
25/// This struct is re-exported as `moka_cht::HashMap`.
26///
27/// # Examples
28///
29/// ```rust
30/// use moka_cht::HashMap;
31/// use std::{sync::Arc, thread};
32///
33/// let map = Arc::new(HashMap::new());
34///
35/// let threads: Vec<_> = (0..16)
36///     .map(|i| {
37///         let map = map.clone();
38///
39///         thread::spawn(move || {
40///             const NUM_INSERTIONS: usize = 64;
41///
42///             for j in (i * NUM_INSERTIONS)..((i + 1) * NUM_INSERTIONS) {
43///                 map.insert_and(j, j, |_prev| unreachable!());
44///             }
45///         })
46///     })
47///     .collect();
48///
49/// let _: Vec<_> = threads.into_iter().map(|t| t.join()).collect();
50/// ```
51///
52/// # Hashing Algorithm
53///
54/// By default, `HashMap` uses a hashing algorithm selected to provide resistance
55/// against HashDoS attacks. It will be the same one used by
56/// `std::collections::HashMap`, which is currently SipHash 1-3.
57///
58/// While SipHash's performance is very competitive for medium sized keys, other
59/// hashing algorithms will outperform it for small keys such as integers as well as
60/// large keys such as long strings. However those algorithms will typically not
61/// protect against attacks such as HashDoS.
62///
63/// The hashing algorithm can be replaced on a per-`HashMap` basis using one of the
64/// following methods:
65///
66/// - [`default`]
67/// - [`with_hasher`]
68/// - [`with_capacity_and_hasher`]
69///
70/// Many alternative algorithms are available on crates.io, such as the [`aHash`]
71/// crate.
72///
73/// It is required that the keys implement the [`Eq`] and [`Hash`] traits, although
74/// this can frequently be achieved by using `#[derive(PartialEq, Eq, Hash)]`. If you
75/// implement these yourself, it is important that the following property holds:
76///
77/// ```text
78/// k1 == k2 -> hash(k1) == hash(k2)
79/// ```
80///
81/// In other words, if two keys are equal, their hashes must be equal.
82///
83/// It is a logic error for a key to be modified in such a way that the key's hash,
84/// as determined by the [`Hash`] trait, or its equality, as determined by the [`Eq`]
85/// trait, changes while it is in the map. This is normally only possible through
86/// [`Cell`], [`RefCell`], global state, I/O, or unsafe code.
87///
88/// [`aHash`]: https://crates.io/crates/ahash
89/// [`default`]: #method.default
90/// [`with_hasher`]: #method.with_hasher
91/// [`with_capacity_and_hasher`]: #method.with_capacity_and_hasher
92/// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
93/// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
94/// [`Cell`]: https://doc.rust-lang.org/std/cell/struct.Ref.html
95/// [`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
96///
97#[derive(Default)]
98pub struct HashMap<K, V, S = DefaultHashBuilder> {
99    bucket_array: Atomic<bucket::BucketArray<K, V>>,
100    build_hasher: S,
101    len: AtomicUsize,
102}
103
104impl<K, V> HashMap<K, V, DefaultHashBuilder> {
105    /// Creates an empty `HashMap`.
106    ///
107    /// The hash map is initially created with a capacity of 0, so it will not
108    /// allocate a bucket pointer array until it is first inserted into.
109    pub fn new() -> HashMap<K, V, DefaultHashBuilder> {
110        HashMap::with_capacity_and_hasher(0, DefaultHashBuilder::default())
111    }
112
113    /// Creates an empty `HashMap` with the specified capacity.
114    ///
115    /// The hash map will be able to hold at least `capacity` elements without
116    /// reallocating its bucket pointer array. If `capacity` is 0, the hash map
117    /// will not allocate.
118    pub fn with_capacity(capacity: usize) -> HashMap<K, V, DefaultHashBuilder> {
119        HashMap::with_capacity_and_hasher(capacity, DefaultHashBuilder::default())
120    }
121}
122
123impl<K, V, S> HashMap<K, V, S> {
124    /// Creates an empty `HashMap` which will use the given hash builder to hash
125    /// keys.
126    ///
127    /// The hash map is initially created with a capacity of 0, so it will not
128    /// allocate a bucket pointer array until it is first inserted into.
129    pub fn with_hasher(build_hasher: S) -> HashMap<K, V, S> {
130        HashMap::with_capacity_and_hasher(0, build_hasher)
131    }
132
133    /// Creates an empty `HashMap` with the specified capacity, using
134    /// `build_hasher` to hash the keys.
135    ///
136    /// The hash map will be able to hold at least `capacity` elements without
137    /// reallocating its bucket pointer array. If `capacity` is 0, the hash map
138    /// will not allocate.
139    pub fn with_capacity_and_hasher(capacity: usize, build_hasher: S) -> HashMap<K, V, S> {
140        let bucket_array = if capacity == 0 {
141            Atomic::null()
142        } else {
143            Atomic::new(BucketArray::with_length(
144                0,
145                (capacity * 2).next_power_of_two(),
146            ))
147        };
148
149        Self {
150            bucket_array,
151            build_hasher,
152            len: AtomicUsize::new(0),
153        }
154    }
155
156    /// Returns the number of elements in the map.
157    ///
158    /// # Safety
159    ///
160    /// This method on its own is safe, but other threads can add or remove
161    /// elements at any time.
162    pub fn len(&self) -> usize {
163        self.len.load(Ordering::Relaxed)
164    }
165
166    /// Returns `true` if the map contains no elements.
167    ///
168    /// # Safety
169    ///
170    /// This method on its own is safe, but other threads can add or remove
171    /// elements at any time.
172    pub fn is_empty(&self) -> bool {
173        self.len() == 0
174    }
175
176    /// Returns the number of elements the map can hold without reallocating its
177    /// bucket pointer array.
178    ///
179    /// Note that all mutating operations except removal will result in a bucket
180    /// being allocated or reallocated.
181    ///
182    /// # Safety
183    ///
184    /// This method on its own is safe, but other threads can increase the
185    /// capacity at any time by adding elements.
186    pub fn capacity(&self) -> usize {
187        let guard = &crossbeam_epoch::pin();
188
189        let bucket_array_ptr = self.bucket_array.load_consume(guard);
190
191        unsafe { bucket_array_ptr.as_ref() }
192            .map(BucketArray::capacity)
193            .unwrap_or(0)
194    }
195}
196
197impl<K: Hash + Eq, V, S: BuildHasher> HashMap<K, V, S> {
198    /// Returns a clone of the value corresponding to the key.
199    ///
200    /// The key may be any borrowed form of the map's key type, but
201    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
202    /// the key type.
203    ///
204    /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
205    /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
206    #[inline]
207    pub fn get<Q: Hash + Eq + ?Sized>(&self, key: &Q) -> Option<V>
208    where
209        K: Borrow<Q>,
210        V: Clone,
211    {
212        self.get_key_value_and(key, |_, v| v.clone())
213    }
214
215    /// Returns a clone of the the key-value pair corresponding to the supplied
216    /// key.
217    ///
218    /// The supplied key may be any borrowed form of the map's key type, but
219    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for the key
220    /// type.
221    ///
222    /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
223    /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
224    #[inline]
225    pub fn get_key_value<Q: Hash + Eq + ?Sized>(&self, key: &Q) -> Option<(K, V)>
226    where
227        K: Borrow<Q> + Clone,
228        V: Clone,
229    {
230        self.get_key_value_and(key, |k, v| (k.clone(), v.clone()))
231    }
232
233    /// Returns the result of invoking a function with a reference to the value
234    /// corresponding to the key.
235    ///
236    /// The key may be any borrowed form of the map's key type, but
237    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
238    /// the key type.
239    ///
240    /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
241    /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
242    #[inline]
243    pub fn get_and<Q: Hash + Eq + ?Sized, F: FnOnce(&V) -> T, T>(
244        &self,
245        key: &Q,
246        with_value: F,
247    ) -> Option<T>
248    where
249        K: Borrow<Q>,
250    {
251        self.get_key_value_and(key, move |_, v| with_value(v))
252    }
253
254    /// Returns the result of invoking a function with a reference to the
255    /// key-value pair corresponding to the supplied key.
256    ///
257    /// The supplied key may be any borrowed form of the map's key type, but
258    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for the key
259    /// type.
260    ///
261    /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
262    /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
263    #[inline]
264    pub fn get_key_value_and<Q: Hash + Eq + ?Sized, F: FnOnce(&K, &V) -> T, T>(
265        &self,
266        key: &Q,
267        with_entry: F,
268    ) -> Option<T>
269    where
270        K: Borrow<Q>,
271    {
272        let hash = bucket::hash(&self.build_hasher, &key);
273
274        self.bucket_array_ref()
275            .get_key_value_and(key, hash, with_entry)
276    }
277
278    /// Inserts a key-value pair into the map, returning a clone of the value
279    /// previously corresponding to the key.
280    ///
281    /// If the map did have this key present, both the key and value are
282    /// updated.
283    #[inline]
284    pub fn insert(&self, key: K, value: V) -> Option<V>
285    where
286        V: Clone,
287    {
288        self.insert_entry_and(key, value, |_, v| v.clone())
289    }
290
291    /// Inserts a key-value pair into the map, returning a clone of the
292    /// key-value pair previously corresponding to the supplied key.
293    ///
294    /// If the map did have this key present, both the key and value are
295    /// updated.
296    #[inline]
297    pub fn insert_entry(&self, key: K, value: V) -> Option<(K, V)>
298    where
299        K: Clone,
300        V: Clone,
301    {
302        self.insert_entry_and(key, value, |k, v| (k.clone(), v.clone()))
303    }
304
305    /// Inserts a key-value pair into the map, returning the result of invoking
306    /// a function with a reference to the value previously corresponding to the
307    /// key.
308    ///
309    /// If the map did have this key present, both the key and value are
310    /// updated.
311    #[inline]
312    pub fn insert_and<F: FnOnce(&V) -> T, T>(
313        &self,
314        key: K,
315        value: V,
316        with_previous_value: F,
317    ) -> Option<T> {
318        self.insert_entry_and(key, value, move |_, v| with_previous_value(v))
319    }
320
321    /// Inserts a key-value pair into the map, returning the result of invoking
322    /// a function with a reference to the key-value pair previously
323    /// corresponding to the supplied key.
324    ///
325    /// If the map did have this key present, both the key and value are
326    /// updated.
327    #[inline]
328    pub fn insert_entry_and<F: FnOnce(&K, &V) -> T, T>(
329        &self,
330        key: K,
331        value: V,
332        with_previous_entry: F,
333    ) -> Option<T> {
334        let hash = bucket::hash(&self.build_hasher, &key);
335
336        self.bucket_array_ref()
337            .insert_entry_and(key, hash, value, with_previous_entry)
338    }
339
340    /// Removes a key from the map, returning a clone of the value previously
341    /// corresponding to the key.
342    ///
343    /// The key may be any borrowed form of the map's key type, but
344    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
345    /// the key type.
346    ///
347    /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
348    /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
349    #[inline]
350    pub fn remove<Q: Hash + Eq + ?Sized>(&self, key: &Q) -> Option<V>
351    where
352        K: Borrow<Q>,
353        V: Clone,
354    {
355        self.remove_entry_if_and(key, |_, _| true, |_, v| v.clone())
356    }
357
358    /// Removes a key from the map, returning a clone of the key-value pair
359    /// previously corresponding to the key.
360    ///
361    /// The key may be any borrowed form of the map's key type, but
362    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
363    /// the key type.
364    ///
365    /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
366    /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
367    #[inline]
368    pub fn remove_entry<Q: Hash + Eq + ?Sized>(&self, key: &Q) -> Option<(K, V)>
369    where
370        K: Borrow<Q> + Clone,
371        V: Clone,
372    {
373        self.remove_entry_if_and(key, |_, _| true, |k, v| (k.clone(), v.clone()))
374    }
375
376    /// Remove a key from the map, returning the result of invoking a function
377    /// with a reference to the value previously corresponding to the key.
378    ///
379    /// The key may be any borrowed form of the map's key type, but
380    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
381    /// the key type.
382    ///
383    /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
384    /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
385    #[inline]
386    pub fn remove_and<Q: Hash + Eq + ?Sized, F: FnOnce(&V) -> T, T>(
387        &self,
388        key: &Q,
389        with_previous_value: F,
390    ) -> Option<T>
391    where
392        K: Borrow<Q>,
393    {
394        self.remove_entry_if_and(key, |_, _| true, move |_, v| with_previous_value(v))
395    }
396
397    /// Removes a key from the map, returning the result of invoking a function
398    /// with a reference to the key-value pair previously corresponding to the
399    /// key.
400    ///
401    /// The key may be any borrowed form of the map's key type, but
402    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
403    /// the key type.
404    ///
405    /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
406    /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
407    #[inline]
408    pub fn remove_entry_and<Q: Hash + Eq + ?Sized, F: FnOnce(&K, &V) -> T, T>(
409        &self,
410        key: &Q,
411        with_previous_entry: F,
412    ) -> Option<T>
413    where
414        K: Borrow<Q>,
415    {
416        self.remove_entry_if_and(key, |_, _| true, with_previous_entry)
417    }
418
419    /// Removes a key from the map if a condition is met, returning a clone of
420    /// the value previously corresponding to the key.
421    ///
422    /// `condition` will be invoked at least once if [`Some`] is returned. It
423    /// may also be invoked one or more times if [`None`] is returned.
424    ///
425    /// The key may be any borrowed form of the map's key type, but
426    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
427    /// the key type.
428    ///
429    /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
430    /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
431    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
432    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
433    pub fn remove_if<Q: Hash + Eq + ?Sized, F: FnMut(&K, &V) -> bool>(
434        &self,
435        key: &Q,
436        condition: F,
437    ) -> Option<V>
438    where
439        K: Borrow<Q>,
440        V: Clone,
441    {
442        self.remove_entry_if_and(key, condition, move |_, v| v.clone())
443    }
444
445    /// Removes a key from the map if a condition is met, returning a clone of
446    /// the key-value pair previously corresponding to the key.
447    ///
448    /// `condition` will be invoked at least once if [`Some`] is returned. It
449    /// may also be invoked one or more times if [`None`] is returned.
450    ///
451    /// The key may be any borrowed form of the map's key type, but
452    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
453    /// the key type.
454    ///
455    /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
456    /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
457    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
458    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
459    #[inline]
460    pub fn remove_entry_if<Q: Hash + Eq + ?Sized, F: FnMut(&K, &V) -> bool>(
461        &self,
462        key: &Q,
463        condition: F,
464    ) -> Option<(K, V)>
465    where
466        K: Clone + Borrow<Q>,
467        V: Clone,
468    {
469        self.remove_entry_if_and(key, condition, move |k, v| (k.clone(), v.clone()))
470    }
471
472    /// Remove a key from the map if a condition is met, returning the result of
473    /// invoking a function with a reference to the value previously
474    /// corresponding to the key.
475    ///
476    /// `condition` will be invoked at least once if [`Some`] is returned. It
477    /// may also be invoked one or more times if [`None`] is returned.
478    ///
479    /// The key may be any borrowed form of the map's key type, but
480    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
481    /// the key type.
482    ///
483    /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
484    /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
485    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
486    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
487    #[inline]
488    pub fn remove_if_and<Q: Hash + Eq + ?Sized, F: FnMut(&K, &V) -> bool, G: FnOnce(&V) -> T, T>(
489        &self,
490        key: &Q,
491        condition: F,
492        with_previous_value: G,
493    ) -> Option<T>
494    where
495        K: Borrow<Q>,
496    {
497        self.remove_entry_if_and(key, condition, move |_, v| with_previous_value(v))
498    }
499
500    /// Removes a key from the map if a condition is met, returning the result
501    /// of invoking a function with a reference to the key-value pair previously
502    /// corresponding to the key.
503    ///
504    /// `condition` will be invoked at least once if [`Some`] is returned. It
505    /// may also be invoked one or more times if [`None`] is returned.
506    ///
507    /// The key may be any borrowed form of the map's key type, but
508    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
509    /// the key type.
510    ///
511    /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
512    /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
513    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
514    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
515    #[inline]
516    pub fn remove_entry_if_and<
517        Q: Hash + Eq + ?Sized,
518        F: FnMut(&K, &V) -> bool,
519        G: FnOnce(&K, &V) -> T,
520        T,
521    >(
522        &self,
523        key: &Q,
524        condition: F,
525        with_previous_entry: G,
526    ) -> Option<T>
527    where
528        K: Borrow<Q>,
529    {
530        let hash = bucket::hash(&self.build_hasher, &key);
531
532        self.bucket_array_ref()
533            .remove_entry_if_and(key, hash, condition, with_previous_entry)
534    }
535
536    /// If no value corresponds to the key, insert a new key-value pair into
537    /// the map. Otherwise, modify the existing value and return a clone of the
538    /// value previously corresponding to the key.
539    ///
540    /// `on_modify` will be invoked at least once if [`Some`] is returned. It
541    /// may also be invoked one or more times if [`None`] is returned.
542    ///
543    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
544    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
545    #[inline]
546    pub fn insert_or_modify<F: FnMut(&K, &V) -> V>(
547        &self,
548        key: K,
549        value: V,
550        on_modify: F,
551    ) -> Option<V>
552    where
553        V: Clone,
554    {
555        self.insert_with_or_modify_entry_and(key, move || value, on_modify, |_, v| v.clone())
556    }
557
558    /// If no value corresponds to the key, insert a new key-value pair into
559    /// the map. Otherwise, modify the existing value and return a clone of the
560    /// key-value pair previously corresponding to the key.
561    ///
562    /// `on_modify` will be invoked at least once if [`Some`] is returned. It
563    /// may also be invoked one or more times if [`None`] is returned.
564    ///
565    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
566    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
567    #[inline]
568    pub fn insert_or_modify_entry<F: FnMut(&K, &V) -> V>(
569        &self,
570        key: K,
571        value: V,
572        on_modify: F,
573    ) -> Option<(K, V)>
574    where
575        K: Clone,
576        V: Clone,
577    {
578        self.insert_with_or_modify_entry_and(
579            key,
580            move || value,
581            on_modify,
582            |k, v| (k.clone(), v.clone()),
583        )
584    }
585
586    /// If no value corresponds to the key, invoke a default function to insert
587    /// a new key-value pair into the map. Otherwise, modify the existing value
588    /// and return a clone of the value previously corresponding to the key.
589    ///
590    /// `on_insert` may be invoked, even if [`None`] is returned.
591    ///
592    /// `on_modify` will be invoked at least once if [`Some`] is returned. It
593    /// may also be invoked one or more times if [`None`] is returned.
594    ///
595    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
596    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
597    #[inline]
598    pub fn insert_with_or_modify<F: FnOnce() -> V, G: FnMut(&K, &V) -> V>(
599        &self,
600        key: K,
601        on_insert: F,
602        on_modify: G,
603    ) -> Option<V>
604    where
605        V: Clone,
606    {
607        self.insert_with_or_modify_entry_and(key, on_insert, on_modify, |_, v| v.clone())
608    }
609
610    /// If no value corresponds to the key, invoke a default function to insert
611    /// a new key-value pair into the map. Otherwise, modify the existing value
612    /// and return a clone of the key-value pair previously corresponding to the
613    /// key.
614    ///
615    /// `on_insert` may be invoked, even if [`None`] is returned.
616    ///
617    /// `on_modify` will be invoked at least once if [`Some`] is returned. It
618    /// may also be invoked one or more times if [`None`] is returned.
619    ///
620    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
621    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
622    #[inline]
623    pub fn insert_with_or_modify_entry<F: FnOnce() -> V, G: FnMut(&K, &V) -> V>(
624        &self,
625        key: K,
626        on_insert: F,
627        on_modify: G,
628    ) -> Option<(K, V)>
629    where
630        K: Clone,
631        V: Clone,
632    {
633        self.insert_with_or_modify_entry_and(key, on_insert, on_modify, |k, v| {
634            (k.clone(), v.clone())
635        })
636    }
637
638    /// If no value corresponds to the key, insert a new key-value pair into
639    /// the map. Otherwise, modify the existing value and return the result of
640    /// invoking a function with a reference to the value previously
641    /// corresponding to the key.
642    ///
643    /// `on_modify` will be invoked at least once if [`Some`] is returned. It
644    /// may also be invoked one or more times if [`None`] is returned.
645    ///
646    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
647    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
648    #[inline]
649    pub fn insert_or_modify_and<F: FnMut(&K, &V) -> V, G: FnOnce(&V) -> T, T>(
650        &self,
651        key: K,
652        value: V,
653        on_modify: F,
654        with_old_value: G,
655    ) -> Option<T> {
656        self.insert_with_or_modify_entry_and(
657            key,
658            move || value,
659            on_modify,
660            move |_, v| with_old_value(v),
661        )
662    }
663
664    /// If no value corresponds to the key, insert a new key-value pair into
665    /// the map. Otherwise, modify the existing value and return the result of
666    /// invoking a function with a reference to the key-value pair previously
667    /// corresponding to the supplied key.
668    ///
669    /// `on_modify` will be invoked at least once if [`Some`] is returned. It
670    /// may also be invoked one or more times if [`None`] is returned.
671    ///
672    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
673    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
674    #[inline]
675    pub fn insert_or_modify_entry_and<F: FnMut(&K, &V) -> V, G: FnOnce(&K, &V) -> T, T>(
676        &self,
677        key: K,
678        value: V,
679        on_modify: F,
680        with_old_entry: G,
681    ) -> Option<T> {
682        self.insert_with_or_modify_entry_and(key, move || value, on_modify, with_old_entry)
683    }
684
685    /// If no value corresponds to the key, invoke a default function to insert
686    /// a new key-value pair into the map. Otherwise, modify the existing value
687    /// and return the result of invoking a function with a reference to the
688    /// value previously corresponding to the key.
689    ///
690    /// `on_insert` may be invoked, even if [`None`] is returned.
691    ///
692    /// `on_modify` will be invoked at least once if [`Some`] is returned. It
693    /// may also be invoked one or more times if [`None`] is returned.
694    ///
695    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
696    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
697    #[inline]
698    pub fn insert_with_or_modify_and<
699        F: FnOnce() -> V,
700        G: FnMut(&K, &V) -> V,
701        H: FnOnce(&V) -> T,
702        T,
703    >(
704        &self,
705        key: K,
706        on_insert: F,
707        on_modify: G,
708        with_old_value: H,
709    ) -> Option<T> {
710        self.insert_with_or_modify_entry_and(key, on_insert, on_modify, move |_, v| {
711            with_old_value(v)
712        })
713    }
714
715    /// If no value corresponds to the key, invoke a default function to insert
716    /// a new key-value pair into the map. Otherwise, modify the existing value
717    /// and return the result of invoking a function with a reference to the
718    /// key-value pair previously corresponding to the supplied key.
719    ///
720    /// `on_insert` may be invoked, even if [`None`] is returned.
721    ///
722    /// `on_modify` will be invoked at least once if [`Some`] is returned. It
723    /// may also be invoked one or more times if [`None`] is returned.
724    ///
725    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
726    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
727    #[inline]
728    pub fn insert_with_or_modify_entry_and<
729        F: FnOnce() -> V,
730        G: FnMut(&K, &V) -> V,
731        H: FnOnce(&K, &V) -> T,
732        T,
733    >(
734        &self,
735        key: K,
736        on_insert: F,
737        on_modify: G,
738        with_old_entry: H,
739    ) -> Option<T> {
740        let hash = bucket::hash(&self.build_hasher, &key);
741
742        self.bucket_array_ref().insert_with_or_modify_entry_and(
743            key,
744            hash,
745            on_insert,
746            on_modify,
747            with_old_entry,
748        )
749    }
750
751    /// Modifies the value corresponding to a key, returning a clone of the
752    /// value previously corresponding to that key.
753    #[inline]
754    pub fn modify<F: FnMut(&K, &V) -> V>(&self, key: K, on_modify: F) -> Option<V>
755    where
756        V: Clone,
757    {
758        self.modify_entry_and(key, on_modify, |_, v| v.clone())
759    }
760
761    /// Modifies the value corresponding to a key, returning a clone of the
762    /// key-value pair previously corresponding to that key.
763    #[inline]
764    pub fn modify_entry<F: FnMut(&K, &V) -> V>(&self, key: K, on_modify: F) -> Option<(K, V)>
765    where
766        K: Clone,
767        V: Clone,
768    {
769        self.modify_entry_and(key, on_modify, |k, v| (k.clone(), v.clone()))
770    }
771
772    /// Modifies the value corresponding to a key, returning the result of
773    /// invoking a function with a reference to the value previously
774    /// corresponding to the key.
775    #[inline]
776    pub fn modify_and<F: FnMut(&K, &V) -> V, G: FnOnce(&V) -> T, T>(
777        &self,
778        key: K,
779        on_modify: F,
780        with_old_value: G,
781    ) -> Option<T> {
782        self.modify_entry_and(key, on_modify, move |_, v| with_old_value(v))
783    }
784
785    /// Modifies the value corresponding to a key, returning the result of
786    /// invoking a function with a reference to the key-value pair previously
787    /// corresponding to the supplied key.
788    #[inline]
789    pub fn modify_entry_and<F: FnMut(&K, &V) -> V, G: FnOnce(&K, &V) -> T, T>(
790        &self,
791        key: K,
792        on_modify: F,
793        with_old_entry: G,
794    ) -> Option<T> {
795        let hash = bucket::hash(&self.build_hasher, &key);
796
797        self.bucket_array_ref()
798            .modify_entry_and(key, hash, on_modify, with_old_entry)
799    }
800}
801
802impl<K, V, S> HashMap<K, V, S> {
803    #[inline]
804    fn bucket_array_ref(&'_ self) -> BucketArrayRef<'_, K, V, S> {
805        BucketArrayRef {
806            bucket_array: &self.bucket_array,
807            build_hasher: &self.build_hasher,
808            len: &self.len,
809        }
810    }
811}
812
813impl<K, V, S> Drop for HashMap<K, V, S> {
814    fn drop(&mut self) {
815        let guard = unsafe { &crossbeam_epoch::unprotected() };
816        atomic::fence(Ordering::Acquire);
817
818        let mut current_ptr = self.bucket_array.load(Ordering::Relaxed, guard);
819
820        while let Some(current_ref) = unsafe { current_ptr.as_ref() } {
821            let next_ptr = current_ref.next.load(Ordering::Relaxed, guard);
822
823            for this_bucket_ptr in current_ref
824                .buckets
825                .iter()
826                .map(|b| b.load(Ordering::Relaxed, guard))
827                .filter(|p| !p.is_null())
828                .filter(|p| next_ptr.is_null() || p.tag() & bucket::TOMBSTONE_TAG == 0)
829            {
830                // only delete tombstones from the newest bucket array
831                // the only way this becomes a memory leak is if there was a panic during a rehash,
832                // in which case i'm going to say that running destructors and freeing memory is
833                // best-effort, and my best effort is to not do it
834                unsafe { bucket::defer_acquire_destroy(guard, this_bucket_ptr) };
835            }
836
837            unsafe { bucket::defer_acquire_destroy(guard, current_ptr) };
838
839            current_ptr = next_ptr;
840        }
841    }
842}
843
844#[cfg(test)]
845mod tests {
846    use crate::write_test_cases_for_me;
847
848    use super::*;
849
850    write_test_cases_for_me!(HashMap);
851}