moka_cht/segment/map.rs
1//! A lock-free hash map implemented with segmented bucket pointer arrays, open
2//! addressing, and linear probing.
3
4use crate::map::{
5 bucket::{self, BucketArray},
6 bucket_array_ref::BucketArrayRef,
7 DefaultHashBuilder,
8};
9
10use std::{
11 borrow::Borrow,
12 hash::{BuildHasher, Hash},
13 ptr,
14 sync::atomic::{self, AtomicUsize, Ordering},
15};
16
17use crossbeam_epoch::Atomic;
18
19/// A lock-free hash map implemented with segmented bucket pointer arrays, open
20/// addressing, and linear probing.
21///
22/// This struct is re-exported as `moka_cht::SegmentedHashMap`.
23///
24/// # Examples
25///
26/// ```rust
27/// use moka_cht::SegmentedHashMap;
28/// use std::{sync::Arc, thread};
29///
30/// let map = Arc::new(SegmentedHashMap::with_num_segments(4));
31///
32/// let threads: Vec<_> = (0..16)
33/// .map(|i| {
34/// let map = map.clone();
35///
36/// thread::spawn(move || {
37/// const NUM_INSERTIONS: usize = 64;
38///
39/// for j in (i * NUM_INSERTIONS)..((i + 1) * NUM_INSERTIONS) {
40/// map.insert_and(j, j, |_prev| unreachable!());
41/// }
42/// })
43/// })
44/// .collect();
45///
46/// let _: Vec<_> = threads.into_iter().map(|t| t.join()).collect();
47/// ```
48///
49/// The number of segments can be specified on a per-`HashMap` basis using the
50/// following methods:
51///
52/// - [`with_num_segments`]
53/// - [`with_num_segments_and_capacity`]
54/// - [`with_num_segments_and_hasher`]
55/// - [`with_num_segments_capacity_and_hasher`]
56///
57/// By default, the `num-cpus` feature is enabled so the following methods will be
58/// available:
59///
60/// - [`new`]
61/// - [`with_capacity`]
62/// - [`with_hasher`]
63/// - [`with_capacity_and_hasher`]
64///
65/// They will create maps with twice as many segments as the system has CPUs.
66///
67/// # Hashing Algorithm
68///
69/// By default, `HashMap` uses a hashing algorithm selected to provide resistance
70/// against HashDoS attacks. It will the same one used by
71/// `std::collections::HashMap`, which is currently SipHash 1-3.
72///
73/// While SipHash's performance is very competitive for medium sized keys, other
74/// hashing algorithms will outperform it for small keys such as integers as well as
75/// large keys such as long strings. However those algorithms will typically not
76/// protect against attacks such as HashDoS.
77///
78/// The hashing algorithm can be replaced on a per-`HashMap` basis using one of the
79/// following methods:
80///
81/// - [`default`]
82/// - [`with_hasher`]
83/// - [`with_capacity_and_hasher`]
84/// - [`with_num_segments_and_hasher`]
85/// - [`with_num_segments_capacity_and_hasher`]
86///
87/// Many alternative algorithms are available on crates.io, such as the [`aHash`]
88/// crate.
89///
90/// It is required that the keys implement the [`Eq`] and [`Hash`] traits,
91/// although this can frequently be achieved by using
92/// `#[derive(PartialEq, Eq, Hash)]`. If you implement these yourself, it is
93/// important that the following property holds:
94///
95/// ```text
96/// k1 == k2 -> hash(k1) == hash(k2)
97/// ```
98///
99/// In other words, if two keys are equal, their hashes must be equal.
100///
101/// It is a logic error for a key to be modified in such a way that the key's
102/// hash, as determined by the [`Hash`] trait, or its equality, as determined by
103/// the [`Eq`] trait, changes while it is in the map. This is normally only
104/// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code.
105///
106/// [`aHash`]: https://crates.io/crates/ahash
107/// [`default`]: #method.default
108/// [`with_hasher`]: #method.with_hasher
109/// [`with_capacity`]: #method.with_capacity
110/// [`with_capacity_and_hasher`]: #method.with_capacity_and_hasher
111/// [`with_num_segments_and_hasher`]: #method.with_num_segments_and_hasher
112/// [`with_num_segments_capacity_and_hasher`]: #method.with_num_segments_capacity_and_hasher
113/// [`with_num_segments`]: #method.with_num_segments
114/// [`with_num_segments_and_capacity`]: #method.with_num_segments_and_capacity
115/// [`new`]: #method.new
116/// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
117/// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
118/// [`Cell`]: https://doc.rust-lang.org/std/cell/struct.Ref.html
119/// [`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
120pub struct HashMap<K, V, S = DefaultHashBuilder> {
121 segments: Box<[Segment<K, V>]>,
122 build_hasher: S,
123 len: AtomicUsize,
124 segment_shift: u32,
125}
126
127#[cfg(feature = "num-cpus")]
128impl<K, V> HashMap<K, V, DefaultHashBuilder> {
129 /// Creates an empty `HashMap`.
130 ///
131 /// The hash map is initially created with a capacity of 0, so it will not
132 /// allocate bucket pointer arrays until it is first inserted into. However,
133 /// it will always allocate memory for segment pointers and lengths.
134 ///
135 /// The `HashMap` will be created with at least twice as many segments as
136 /// the system has CPUs.
137 pub fn new() -> Self {
138 Self::with_num_segments_capacity_and_hasher(
139 default_num_segments(),
140 0,
141 DefaultHashBuilder::default(),
142 )
143 }
144
145 /// Creates an empty `HashMap` with the specified capacity.
146 ///
147 /// The hash map will be able to hold at least `capacity` elements without
148 /// reallocating any bucket pointer arrays. If `capacity` is 0, the hash map
149 /// will not allocate any bucket pointer arrays. However, it will always
150 /// allocate memory for segment pointers and lengths.
151 ///
152 /// The `HashMap` will be created with at least twice as many segments as
153 /// the system has CPUs.
154 pub fn with_capacity(capacity: usize) -> Self {
155 Self::with_num_segments_capacity_and_hasher(
156 default_num_segments(),
157 capacity,
158 DefaultHashBuilder::default(),
159 )
160 }
161}
162
163#[cfg(feature = "num-cpus")]
164impl<K, V, S: BuildHasher> HashMap<K, V, S> {
165 /// Creates an empty `HashMap` which will use the given hash builder to hash
166 /// keys.
167 ///
168 /// The hash map is initially created with a capacity of 0, so it will not
169 /// allocate bucket pointer arrays until it is first inserted into. However,
170 /// it will always allocate memory for segment pointers and lengths.
171 ///
172 /// The `HashMap` will be created with at least twice as many segments as
173 /// the system has CPUs.
174 pub fn with_hasher(build_hasher: S) -> Self {
175 Self::with_num_segments_capacity_and_hasher(default_num_segments(), 0, build_hasher)
176 }
177
178 /// Creates an empty `HashMap` with the specified capacity, using
179 /// `build_hasher` to hash the keys.
180 ///
181 /// The hash map will be able to hold at least `capacity` elements without
182 /// reallocating any bucket pointer arrays. If `capacity` is 0, the hash map
183 /// will not allocate any bucket pointer arrays. However, it will always
184 /// allocate memory for segment pointers and lengths.
185 ///
186 /// The `HashMap` will be created with at least twice as many segments as
187 /// the system has CPUs.
188 pub fn with_capacity_and_hasher(capacity: usize, build_hasher: S) -> Self {
189 Self::with_num_segments_capacity_and_hasher(default_num_segments(), capacity, build_hasher)
190 }
191}
192
193impl<K, V> HashMap<K, V, DefaultHashBuilder> {
194 /// Creates an empty `HashMap` with the specified number of segments.
195 ///
196 /// The hash map is initially created with a capacity of 0, so it will not
197 /// allocate bucket pointer arrays until it is first inserted into. However,
198 /// it will always allocate memory for segment pointers and lengths.
199 ///
200 /// # Panics
201 ///
202 /// Panics if `num_segments` is 0.
203 pub fn with_num_segments(num_segments: usize) -> Self {
204 Self::with_num_segments_capacity_and_hasher(num_segments, 0, DefaultHashBuilder::default())
205 }
206
207 /// Creates an empty `HashMap` with the specified number of segments and
208 /// capacity.
209 ///
210 /// The hash map will be able to hold at least `capacity` elements without
211 /// reallocating any bucket pointer arrays. If `capacity` is 0, the hash map
212 /// will not allocate any bucket pointer arrays. However, it will always
213 /// allocate memory for segment pointers and lengths.
214 ///
215 /// # Panics
216 ///
217 /// Panics if `num_segments` is 0.
218 pub fn with_num_segments_and_capacity(num_segments: usize, capacity: usize) -> Self {
219 Self::with_num_segments_capacity_and_hasher(
220 num_segments,
221 capacity,
222 DefaultHashBuilder::default(),
223 )
224 }
225}
226
227impl<K, V, S> HashMap<K, V, S> {
228 /// Creates an empty `HashMap` with the specified number of segments, using
229 /// `build_hasher` to hash the keys.
230 ///
231 /// The hash map is initially created with a capacity of 0, so it will not
232 /// allocate bucket pointer arrays until it is first inserted into. However,
233 /// it will always allocate memory for segment pointers and lengths.
234 ///
235 /// # Panics
236 ///
237 /// Panics if `num_segments` is 0.
238 pub fn with_num_segments_and_hasher(num_segments: usize, build_hasher: S) -> Self {
239 Self::with_num_segments_capacity_and_hasher(num_segments, 0, build_hasher)
240 }
241
242 /// Creates an empty `HashMap` with the specified number of segments and
243 /// capacity, using `build_hasher` to hash the keys.
244 ///
245 /// The hash map will be able to hold at least `capacity` elements without
246 /// reallocating any bucket pointer arrays. If `capacity` is 0, the hash map
247 /// will not allocate any bucket pointer arrays. However, it will always
248 /// allocate memory for segment pointers and lengths.
249 ///
250 /// # Panics
251 ///
252 /// Panics if `num_segments` is 0.
253 pub fn with_num_segments_capacity_and_hasher(
254 num_segments: usize,
255 capacity: usize,
256 build_hasher: S,
257 ) -> Self {
258 assert!(num_segments > 0);
259
260 let actual_num_segments = num_segments.next_power_of_two();
261 let segment_shift = 64 - actual_num_segments.trailing_zeros();
262
263 let mut segments = Vec::with_capacity(actual_num_segments);
264
265 if capacity == 0 {
266 unsafe {
267 ptr::write_bytes(segments.as_mut_ptr(), 0, actual_num_segments);
268 segments.set_len(actual_num_segments);
269 }
270 } else {
271 let actual_capacity = (capacity * 2).next_power_of_two();
272
273 for _ in 0..actual_num_segments {
274 segments.push(Segment {
275 bucket_array: Atomic::new(BucketArray::with_length(0, actual_capacity)),
276 len: AtomicUsize::new(0),
277 });
278 }
279 }
280
281 let segments = segments.into_boxed_slice();
282
283 Self {
284 segments,
285 build_hasher,
286 len: AtomicUsize::new(0),
287 segment_shift,
288 }
289 }
290
291 /// Returns the number of elements in the map.
292 ///
293 /// # Safety
294 ///
295 /// This method on its own is safe, but other threads can add or remove
296 /// elements at any time.
297 pub fn len(&self) -> usize {
298 self.len.load(Ordering::Relaxed)
299 }
300
301 /// Returns `true` if the map contains no elements.
302 ///
303 /// # Safety
304 ///
305 /// This method on its own is safe, but other threads can add or remove
306 /// elements at any time.
307 pub fn is_empty(&self) -> bool {
308 self.len() == 0
309 }
310
311 /// Returns the number of elements the map can hold without reallocating any
312 /// bucket pointer arrays.
313 ///
314 /// Note that all mutating operations except removal will result in a bucket
315 /// being allocated or reallocated.
316 ///
317 /// # Safety
318 ///
319 /// This method on its own is safe, but other threads can increase the
320 /// capacity of each segment at any time by adding elements.
321 pub fn capacity(&self) -> usize {
322 let guard = &crossbeam_epoch::pin();
323
324 self.segments
325 .iter()
326 .map(|s| s.bucket_array.load_consume(guard))
327 .map(|p| unsafe { p.as_ref() })
328 .map(|a| a.map(BucketArray::capacity).unwrap_or(0))
329 .min()
330 .unwrap()
331 }
332
333 /// Returns the number of elements the `index`-th segment of the map can
334 /// hold without reallocating a bucket pointer array.
335 ///
336 /// Note that all mutating operations, with the exception of removing
337 /// elements, will result in an allocation for a new bucket.
338 ///
339 /// # Safety
340 ///
341 /// This method on its own is safe, but other threads can increase the
342 /// capacity of a segment at any time by adding elements.
343 pub fn segment_capacity(&self, index: usize) -> usize {
344 assert!(index < self.segments.len());
345
346 let guard = &crossbeam_epoch::pin();
347
348 unsafe {
349 self.segments[index]
350 .bucket_array
351 .load_consume(guard)
352 .as_ref()
353 }
354 .map(BucketArray::capacity)
355 .unwrap_or(0)
356 }
357
358 /// Returns the number of segments in the map.
359 pub fn num_segments(&self) -> usize {
360 self.segments.len()
361 }
362}
363
364impl<K, V, S: BuildHasher> HashMap<K, V, S> {
365 /// Returns the index of the segment that `key` would belong to if inserted
366 /// into the map.
367 pub fn segment_index<Q: Hash>(&self, key: &Q) -> usize
368 where
369 K: Borrow<Q>,
370 {
371 let hash = bucket::hash(&self.build_hasher, key);
372
373 self.segment_index_from_hash(hash)
374 }
375}
376
377impl<K: Hash + Eq, V, S: BuildHasher> HashMap<K, V, S> {
378 /// Returns a clone of the value corresponding to the key.
379 ///
380 /// The key may be any borrowed form of the map's key type, but
381 /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
382 /// the key type.
383 ///
384 /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
385 /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
386 #[inline]
387 pub fn get<Q: Hash + Eq + ?Sized>(&self, key: &Q) -> Option<V>
388 where
389 K: Borrow<Q>,
390 V: Clone,
391 {
392 self.get_key_value_and(key, |_, v| v.clone())
393 }
394
395 /// Returns a clone of the the key-value pair corresponding to the supplied
396 /// key.
397 ///
398 /// The supplied key may be any borrowed form of the map's key type, but
399 /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for the key
400 /// type.
401 ///
402 /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
403 /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
404 #[inline]
405 pub fn get_key_value<Q: Hash + Eq + ?Sized>(&self, key: &Q) -> Option<(K, V)>
406 where
407 K: Borrow<Q> + Clone,
408 V: Clone,
409 {
410 self.get_key_value_and(key, |k, v| (k.clone(), v.clone()))
411 }
412
413 /// Returns the result of invoking a function with a reference to the value
414 /// corresponding to the key.
415 ///
416 /// The key may be any borrowed form of the map's key type, but
417 /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
418 /// the key type.
419 ///
420 /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
421 /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
422 #[inline]
423 pub fn get_and<Q: Hash + Eq + ?Sized, F: FnOnce(&V) -> T, T>(
424 &self,
425 key: &Q,
426 with_value: F,
427 ) -> Option<T>
428 where
429 K: Borrow<Q>,
430 {
431 self.get_key_value_and(key, move |_, v| with_value(v))
432 }
433
434 /// Returns the result of invoking a function with a reference to the
435 /// key-value pair corresponding to the supplied key.
436 ///
437 /// The supplied key may be any borrowed form of the map's key type, but
438 /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for the key
439 /// type.
440 ///
441 /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
442 /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
443 #[inline]
444 pub fn get_key_value_and<Q: Hash + Eq + ?Sized, F: FnOnce(&K, &V) -> T, T>(
445 &self,
446 key: &Q,
447 with_entry: F,
448 ) -> Option<T>
449 where
450 K: Borrow<Q>,
451 {
452 let hash = bucket::hash(&self.build_hasher, &key);
453
454 self.bucket_array_ref(hash)
455 .get_key_value_and(key, hash, with_entry)
456 }
457
458 /// Inserts a key-value pair into the map, returning a clone of the value
459 /// previously corresponding to the key.
460 ///
461 /// If the map did have this key present, both the key and value are
462 /// updated.
463 #[inline]
464 pub fn insert(&self, key: K, value: V) -> Option<V>
465 where
466 V: Clone,
467 {
468 self.insert_entry_and(key, value, |_, v| v.clone())
469 }
470
471 /// Inserts a key-value pair into the map, returning a clone of the
472 /// key-value pair previously corresponding to the supplied key.
473 ///
474 /// If the map did have this key present, both the key and value are
475 /// updated.
476 #[inline]
477 pub fn insert_entry(&self, key: K, value: V) -> Option<(K, V)>
478 where
479 K: Clone,
480 V: Clone,
481 {
482 self.insert_entry_and(key, value, |k, v| (k.clone(), v.clone()))
483 }
484
485 /// Inserts a key-value pair into the map, returning the result of invoking
486 /// a function with a reference to the value previously corresponding to the
487 /// key.
488 ///
489 /// If the map did have this key present, both the key and value are
490 /// updated.
491 #[inline]
492 pub fn insert_and<F: FnOnce(&V) -> T, T>(
493 &self,
494 key: K,
495 value: V,
496 with_previous_value: F,
497 ) -> Option<T> {
498 self.insert_entry_and(key, value, move |_, v| with_previous_value(v))
499 }
500
501 /// Inserts a key-value pair into the map, returning the result of invoking
502 /// a function with a reference to the key-value pair previously
503 /// corresponding to the supplied key.
504 ///
505 /// If the map did have this key present, both the key and value are
506 /// updated.
507 #[inline]
508 pub fn insert_entry_and<F: FnOnce(&K, &V) -> T, T>(
509 &self,
510 key: K,
511 value: V,
512 with_previous_entry: F,
513 ) -> Option<T> {
514 let hash = bucket::hash(&self.build_hasher, &key);
515
516 let result =
517 self.bucket_array_ref(hash)
518 .insert_entry_and(key, hash, value, with_previous_entry);
519
520 if result.is_none() {
521 self.len.fetch_add(1, Ordering::Relaxed);
522 }
523
524 result
525 }
526
527 /// Removes a key from the map, returning a clone of the value previously
528 /// corresponding to the key.
529 ///
530 /// The key may be any borrowed form of the map's key type, but
531 /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
532 /// the key type.
533 ///
534 /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
535 /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
536 #[inline]
537 pub fn remove<Q: Hash + Eq + ?Sized>(&self, key: &Q) -> Option<V>
538 where
539 K: Borrow<Q>,
540 V: Clone,
541 {
542 self.remove_entry_if_and(key, |_, _| true, |_, v| v.clone())
543 }
544
545 /// Removes a key from the map, returning a clone of the key-value pair
546 /// previously corresponding to the key.
547 ///
548 /// The key may be any borrowed form of the map's key type, but
549 /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
550 /// the key type.
551 ///
552 /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
553 /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
554 #[inline]
555 pub fn remove_entry<Q: Hash + Eq + ?Sized>(&self, key: &Q) -> Option<(K, V)>
556 where
557 K: Borrow<Q> + Clone,
558 V: Clone,
559 {
560 self.remove_entry_if_and(key, |_, _| true, |k, v| (k.clone(), v.clone()))
561 }
562
563 /// Remove a key from the map, returning the result of invoking a function
564 /// with a reference to the value previously corresponding to the key.
565 ///
566 /// The key may be any borrowed form of the map's key type, but
567 /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
568 /// the key type.
569 ///
570 /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
571 /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
572 #[inline]
573 pub fn remove_and<Q: Hash + Eq + ?Sized, F: FnOnce(&V) -> T, T>(
574 &self,
575 key: &Q,
576 with_previous_value: F,
577 ) -> Option<T>
578 where
579 K: Borrow<Q>,
580 {
581 self.remove_entry_if_and(key, |_, _| true, move |_, v| with_previous_value(v))
582 }
583
584 /// Removes a key from the map, returning the result of invoking a function
585 /// with a reference to the key-value pair previously corresponding to the
586 /// key.
587 ///
588 /// The key may be any borrowed form of the map's key type, but
589 /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
590 /// the key type.
591 ///
592 /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
593 /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
594 #[inline]
595 pub fn remove_entry_and<Q: Hash + Eq + ?Sized, F: FnOnce(&K, &V) -> T, T>(
596 &self,
597 key: &Q,
598 with_previous_entry: F,
599 ) -> Option<T>
600 where
601 K: Borrow<Q>,
602 {
603 self.remove_entry_if_and(key, |_, _| true, with_previous_entry)
604 }
605
606 /// Removes a key from the map if a condition is met, returning a clone of
607 /// the value previously corresponding to the key.
608 ///
609 /// `condition` will be invoked at least once if [`Some`] is returned. It
610 /// may also be invoked one or more times if [`None`] is returned.
611 ///
612 /// The key may be any borrowed form of the map's key type, but
613 /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
614 /// the key type.
615 ///
616 /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
617 /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
618 /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
619 /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
620 pub fn remove_if<Q: Hash + Eq + ?Sized, F: FnMut(&K, &V) -> bool>(
621 &self,
622 key: &Q,
623 condition: F,
624 ) -> Option<V>
625 where
626 K: Borrow<Q>,
627 V: Clone,
628 {
629 self.remove_entry_if_and(key, condition, move |_, v| v.clone())
630 }
631
632 /// Removes a key from the map if a condition is met, returning a clone of
633 /// the key-value pair previously corresponding to the key.
634 ///
635 /// `condition` will be invoked at least once if [`Some`] is returned. It
636 /// may also be invoked one or more times if [`None`] is returned.
637 ///
638 /// The key may be any borrowed form of the map's key type, but
639 /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
640 /// the key type.
641 ///
642 /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
643 /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
644 /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
645 /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
646 #[inline]
647 pub fn remove_entry_if<Q: Hash + Eq + ?Sized, F: FnMut(&K, &V) -> bool>(
648 &self,
649 key: &Q,
650 condition: F,
651 ) -> Option<(K, V)>
652 where
653 K: Clone + Borrow<Q>,
654 V: Clone,
655 {
656 self.remove_entry_if_and(key, condition, move |k, v| (k.clone(), v.clone()))
657 }
658
659 /// Remove a key from the map if a condition is met, returning the result of
660 /// invoking a function with a reference to the value previously
661 /// corresponding to the key.
662 ///
663 /// `condition` will be invoked at least once if [`Some`] is returned. It
664 /// may also be invoked one or more times if [`None`] is returned.
665 ///
666 /// The key may be any borrowed form of the map's key type, but
667 /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
668 /// the key type.
669 ///
670 /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
671 /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
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 remove_if_and<Q: Hash + Eq + ?Sized, F: FnMut(&K, &V) -> bool, G: FnOnce(&V) -> T, T>(
676 &self,
677 key: &Q,
678 condition: F,
679 with_previous_value: G,
680 ) -> Option<T>
681 where
682 K: Borrow<Q>,
683 {
684 self.remove_entry_if_and(key, condition, move |_, v| with_previous_value(v))
685 }
686
687 /// Removes a key from the map if a condition is met, returning the result
688 /// of invoking a function with a reference to the key-value pair previously
689 /// corresponding to the key.
690 ///
691 /// `condition` will be invoked at least once if [`Some`] is returned. It
692 /// may also be invoked one or more times if [`None`] is returned.
693 ///
694 /// The key may be any borrowed form of the map's key type, but
695 /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
696 /// the key type.
697 ///
698 /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
699 /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
700 /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
701 /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
702 #[inline]
703 pub fn remove_entry_if_and<
704 Q: Hash + Eq + ?Sized,
705 F: FnMut(&K, &V) -> bool,
706 G: FnOnce(&K, &V) -> T,
707 T,
708 >(
709 &self,
710 key: &Q,
711 condition: F,
712 with_previous_entry: G,
713 ) -> Option<T>
714 where
715 K: Borrow<Q>,
716 {
717 let hash = bucket::hash(&self.build_hasher, &key);
718
719 self.bucket_array_ref(hash)
720 .remove_entry_if_and(key, hash, condition, move |k, v| {
721 self.len.fetch_sub(1, Ordering::Relaxed);
722
723 with_previous_entry(k, v)
724 })
725 }
726
727 /// If no value corresponds to the key, insert a new key-value pair into
728 /// the map. Otherwise, modify the existing value and return a clone of the
729 /// value previously corresponding to the key.
730 ///
731 /// `on_modify` will be invoked at least once if [`Some`] is returned. It
732 /// may also be invoked one or more times if [`None`] is returned.
733 ///
734 /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
735 /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
736 #[inline]
737 pub fn insert_or_modify<F: FnMut(&K, &V) -> V>(
738 &self,
739 key: K,
740 value: V,
741 on_modify: F,
742 ) -> Option<V>
743 where
744 V: Clone,
745 {
746 self.insert_with_or_modify_entry_and(key, move || value, on_modify, |_, v| v.clone())
747 }
748
749 /// If no value corresponds to the key, insert a new key-value pair into
750 /// the map. Otherwise, modify the existing value and return a clone of the
751 /// key-value pair previously corresponding to the key.
752 ///
753 /// `on_modify` will be invoked at least once if [`Some`] is returned. It
754 /// may also be invoked one or more times if [`None`] is returned.
755 ///
756 /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
757 /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
758 #[inline]
759 pub fn insert_or_modify_entry<F: FnMut(&K, &V) -> V>(
760 &self,
761 key: K,
762 value: V,
763 on_modify: F,
764 ) -> Option<(K, V)>
765 where
766 K: Clone,
767 V: Clone,
768 {
769 self.insert_with_or_modify_entry_and(
770 key,
771 move || value,
772 on_modify,
773 |k, v| (k.clone(), v.clone()),
774 )
775 }
776
777 /// If no value corresponds to the key, invoke a default function to insert
778 /// a new key-value pair into the map. Otherwise, modify the existing value
779 /// and return a clone of the value previously corresponding to the key.
780 ///
781 /// `on_insert` may be invoked, even if [`None`] is returned.
782 ///
783 /// `on_modify` will be invoked at least once if [`Some`] is returned. It
784 /// may also be invoked one or more times if [`None`] is returned.
785 ///
786 /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
787 /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
788 #[inline]
789 pub fn insert_with_or_modify<F: FnOnce() -> V, G: FnMut(&K, &V) -> V>(
790 &self,
791 key: K,
792 on_insert: F,
793 on_modify: G,
794 ) -> Option<V>
795 where
796 V: Clone,
797 {
798 self.insert_with_or_modify_entry_and(key, on_insert, on_modify, |_, v| v.clone())
799 }
800
801 /// If no value corresponds to the key, invoke a default function to insert
802 /// a new key-value pair into the map. Otherwise, modify the existing value
803 /// and return a clone of the key-value pair previously corresponding to the
804 /// key.
805 ///
806 /// `on_insert` may be invoked, even if [`None`] is returned.
807 ///
808 /// `on_modify` will be invoked at least once if [`Some`] is returned. It
809 /// may also be invoked one or more times if [`None`] is returned.
810 ///
811 /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
812 /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
813 #[inline]
814 pub fn insert_with_or_modify_entry<F: FnOnce() -> V, G: FnMut(&K, &V) -> V>(
815 &self,
816 key: K,
817 on_insert: F,
818 on_modify: G,
819 ) -> Option<(K, V)>
820 where
821 K: Clone,
822 V: Clone,
823 {
824 self.insert_with_or_modify_entry_and(key, on_insert, on_modify, |k, v| {
825 (k.clone(), v.clone())
826 })
827 }
828
829 /// If no value corresponds to the key, insert a new key-value pair into
830 /// the map. Otherwise, modify the existing value and return the result of
831 /// invoking a function with a reference to the value previously
832 /// corresponding to the key.
833 ///
834 /// `on_modify` will be invoked at least once if [`Some`] is returned. It
835 /// may also be invoked one or more times if [`None`] is returned.
836 ///
837 /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
838 /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
839 #[inline]
840 pub fn insert_or_modify_and<F: FnMut(&K, &V) -> V, G: FnOnce(&V) -> T, T>(
841 &self,
842 key: K,
843 value: V,
844 on_modify: F,
845 with_old_value: G,
846 ) -> Option<T> {
847 self.insert_with_or_modify_entry_and(
848 key,
849 move || value,
850 on_modify,
851 move |_, v| with_old_value(v),
852 )
853 }
854
855 /// If no value corresponds to the key, insert a new key-value pair into
856 /// the map. Otherwise, modify the existing value and return the result of
857 /// invoking a function with a reference to the key-value pair previously
858 /// corresponding to the supplied key.
859 ///
860 /// `on_modify` will be invoked at least once if [`Some`] is returned. It
861 /// may also be invoked one or more times if [`None`] is returned.
862 ///
863 /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
864 /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
865 #[inline]
866 pub fn insert_or_modify_entry_and<F: FnMut(&K, &V) -> V, G: FnOnce(&K, &V) -> T, T>(
867 &self,
868 key: K,
869 value: V,
870 on_modify: F,
871 with_old_entry: G,
872 ) -> Option<T> {
873 self.insert_with_or_modify_entry_and(key, move || value, on_modify, with_old_entry)
874 }
875
876 /// If no value corresponds to the key, invoke a default function to insert
877 /// a new key-value pair into the map. Otherwise, modify the existing value
878 /// and return the result of invoking a function with a reference to the
879 /// value previously corresponding to the key.
880 ///
881 /// `on_insert` may be invoked, even if [`None`] is returned.
882 ///
883 /// `on_modify` will be invoked at least once if [`Some`] is returned. It
884 /// may also be invoked one or more times if [`None`] is returned.
885 ///
886 /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
887 /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
888 #[inline]
889 pub fn insert_with_or_modify_and<
890 F: FnOnce() -> V,
891 G: FnMut(&K, &V) -> V,
892 H: FnOnce(&V) -> T,
893 T,
894 >(
895 &self,
896 key: K,
897 on_insert: F,
898 on_modify: G,
899 with_old_value: H,
900 ) -> Option<T> {
901 self.insert_with_or_modify_entry_and(key, on_insert, on_modify, move |_, v| {
902 with_old_value(v)
903 })
904 }
905
906 /// If no value corresponds to the key, invoke a default function to insert
907 /// a new key-value pair into the map. Otherwise, modify the existing value
908 /// and return the result of invoking a function with a reference to the
909 /// key-value pair previously corresponding to the supplied key.
910 ///
911 /// `on_insert` may be invoked, even if [`None`] is returned.
912 ///
913 /// `on_modify` will be invoked at least once if [`Some`] is returned. It
914 /// may also be invoked one or more times if [`None`] is returned.
915 ///
916 /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
917 /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
918 #[inline]
919 pub fn insert_with_or_modify_entry_and<
920 F: FnOnce() -> V,
921 G: FnMut(&K, &V) -> V,
922 H: FnOnce(&K, &V) -> T,
923 T,
924 >(
925 &self,
926 key: K,
927 on_insert: F,
928 on_modify: G,
929 with_old_entry: H,
930 ) -> Option<T> {
931 let hash = bucket::hash(&self.build_hasher, &key);
932
933 let result = self.bucket_array_ref(hash).insert_with_or_modify_entry_and(
934 key,
935 hash,
936 on_insert,
937 on_modify,
938 with_old_entry,
939 );
940
941 if result.is_none() {
942 self.len.fetch_add(1, Ordering::Relaxed);
943 }
944
945 result
946 }
947
948 /// Modifies the value corresponding to a key, returning a clone of the
949 /// value previously corresponding to that key.
950 #[inline]
951 pub fn modify<F: FnMut(&K, &V) -> V>(&self, key: K, on_modify: F) -> Option<V>
952 where
953 V: Clone,
954 {
955 self.modify_entry_and(key, on_modify, |_, v| v.clone())
956 }
957
958 /// Modifies the value corresponding to a key, returning a clone of the
959 /// key-value pair previously corresponding to that key.
960 #[inline]
961 pub fn modify_entry<F: FnMut(&K, &V) -> V>(&self, key: K, on_modify: F) -> Option<(K, V)>
962 where
963 K: Clone,
964 V: Clone,
965 {
966 self.modify_entry_and(key, on_modify, |k, v| (k.clone(), v.clone()))
967 }
968
969 /// Modifies the value corresponding to a key, returning the result of
970 /// invoking a function with a reference to the value previously
971 /// corresponding to the key.
972 #[inline]
973 pub fn modify_and<F: FnMut(&K, &V) -> V, G: FnOnce(&V) -> T, T>(
974 &self,
975 key: K,
976 on_modify: F,
977 with_old_value: G,
978 ) -> Option<T> {
979 self.modify_entry_and(key, on_modify, move |_, v| with_old_value(v))
980 }
981
982 /// Modifies the value corresponding to a key, returning the result of
983 /// invoking a function with a reference to the key-value pair previously
984 /// corresponding to the supplied key.
985 #[inline]
986 pub fn modify_entry_and<F: FnMut(&K, &V) -> V, G: FnOnce(&K, &V) -> T, T>(
987 &self,
988 key: K,
989 on_modify: F,
990 with_old_entry: G,
991 ) -> Option<T> {
992 let hash = bucket::hash(&self.build_hasher, &key);
993
994 self.bucket_array_ref(hash)
995 .modify_entry_and(key, hash, on_modify, with_old_entry)
996 }
997}
998
999#[cfg(feature = "num-cpus")]
1000impl<K, V, S: Default> Default for HashMap<K, V, S> {
1001 fn default() -> Self {
1002 HashMap::with_num_segments_capacity_and_hasher(default_num_segments(), 0, S::default())
1003 }
1004}
1005
1006impl<K, V, S> Drop for HashMap<K, V, S> {
1007 fn drop(&mut self) {
1008 let guard = unsafe { &crossbeam_epoch::unprotected() };
1009 atomic::fence(Ordering::Acquire);
1010
1011 for Segment {
1012 bucket_array: this_bucket_array,
1013 ..
1014 } in self.segments.iter()
1015 {
1016 let mut current_ptr = this_bucket_array.load(Ordering::Relaxed, guard);
1017
1018 while let Some(current_ref) = unsafe { current_ptr.as_ref() } {
1019 let next_ptr = current_ref.next.load(Ordering::Relaxed, guard);
1020
1021 for this_bucket_ptr in current_ref
1022 .buckets
1023 .iter()
1024 .map(|b| b.load(Ordering::Relaxed, guard))
1025 .filter(|p| !p.is_null())
1026 .filter(|p| next_ptr.is_null() || p.tag() & bucket::TOMBSTONE_TAG == 0)
1027 {
1028 // only delete tombstones from the newest bucket array
1029 // the only way this becomes a memory leak is if there was a panic during a rehash,
1030 // in which case i'm going to say that running destructors and freeing memory is
1031 // best-effort, and my best effort is to not do it
1032 unsafe { bucket::defer_acquire_destroy(guard, this_bucket_ptr) };
1033 }
1034
1035 unsafe { bucket::defer_acquire_destroy(guard, current_ptr) };
1036
1037 current_ptr = next_ptr;
1038 }
1039 }
1040 }
1041}
1042
1043impl<K, V, S> HashMap<K, V, S> {
1044 #[inline]
1045 fn bucket_array_ref(&'_ self, hash: u64) -> BucketArrayRef<'_, K, V, S> {
1046 let index = self.segment_index_from_hash(hash);
1047
1048 let Segment {
1049 ref bucket_array,
1050 ref len,
1051 } = self.segments[index];
1052
1053 BucketArrayRef {
1054 bucket_array,
1055 build_hasher: &self.build_hasher,
1056 len,
1057 }
1058 }
1059
1060 #[inline]
1061 fn segment_index_from_hash(&'_ self, hash: u64) -> usize {
1062 if self.segment_shift == 64 {
1063 0
1064 } else {
1065 (hash >> self.segment_shift) as usize
1066 }
1067 }
1068}
1069
1070struct Segment<K, V> {
1071 bucket_array: Atomic<BucketArray<K, V>>,
1072 len: AtomicUsize,
1073}
1074
1075#[cfg(feature = "num-cpus")]
1076fn default_num_segments() -> usize {
1077 num_cpus::get() * 2
1078}
1079
1080#[cfg(test)]
1081mod tests {
1082 use crate::write_test_cases_for_me;
1083
1084 use super::*;
1085
1086 write_test_cases_for_me!(HashMap);
1087
1088 #[test]
1089 fn single_segment() {
1090 let map = HashMap::with_num_segments(1);
1091
1092 assert!(map.is_empty());
1093 assert_eq!(map.len(), 0);
1094
1095 assert_eq!(map.insert("foo", 5), None);
1096 assert_eq!(map.get("foo"), Some(5));
1097
1098 assert!(!map.is_empty());
1099 assert_eq!(map.len(), 1);
1100
1101 assert_eq!(map.remove("foo"), Some(5));
1102 assert!(map.is_empty());
1103 assert_eq!(map.len(), 0);
1104 }
1105}