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}