[go: up one dir, main page]

cht/
map.rs

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