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}