Skip to main content

kovan_map/
hashmap.rs

1//! High-Performance Lock-Free Concurrent Hash Map (FoldHash + resizable table).
2//!
3//! # Strategy
4//!
5//! 1. **FoldHash**: `foldhash::fast::FixedState` for fast, quality hashing.
6//! 2. **Resizable bucket table**: the bucket array lives in a single
7//!    allocation (header + inline buckets) swapped atomically on resize and
8//!    reclaimed through kovan. The map grows when the load factor exceeds
9//!    3/4 and shrinks below 1/4 (never under its initial capacity),
10//!    mirroring `HopscotchMap`'s resize protocol.
11//! 3. **Optimized Node Layout**: fields ordered `hash -> key -> value -> next`
12//!    to optimize cache line usage during checks.
13//!
14//! # Architecture
15//! - **Table**: kovan-retired object holding the bucket array (atomic head
16//!   pointers). Readers snapshot the table under a guard and never block.
17//! - **Nodes**: singly linked chains, CAS-based lock-free insert/remove.
18//! - **Resize**: a single resizer (CAS on `resizing`) clones all entries
19//!   into a new table, swaps the table pointer, and retires the old table;
20//!   the old table's destructor frees its remaining chains exactly once at
21//!   reclamation time. Writers wait out an active resize and re-validate
22//!   after success so no update is lost to a concurrent migration.
23
24extern crate alloc;
25
26#[cfg(feature = "std")]
27extern crate std;
28
29use alloc::boxed::Box;
30use core::borrow::Borrow;
31use core::hash::{BuildHasher, Hash};
32use core::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
33use foldhash::fast::FixedState;
34use kovan::{Atomic, RetiredNode, Shared, pin, retire};
35
36/// Default number of buckets for `new()`. Matches the previous fixed-table
37/// sizing (zero collisions for ~100k items, fits in L3); maps created with
38/// `new()` keep exactly the old memory/performance profile and additionally
39/// grow past it on demand. Use [`HashMap::with_capacity`] for small elastic
40/// maps.
41const DEFAULT_CAPACITY: usize = 524_288;
42
43/// Minimum number of buckets; the map never shrinks below this.
44const MIN_CAPACITY: usize = 64;
45
46// Load-factor thresholds (implemented as integer comparisons on the hot
47// paths): grow when count/capacity > 3/4, shrink when count/capacity < 1/4
48// (never below the map's floor capacity). Same thresholds as HopscotchMap.
49
50/// A simple exponential backoff for reducing contention.
51struct Backoff {
52    step: u32,
53}
54
55impl Backoff {
56    #[inline(always)]
57    fn new() -> Self {
58        Self { step: 0 }
59    }
60
61    #[inline(always)]
62    fn spin(&mut self) {
63        for _ in 0..(1 << self.step.min(6)) {
64            core::hint::spin_loop();
65        }
66        if self.step <= 6 {
67            self.step += 1;
68        }
69    }
70}
71
72/// Node in the lock-free linked list.
73#[repr(C)]
74struct Node<K, V> {
75    retired: RetiredNode,
76    hash: u64,
77    key: K,
78    value: V,
79    next: Atomic<Node<K, V>>,
80}
81
82// SAFETY (kovan retirement rule): a retired Node's destructor may run on
83// any thread, and nodes (with K and V inside) move between threads — hence
84// `K: Send, V: Send` for Send. Unlike exclusive-transfer containers,
85// lookups DO produce `&K`/`&V` from a shared `&Node` (get() clones V
86// through &V under concurrent readers), so Sync additionally requires
87// `K: Sync, V: Sync` — the same bounds the map-level Sync impl below has
88// always required for sharing the map.
89unsafe impl<K: Send, V: Send> Send for Node<K, V> {}
90unsafe impl<K: Send + Sync, V: Send + Sync> Sync for Node<K, V> {}
91
92// ---------------------------------------------------------------------------
93// Harris-style logical deletion
94// ---------------------------------------------------------------------------
95//
96// Removing (or replacing) a node first TAGS the victim's `next` pointer
97// (low bit set) — the logical delete — and only then unlinks it from its
98// predecessor. The thread whose tag-CAS succeeded exclusively owns the node
99// and is the only one to `retire()` it. This closes two races a plain
100// unlink-CAS protocol has:
101//
102//  * insert-after-removed-tail: a tail insert CASes `tail.next: null -> new`;
103//    if the tail was concurrently unlinked and retired, the new node is
104//    spliced onto dead memory — the insert is lost and the node leaks.
105//    With tagging, the remover first turns the tail's `next` into
106//    tagged-null, so the insert's CAS (expecting untagged null) fails.
107//
108//  * adjacent removes: removing B (A->B->C) and C (B->C->D) concurrently
109//    can unlink C from the already-detached B while C is still reachable
110//    through A, retiring a reachable node (use-after-free for later
111//    readers). With tagging, C's remover owns C via the tag; walkers
112//    observe `B.next` tagged and never operate relative to deleted nodes.
113//
114// Invariants:
115//  * tags appear only on `Node.next` fields, never on bucket heads
116//    (snipping stores the untagged successor);
117//  * a node whose `next` is tagged has been retired by its tag owner —
118//    `clear()`, the migration sweep, and `Table::drop` must skip it;
119//  * every traversal untags before following a `next` pointer.
120
121#[inline(always)]
122fn tagged<K, V>(p: *mut Node<K, V>) -> *mut Node<K, V> {
123    (p as usize | 1) as *mut Node<K, V>
124}
125
126#[inline(always)]
127fn untag<K, V>(p: *mut Node<K, V>) -> *mut Node<K, V> {
128    (p as usize & !1) as *mut Node<K, V>
129}
130
131#[inline(always)]
132fn is_tagged<K, V>(p: *const Node<K, V>) -> bool {
133    (p as usize) & 1 != 0
134}
135
136// ---------------------------------------------------------------------------
137// Single-allocation table: [TableHeader][Atomic<Node>; capacity]
138// ---------------------------------------------------------------------------
139//
140// The header and the bucket array share one allocation, so the read path is
141// `table ptr -> header line (mask, hot in cache) -> bucket line` — the same
142// number of cold dereferences as a fixed embedded array. The table pointer
143// itself is swapped atomically on resize.
144//
145// Reclamation goes through a tiny boxed `TableProxy` (RetiredNode at offset
146// 0, as `retire()` requires): retiring the proxy defers until every guard
147// that could observe the old table has been released; the proxy's destructor
148// then frees the table's remaining chains and the allocation itself.
149
150#[repr(C)]
151struct TableHeader {
152    mask: usize,
153    capacity: usize,
154}
155
156/// Borrowed view of a table allocation.
157struct TableRef<K: 'static, V: 'static> {
158    header: *mut TableHeader,
159    _marker: core::marker::PhantomData<(K, V)>,
160}
161
162impl<K, V> Clone for TableRef<K, V> {
163    fn clone(&self) -> Self {
164        *self
165    }
166}
167impl<K, V> Copy for TableRef<K, V> {}
168
169impl<K: 'static, V: 'static> TableRef<K, V> {
170    #[inline(always)]
171    fn from_raw(header: *mut TableHeader) -> Self {
172        Self {
173            header,
174            _marker: core::marker::PhantomData,
175        }
176    }
177
178    fn layout(capacity: usize) -> (core::alloc::Layout, usize) {
179        let header = core::alloc::Layout::new::<TableHeader>();
180        let buckets = core::alloc::Layout::array::<Atomic<Node<K, V>>>(capacity)
181            .expect("bucket array layout overflow");
182        let (layout, offset) = header.extend(buckets).expect("table layout overflow");
183        (layout.pad_to_align(), offset)
184    }
185
186    /// Allocate a zero-initialized table (`Atomic` buckets zero == null).
187    fn alloc(capacity: usize) -> Self {
188        let capacity = capacity.next_power_of_two().max(MIN_CAPACITY);
189        let (layout, _) = Self::layout(capacity);
190        // SAFETY: layout is non-zero sized; zeroed AtomicUsize == null bucket.
191        let header = unsafe { alloc::alloc::alloc_zeroed(layout) as *mut TableHeader };
192        assert!(!header.is_null(), "table allocation failed");
193        unsafe {
194            (*header).mask = capacity - 1;
195            (*header).capacity = capacity;
196        }
197        Self::from_raw(header)
198    }
199
200    /// Free remaining chains (skipping tagged nodes — already retired by
201    /// their tag owners) and the allocation itself.
202    ///
203    /// Free only the table allocation, not the chains (caller already drained
204    /// the live nodes, e.g. `IntoIter`). Tagged nodes remain kovan-owned.
205    ///
206    /// # Safety
207    /// Exclusive access; the chains must already be drained.
208    unsafe fn free_array_only(self) {
209        let capacity = unsafe { (*self.header).capacity };
210        let (layout, _) = Self::layout(capacity);
211        unsafe { alloc::alloc::dealloc(self.header as *mut u8, layout) };
212    }
213
214    /// # Safety
215    /// Caller must have exclusive access (map drop, or proxy reclamation
216    /// after guard quiescence).
217    unsafe fn free(self) {
218        let capacity = unsafe { (*self.header).capacity };
219        let guard = pin();
220        for i in 0..capacity {
221            let mut current = self.bucket(i).load(Ordering::Relaxed, &guard).as_raw();
222            while !current.is_null() {
223                unsafe {
224                    let next = (*current).next.load(Ordering::Relaxed, &guard).as_raw();
225                    if !is_tagged(next) {
226                        drop(Box::from_raw(current));
227                    }
228                    current = untag(next);
229                }
230            }
231        }
232        drop(guard);
233        let (layout, _) = Self::layout(capacity);
234        unsafe { alloc::alloc::dealloc(self.header as *mut u8, layout) };
235    }
236
237    #[inline(always)]
238    fn as_raw(self) -> *mut TableHeader {
239        self.header
240    }
241
242    #[inline(always)]
243    fn capacity(self) -> usize {
244        unsafe { (*self.header).capacity }
245    }
246
247    #[inline(always)]
248    fn buckets(self) -> *const Atomic<Node<K, V>> {
249        let (_, offset) = Self::layout_offset();
250        unsafe { (self.header as *const u8).add(offset) as *const Atomic<Node<K, V>> }
251    }
252
253    /// Header/bucket offset is capacity-independent; compute it once.
254    #[inline(always)]
255    fn layout_offset() -> ((), usize) {
256        let header = core::alloc::Layout::new::<TableHeader>();
257        let one = core::alloc::Layout::new::<Atomic<Node<K, V>>>();
258        let (_, offset) = header.extend(one).expect("layout");
259        ((), offset)
260    }
261
262    #[inline(always)]
263    fn bucket_index(self, hash: u64) -> usize {
264        (hash as usize) & unsafe { (*self.header).mask }
265    }
266
267    #[inline(always)]
268    fn bucket(self, idx: usize) -> &'static Atomic<Node<K, V>> {
269        // SAFETY: idx is masked or bounded by capacity; the 'static is a
270        // lie scoped by the caller's guard (same discipline as Shared).
271        unsafe { &*self.buckets().add(idx) }
272    }
273}
274
275/// Reclamation proxy for a table allocation (RetiredNode at offset 0).
276#[repr(C)]
277struct TableProxy<K: 'static, V: 'static> {
278    retired: RetiredNode,
279    table: TableRef<K, V>,
280}
281
282// SAFETY (kovan retirement rule): the proxy's destructor (running on any
283// thread) frees the table's nodes, hence K, V: Send.
284unsafe impl<K: Send, V: Send> Send for TableProxy<K, V> {}
285unsafe impl<K: Send + Sync, V: Send + Sync> Sync for TableProxy<K, V> {}
286
287impl<K, V> Drop for TableProxy<K, V> {
288    fn drop(&mut self) {
289        // SAFETY: kovan reclaimed the proxy only after every guard that
290        // could observe the old table has been released.
291        unsafe { self.table.free() };
292    }
293}
294
295/// High-Performance Lock-Free Map with automatic grow/shrink.
296pub struct HashMap<K: 'static, V: 'static, S = FixedState> {
297    table: Atomic<TableHeader>,
298    /// Approximate live-entry count driving the resize thresholds.
299    /// Signed: a transient negative under racing removes is harmless and
300    /// avoids a CAS loop (fetch_update) on the hot remove path.
301    count: AtomicIsize,
302    /// Single-resizer latch; writers wait while a resize is in flight.
303    resizing: AtomicBool,
304    /// Shrink floor: the initial capacity. The map never shrinks below the
305    /// size it was created with, preserving the caller's sizing intent (and
306    /// the historical fixed-table behavior for `new()`).
307    floor: usize,
308    hasher: S,
309    _marker: core::marker::PhantomData<(K, V)>,
310}
311
312#[cfg(feature = "std")]
313impl<K, V> HashMap<K, V, FixedState>
314where
315    K: Hash + Eq + Clone + 'static,
316    V: Clone + 'static,
317{
318    /// Creates a new empty hash map with FoldHash (FixedState).
319    pub fn new() -> Self {
320        Self::with_hasher(FixedState::default())
321    }
322
323    /// Creates a new empty hash map with at least `capacity` buckets.
324    pub fn with_capacity(capacity: usize) -> Self {
325        Self::with_capacity_and_hasher(capacity, FixedState::default())
326    }
327}
328
329impl<K, V, S> HashMap<K, V, S>
330where
331    K: Hash + Eq + Clone + 'static,
332    V: Clone + 'static,
333    S: BuildHasher,
334{
335    /// Creates a new hash map with custom hasher.
336    pub fn with_hasher(hasher: S) -> Self {
337        Self::with_capacity_and_hasher(DEFAULT_CAPACITY, hasher)
338    }
339
340    /// Creates a new hash map with at least `capacity` buckets and a custom hasher.
341    ///
342    /// The map grows when its load factor exceeds 0.75 and shrinks when it
343    /// falls below 0.25 — but never below `capacity`.
344    pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> Self {
345        let table = TableRef::<K, V>::alloc(capacity);
346        let floor = table.capacity();
347        Self {
348            table: Atomic::new(table.as_raw()),
349            count: AtomicIsize::new(0),
350            resizing: AtomicBool::new(false),
351            floor,
352            hasher,
353            _marker: core::marker::PhantomData,
354        }
355    }
356
357    /// Returns the current number of buckets.
358    pub fn capacity(&self) -> usize {
359        let guard = pin();
360        let table = TableRef::<K, V>::from_raw(self.table.load(Ordering::Acquire, &guard).as_raw());
361        table.capacity()
362    }
363
364    /// Spin until any in-flight resize completes.
365    #[inline]
366    fn wait_for_resize(&self) {
367        while self.resizing.load(Ordering::Acquire) {
368            core::hint::spin_loop();
369        }
370    }
371
372    /// Optimized get operation. Never blocks — reads the current table
373    /// snapshot under a guard, even while a resize is in flight.
374    pub fn get<Q>(&self, key: &Q) -> Option<V>
375    where
376        K: Borrow<Q>,
377        Q: Hash + Eq + ?Sized,
378    {
379        let hash = self.hasher.hash_one(key);
380        let guard = pin();
381        let table = TableRef::<K, V>::from_raw(self.table.load(Ordering::Acquire, &guard).as_raw());
382        let bucket = table.bucket(table.bucket_index(hash));
383
384        let mut current = bucket.load(Ordering::Acquire, &guard).as_raw();
385        while !current.is_null() {
386            unsafe {
387                let node = &*current;
388                // Check hash first (integer compare is fast). Matching a
389                // logically-deleted node is linearizable (the read happened
390                // before the delete), so no tag check on the match path.
391                if node.hash == hash && node.key.borrow() == key {
392                    return Some(node.value.clone());
393                }
394                // Untag: the pointer may carry the deletion tag.
395                current = untag(node.next.load(Ordering::Acquire, &guard).as_raw());
396            }
397        }
398        None
399    }
400
401    /// Checks if the key exists.
402    pub fn contains_key<Q>(&self, key: &Q) -> bool
403    where
404        K: Borrow<Q>,
405        Q: Hash + Eq + ?Sized,
406    {
407        self.get(key).is_some()
408    }
409
410    /// Insert a key-value pair.
411    pub fn insert(&self, key: K, value: V) -> Option<V> {
412        let hash = self.hasher.hash_one(&key);
413        let mut backoff = Backoff::new();
414        // Count a new key exactly once across re-validation retries
415        // (mirrors HopscotchMap: prevents both under-count, which causes
416        // cascading resizes, and double-count).
417        let mut counted = false;
418        // The first successful op's previous value is the linearized result;
419        // re-validation retries may replace a migrated clone of it.
420        let mut result: Option<Option<V>> = None;
421
422        'outer: loop {
423            self.wait_for_resize();
424
425            let guard = pin();
426            let table_raw = self.table.load(Ordering::Acquire, &guard).as_raw();
427            let table = TableRef::<K, V>::from_raw(table_raw);
428            if self.resizing.load(Ordering::Acquire) {
429                continue;
430            }
431
432            let bucket = table.bucket(table.bucket_index(hash));
433
434            // 1. Search for existing key to update (snip-walk: physically
435            //    unlink logically-deleted nodes as we pass them).
436            let mut prev_link = bucket;
437            let mut current = prev_link.load(Ordering::Acquire, &guard).as_raw();
438
439            while !current.is_null() {
440                unsafe {
441                    let node = &*current;
442                    let next = node.next.load(Ordering::Acquire, &guard).as_raw();
443
444                    if is_tagged(next) {
445                        // Logically deleted: snip it out (its tag owner has
446                        // already retired it). On contention restart the scan.
447                        if prev_link
448                            .compare_exchange(
449                                Shared::from_raw(current),
450                                Shared::from_raw(untag(next)),
451                                Ordering::AcqRel,
452                                Ordering::Relaxed,
453                                &guard,
454                            )
455                            .is_err()
456                        {
457                            backoff.spin();
458                            continue 'outer;
459                        }
460                        current = untag(next);
461                        continue;
462                    }
463
464                    if node.hash == hash && node.key == key {
465                        // Replace: logically delete the old node (tag-CAS
466                        // makes us its exclusive owner), then swing the
467                        // predecessor to the replacement in one step.
468                        let old_value = node.value.clone();
469                        if node
470                            .next
471                            .compare_exchange(
472                                Shared::from_raw(next),
473                                Shared::from_raw(tagged(next)),
474                                Ordering::AcqRel,
475                                Ordering::Relaxed,
476                                &guard,
477                            )
478                            .is_err()
479                        {
480                            // Someone else deleted/replaced it first.
481                            backoff.spin();
482                            continue 'outer;
483                        }
484                        // We own the old node now — we retire it, exactly once.
485                        let new_node = Box::into_raw(Box::new(Node {
486                            retired: RetiredNode::new(),
487                            hash,
488                            key: key.clone(),
489                            value: value.clone(),
490                            next: Atomic::new(next),
491                        }));
492                        let swapped = prev_link
493                            .compare_exchange(
494                                Shared::from_raw(current),
495                                Shared::from_raw(new_node),
496                                Ordering::AcqRel,
497                                Ordering::Relaxed,
498                                &guard,
499                            )
500                            .is_ok();
501                        // SAFETY: tag ownership; Node is #[repr(C)] with
502                        // RetiredNode at offset 0.
503                        retire(current);
504                        if result.is_none() {
505                            result = Some(Some(old_value));
506                        }
507                        if !swapped {
508                            // A helper snipped the old node before our swing;
509                            // the replacement is not installed — retry the
510                            // whole op (the removal already linearized).
511                            drop(Box::from_raw(new_node));
512                            backoff.spin();
513                            continue 'outer;
514                        }
515                        // Re-validate: if a resize started (or completed)
516                        // since we loaded the table, the migration may have
517                        // cloned the entry before our update — redo the op
518                        // on the new table so the update is not lost.
519                        if self.resizing.load(Ordering::SeqCst)
520                            || self.table.load(Ordering::SeqCst, &guard).as_raw() != table_raw
521                        {
522                            continue 'outer;
523                        }
524                        return result.unwrap();
525                    }
526
527                    prev_link = &node.next;
528                    current = next;
529                }
530            }
531
532            // 2. Key not found. Insert at TAIL (prev_link). The CAS expects
533            //    an untagged null, so it fails if the tail node was
534            //    concurrently logically deleted.
535            let new_node_ptr = Box::into_raw(Box::new(Node {
536                retired: RetiredNode::new(),
537                hash,
538                key: key.clone(),
539                value: value.clone(),
540                next: Atomic::null(),
541            }));
542
543            match prev_link.compare_exchange(
544                unsafe { Shared::from_raw(core::ptr::null_mut()) },
545                unsafe { Shared::from_raw(new_node_ptr) },
546                Ordering::Release,
547                Ordering::Relaxed,
548                &guard,
549            ) {
550                Ok(_) => {
551                    if !counted {
552                        counted = true;
553                        self.count.fetch_add(1, Ordering::Relaxed);
554                    }
555                    if result.is_none() {
556                        result = Some(None);
557                    }
558                    // Re-validate against a concurrent migration (see above).
559                    if self.resizing.load(Ordering::SeqCst)
560                        || self.table.load(Ordering::SeqCst, &guard).as_raw() != table_raw
561                    {
562                        continue 'outer;
563                    }
564
565                    // Grow check (only when we actually added an entry).
566                    let new_count = self.count.load(Ordering::Relaxed).max(0) as usize;
567                    let capacity = table.capacity();
568                    // Integer load-factor check: count/cap > 3/4.
569                    if 4 * new_count > 3 * capacity {
570                        drop(guard);
571                        self.try_resize(capacity * 2);
572                    }
573                    return result.unwrap();
574                }
575                Err(_) => {
576                    // Contention at the tail — retry the search/append loop.
577                    unsafe {
578                        drop(Box::from_raw(new_node_ptr));
579                    }
580                    backoff.spin();
581                    continue 'outer;
582                }
583            }
584        }
585    }
586
587    /// Insert a key-value pair only if the key does not exist.
588    /// Returns `None` if inserted, `Some(existing_value)` if the key already exists.
589    pub fn insert_if_absent(&self, key: K, value: V) -> Option<V> {
590        let hash = self.hasher.hash_one(&key);
591        let mut backoff = Backoff::new();
592        let mut counted = false;
593
594        'outer: loop {
595            self.wait_for_resize();
596
597            let guard = pin();
598            let table_raw = self.table.load(Ordering::Acquire, &guard).as_raw();
599            let table = TableRef::<K, V>::from_raw(table_raw);
600            if self.resizing.load(Ordering::Acquire) {
601                continue;
602            }
603
604            let bucket = table.bucket(table.bucket_index(hash));
605
606            // 1. Search for existing key (snip-walk).
607            let mut prev_link = bucket;
608            let mut current = prev_link.load(Ordering::Acquire, &guard).as_raw();
609
610            while !current.is_null() {
611                unsafe {
612                    let node = &*current;
613                    let next = node.next.load(Ordering::Acquire, &guard).as_raw();
614
615                    if is_tagged(next) {
616                        if prev_link
617                            .compare_exchange(
618                                Shared::from_raw(current),
619                                Shared::from_raw(untag(next)),
620                                Ordering::AcqRel,
621                                Ordering::Relaxed,
622                                &guard,
623                            )
624                            .is_err()
625                        {
626                            backoff.spin();
627                            continue 'outer;
628                        }
629                        current = untag(next);
630                        continue;
631                    }
632
633                    if node.hash == hash && node.key == key {
634                        // Found on a retry. This may be our own migrated
635                        // clone OR another caller's entry that landed while
636                        // the table swapped - the two are indistinguishable
637                        // here, and reporting None for someone else's entry
638                        // would admit a second winner. Return the canonical
639                        // value either way (for our own clone that is a
640                        // clone of the value we just inserted), matching
641                        // HopscotchMap's retry semantics: under a concurrent
642                        // resize a successful insert may report
643                        // Some(its own value); callers must treat the
644                        // returned value as canonical.
645                        return Some(node.value.clone());
646                    }
647                    prev_link = &node.next;
648                    current = next;
649                }
650            }
651
652            // 2. Key not found (or our pre-migration insert was not carried
653            //    over) — insert at TAIL. Untagged-null expectation makes the
654            //    CAS fail if the tail was concurrently logically deleted.
655            let new_node_ptr = Box::into_raw(Box::new(Node {
656                retired: RetiredNode::new(),
657                hash,
658                key: key.clone(),
659                value: value.clone(),
660                next: Atomic::null(),
661            }));
662
663            match prev_link.compare_exchange(
664                unsafe { Shared::from_raw(core::ptr::null_mut()) },
665                unsafe { Shared::from_raw(new_node_ptr) },
666                Ordering::Release,
667                Ordering::Relaxed,
668                &guard,
669            ) {
670                Ok(_) => {
671                    if !counted {
672                        counted = true;
673                        self.count.fetch_add(1, Ordering::Relaxed);
674                    }
675                    // Re-validate against a concurrent migration.
676                    if self.resizing.load(Ordering::SeqCst)
677                        || self.table.load(Ordering::SeqCst, &guard).as_raw() != table_raw
678                    {
679                        continue 'outer;
680                    }
681
682                    let new_count = self.count.load(Ordering::Relaxed).max(0) as usize;
683                    let capacity = table.capacity();
684                    // Integer load-factor check: count/cap > 3/4.
685                    if 4 * new_count > 3 * capacity {
686                        drop(guard);
687                        self.try_resize(capacity * 2);
688                    }
689                    return None;
690                }
691                Err(actual_val) => {
692                    // Contention at the tail.
693                    unsafe {
694                        let appended_ptr = actual_val.as_raw();
695                        drop(Box::from_raw(new_node_ptr));
696                        if !is_tagged(appended_ptr) && !appended_ptr.is_null() {
697                            let appended = &*appended_ptr;
698                            if appended.hash == hash && appended.key == key {
699                                // Race lost, key exists now. Same canonical-
700                                // value rule as the retry-found path above:
701                                // even if a pre-migration attempt of ours
702                                // inserted, the surviving entry is what
703                                // every caller must converge on.
704                                return Some(appended.value.clone());
705                            }
706                        }
707                    }
708                    backoff.spin();
709                    continue 'outer;
710                }
711            }
712        }
713    }
714
715    /// Returns the value corresponding to the key, or inserts the given value if the key is not present.
716    ///
717    /// This is linearizable: concurrent callers for the same key are guaranteed to
718    /// agree on which value was inserted (exactly one thread's CAS succeeds at the
719    /// list tail, and all others see that node on retry).
720    pub fn get_or_insert(&self, key: K, value: V) -> V {
721        match self.insert_if_absent(key, value.clone()) {
722            Some(existing) => existing,
723            None => value,
724        }
725    }
726
727    /// Remove a key-value pair.
728    pub fn remove<Q>(&self, key: &Q) -> Option<V>
729    where
730        K: Borrow<Q>,
731        Q: Hash + Eq + ?Sized,
732    {
733        let hash = self.hasher.hash_one(key);
734        let mut backoff = Backoff::new();
735        // First successful removal's value is the linearized result;
736        // re-validation retries only evict migrated clones.
737        let mut result: Option<V> = None;
738
739        'outer: loop {
740            self.wait_for_resize();
741
742            let guard = pin();
743            let table_raw = self.table.load(Ordering::Acquire, &guard).as_raw();
744            let table = TableRef::<K, V>::from_raw(table_raw);
745            if self.resizing.load(Ordering::Acquire) {
746                continue;
747            }
748
749            let bucket = table.bucket(table.bucket_index(hash));
750
751            let mut prev_link = bucket;
752            let mut current = prev_link.load(Ordering::Acquire, &guard).as_raw();
753
754            while !current.is_null() {
755                unsafe {
756                    let node = &*current;
757                    let next = node.next.load(Ordering::Acquire, &guard).as_raw();
758
759                    if is_tagged(next) {
760                        // Logically deleted by someone else: snip and move on.
761                        if prev_link
762                            .compare_exchange(
763                                Shared::from_raw(current),
764                                Shared::from_raw(untag(next)),
765                                Ordering::AcqRel,
766                                Ordering::Relaxed,
767                                &guard,
768                            )
769                            .is_err()
770                        {
771                            backoff.spin();
772                            continue 'outer;
773                        }
774                        current = untag(next);
775                        continue;
776                    }
777
778                    if node.hash == hash && node.key.borrow() == key {
779                        let old_value = node.value.clone();
780
781                        // Logical delete: tag the victim's next. The tag
782                        // owner — and only the tag owner — retires the node,
783                        // and the tag makes concurrent tail-inserts onto
784                        // this node fail.
785                        if node
786                            .next
787                            .compare_exchange(
788                                Shared::from_raw(next),
789                                Shared::from_raw(tagged(next)),
790                                Ordering::AcqRel,
791                                Ordering::Relaxed,
792                                &guard,
793                            )
794                            .is_err()
795                        {
796                            backoff.spin();
797                            continue 'outer;
798                        }
799
800                        // Physical unlink (best effort — if it fails, a
801                        // later walker snips it).
802                        let _ = prev_link.compare_exchange(
803                            Shared::from_raw(current),
804                            Shared::from_raw(next),
805                            Ordering::AcqRel,
806                            Ordering::Relaxed,
807                            &guard,
808                        );
809
810                        // SAFETY: tag ownership; Node is #[repr(C)] with
811                        // RetiredNode at offset 0.
812                        retire(current);
813                        if result.is_none() {
814                            result = Some(old_value);
815                        }
816
817                        // Single atomic decrement (signed counter — cannot
818                        // wrap; a transient negative just clamps to 0 below).
819                        let new_count =
820                            (self.count.fetch_sub(1, Ordering::Relaxed) - 1).max(0) as usize;
821                        // Integer load-factor check: count/cap < 1/4.
822                        let shrink_to = (4 * new_count < table.capacity()
823                            && table.capacity() > self.floor)
824                            .then_some(table.capacity() / 2);
825
826                        // Re-validate: a concurrent migration may have cloned
827                        // this entry into the new table before we deleted it
828                        // here — redo the removal on the current table so the
829                        // key does not resurrect.
830                        if self.resizing.load(Ordering::SeqCst)
831                            || self.table.load(Ordering::SeqCst, &guard).as_raw() != table_raw
832                        {
833                            continue 'outer;
834                        }
835
836                        if let Some(cap) = shrink_to {
837                            drop(guard);
838                            self.try_resize(cap);
839                        }
840                        return result;
841                    }
842
843                    prev_link = &node.next;
844                    current = next;
845                }
846            }
847
848            // Key not present in the current table.
849            return result;
850        }
851    }
852
853    /// Remove **all** nodes matching `key`, returning the most recent value
854    /// if the key was present.
855    ///
856    /// [`remove`](Self::remove) unlinks only the first matching entry.
857    /// Insert/remove races can transiently leave more than one entry for
858    /// the same key ("versions"); after a plain `remove()` an older version
859    /// would become visible again. This method keeps removing until a full
860    /// scan finds no match, so the key is guaranteed absent at the
861    /// linearization point of the final scan.
862    ///
863    /// Use `remove()` for single-version removal semantics and
864    /// `force_remove()` when the key must be fully evicted.
865    ///
866    /// Note: a concurrent `insert` of the same key can land after the final
867    /// scan, as with any removal under contention.
868    pub fn force_remove<Q>(&self, key: &Q) -> Option<V>
869    where
870        K: Borrow<Q>,
871        Q: Hash + Eq + ?Sized,
872    {
873        let mut newest = None;
874        loop {
875            match self.remove(key) {
876                Some(v) => {
877                    // The first removal unlinks the first match in scan
878                    // order — the live (most recent) version.
879                    if newest.is_none() {
880                        newest = Some(v);
881                    }
882                }
883                None => return newest,
884            }
885        }
886    }
887
888    /// Clear the map.
889    pub fn clear(&self) {
890        // Take the resize latch so the table cannot be swapped (and no
891        // writer is mid-migration) while we unlink the chains.
892        while self
893            .resizing
894            .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
895            .is_err()
896        {
897            core::hint::spin_loop();
898        }
899
900        let guard = pin();
901        let table = TableRef::<K, V>::from_raw(self.table.load(Ordering::Acquire, &guard).as_raw());
902
903        for i in 0..table.capacity() {
904            let bucket = table.bucket(i);
905            loop {
906                let head = bucket.load(Ordering::Acquire, &guard);
907                if head.is_null() {
908                    break;
909                }
910
911                // Try to unlink the whole chain at once
912                match bucket.compare_exchange(
913                    head,
914                    unsafe { Shared::from_raw(core::ptr::null_mut()) },
915                    Ordering::Release,
916                    Ordering::Relaxed,
917                    &guard,
918                ) {
919                    Ok(_) => {
920                        // Retire the chain's live nodes. Tagged nodes were
921                        // already retired by their tag owners — skip them.
922                        unsafe {
923                            let mut current = head.as_raw();
924                            while !current.is_null() {
925                                let next = (*current).next.load(Ordering::Relaxed, &guard).as_raw();
926                                if !is_tagged(next) {
927                                    // SAFETY: allocated via Box::into_raw;
928                                    // Node is #[repr(C)], RetiredNode first.
929                                    retire(current);
930                                }
931                                current = untag(next);
932                            }
933                        }
934                        break;
935                    }
936                    Err(_) => {
937                        // Contention, retry
938                        continue;
939                    }
940                }
941            }
942        }
943
944        self.count.store(0, Ordering::Release);
945        self.resizing.store(false, Ordering::Release);
946    }
947
948    /// Returns true if the map is empty.
949    pub fn is_empty(&self) -> bool {
950        self.len() == 0
951    }
952
953    /// Returns the number of elements in the map.
954    ///
955    /// O(1): maintained by insert/remove. Approximate while concurrent
956    /// updates are in flight (exact in quiescence), like `HopscotchMap`.
957    pub fn len(&self) -> usize {
958        self.count.load(Ordering::Relaxed).max(0) as usize
959    }
960
961    /// Resize the table to `new_capacity` buckets (single resizer wins).
962    ///
963    /// Clones every entry into a new table, swaps the table pointer, then
964    /// retires the old table. The old table's destructor frees whatever
965    /// nodes remain in its chains at reclamation time — entries removed or
966    /// replaced in the meantime were unlinked and retired individually, so
967    /// nothing is freed twice and nothing leaks.
968    fn try_resize(&self, new_capacity: usize) {
969        if self
970            .resizing
971            .compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed)
972            .is_err()
973        {
974            return;
975        }
976
977        let new_capacity = new_capacity
978            .next_power_of_two()
979            .max(MIN_CAPACITY)
980            .max(self.floor);
981        let guard = pin();
982        let old_raw = self.table.load(Ordering::Acquire, &guard).as_raw();
983        let old_table = TableRef::<K, V>::from_raw(old_raw);
984
985        if old_table.capacity() == new_capacity {
986            self.resizing.store(false, Ordering::Release);
987            return;
988        }
989
990        let new_table = TableRef::<K, V>::alloc(new_capacity);
991
992        // Migrate: clone every live entry (logically-deleted nodes — tagged
993        // next — are skipped). We are the only writer of the new table (it
994        // is unpublished), so plain stores are sufficient.
995        for i in 0..old_table.capacity() {
996            let bucket = old_table.bucket(i);
997            let mut current = bucket.load(Ordering::Acquire, &guard).as_raw();
998            while !current.is_null() {
999                let node = unsafe { &*current };
1000                let next = node.next.load(Ordering::Acquire, &guard).as_raw();
1001                if !is_tagged(next) {
1002                    let dst = new_table.bucket(new_table.bucket_index(node.hash));
1003                    let head = dst.load(Ordering::Relaxed, &guard);
1004                    let clone = Box::into_raw(Box::new(Node {
1005                        retired: RetiredNode::new(),
1006                        hash: node.hash,
1007                        key: node.key.clone(),
1008                        value: node.value.clone(),
1009                        next: Atomic::new(head.as_raw()),
1010                    }));
1011                    dst.store(unsafe { Shared::from_raw(clone) }, Ordering::Relaxed);
1012                }
1013                current = untag(next);
1014            }
1015        }
1016
1017        match self.table.compare_exchange(
1018            unsafe { Shared::from_raw(old_raw) },
1019            unsafe { Shared::from_raw(new_table.as_raw()) },
1020            Ordering::Release,
1021            Ordering::Relaxed,
1022            &guard,
1023        ) {
1024            Ok(_) => {
1025                // Retire the old table through a proxy: reclamation (which
1026                // frees the remaining chains and the allocation) is deferred
1027                // until every guard that could observe it is gone.
1028                let proxy = Box::into_raw(Box::new(TableProxy {
1029                    retired: RetiredNode::new(),
1030                    table: old_table,
1031                }));
1032                // SAFETY: TableProxy is #[repr(C)] with RetiredNode at
1033                // offset 0, allocated via Box::into_raw.
1034                unsafe { retire(proxy) };
1035            }
1036            Err(_) => {
1037                // Table changed under us (cannot normally happen — we hold
1038                // the resize latch). Discard the unpublished new table.
1039                unsafe { new_table.free() };
1040            }
1041        }
1042
1043        self.resizing.store(false, Ordering::Release);
1044    }
1045
1046    /// Returns an iterator over the map entries.
1047    /// Yields (K, V) clones from a table snapshot taken at creation.
1048    pub fn iter(&self) -> Iter<'_, K, V, S> {
1049        let guard = pin();
1050        let table = TableRef::<K, V>::from_raw(self.table.load(Ordering::Acquire, &guard).as_raw());
1051        Iter {
1052            _map: self,
1053            table,
1054            bucket_idx: 0,
1055            current: core::ptr::null(),
1056            guard,
1057        }
1058    }
1059
1060    /// Returns an iterator over the map keys.
1061    /// Yields K clones.
1062    pub fn keys(&self) -> Keys<'_, K, V, S> {
1063        Keys { iter: self.iter() }
1064    }
1065
1066    /// Returns an iterator over the map values (clones `V`).
1067    pub fn values(&self) -> Values<'_, K, V, S> {
1068        Values { iter: self.iter() }
1069    }
1070
1071    /// Insert all `(K, V)` pairs from `iter`. Takes `&self` (concurrent map).
1072    pub fn extend<I: IntoIterator<Item = (K, V)>>(&self, iter: I) {
1073        for (k, v) in iter {
1074            self.insert(k, v);
1075        }
1076    }
1077
1078    /// Get the underlying hasher itself.
1079    pub fn hasher(&self) -> &S {
1080        &self.hasher
1081    }
1082}
1083
1084/// Iterator over HashMap entries.
1085///
1086/// Field ordering matters for drop safety.
1087/// Rust drops struct fields in declaration order.
1088/// The `guard` must be dropped *after* `current`/`table` so that the epoch
1089/// pin covering the snapshot is not released before we're done with the raw
1090/// pointers.
1091pub struct Iter<'a, K: 'static, V: 'static, S> {
1092    _map: &'a HashMap<K, V, S>,
1093    table: TableRef<K, V>,
1094    bucket_idx: usize,
1095    current: *const Node<K, V>,
1096    guard: kovan::Guard,
1097}
1098
1099impl<'a, K, V, S> Iterator for Iter<'a, K, V, S>
1100where
1101    K: Clone,
1102    V: Clone,
1103{
1104    type Item = (K, V);
1105
1106    fn next(&mut self) -> Option<Self::Item> {
1107        loop {
1108            if !self.current.is_null() {
1109                unsafe {
1110                    let node = &*self.current;
1111                    let next = node.next.load(Ordering::Acquire, &self.guard).as_raw();
1112                    // Advance current (the pointer may carry a deletion tag).
1113                    self.current = untag(next);
1114                    if is_tagged(next) {
1115                        // Logically deleted — do not yield.
1116                        continue;
1117                    }
1118                    return Some((node.key.clone(), node.value.clone()));
1119                }
1120            }
1121
1122            // Move to next bucket
1123            let table = self.table;
1124            if self.bucket_idx >= table.capacity() {
1125                return None;
1126            }
1127
1128            let bucket = table.bucket(self.bucket_idx);
1129            self.bucket_idx += 1;
1130            self.current = bucket.load(Ordering::Acquire, &self.guard).as_raw();
1131        }
1132    }
1133}
1134
1135/// Iterator over HashMap keys.
1136pub struct Keys<'a, K: 'static, V: 'static, S> {
1137    iter: Iter<'a, K, V, S>,
1138}
1139
1140impl<'a, K, V, S> Iterator for Keys<'a, K, V, S>
1141where
1142    K: Clone,
1143    V: Clone,
1144{
1145    type Item = K;
1146
1147    fn next(&mut self) -> Option<Self::Item> {
1148        self.iter.next().map(|(k, _)| k)
1149    }
1150}
1151
1152impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S>
1153where
1154    K: Hash + Eq + Clone + 'static,
1155    V: Clone + 'static,
1156    S: BuildHasher,
1157{
1158    type Item = (K, V);
1159    type IntoIter = Iter<'a, K, V, S>;
1160
1161    fn into_iter(self) -> Self::IntoIter {
1162        self.iter()
1163    }
1164}
1165
1166/// Iterator over HashMap values (clones `V`).
1167pub struct Values<'a, K: 'static, V: 'static, S> {
1168    iter: Iter<'a, K, V, S>,
1169}
1170
1171impl<'a, K, V, S> Iterator for Values<'a, K, V, S>
1172where
1173    K: Clone,
1174    V: Clone,
1175{
1176    type Item = V;
1177
1178    #[inline]
1179    fn next(&mut self) -> Option<V> {
1180        self.iter.next().map(|(_, v)| v)
1181    }
1182}
1183
1184/// Owned iterator yielding `(K, V)` by value — moves out of the nodes, no
1185/// clone. Consuming the map gives exclusive access, so no guard protection of
1186/// the yielded values is needed.
1187pub struct IntoIter<K: 'static, V: 'static> {
1188    table: TableRef<K, V>,
1189    bucket_idx: usize,
1190    current: *mut Node<K, V>,
1191    guard: kovan::Guard,
1192}
1193
1194impl<K, V> Iterator for IntoIter<K, V> {
1195    type Item = (K, V);
1196
1197    fn next(&mut self) -> Option<(K, V)> {
1198        loop {
1199            if !self.current.is_null() {
1200                let node = self.current;
1201                let next = unsafe { (*node).next.load(Ordering::Acquire, &self.guard).as_raw() };
1202                self.current = untag(next);
1203                if is_tagged(next) {
1204                    continue; // logically deleted, owned by kovan
1205                }
1206                // Move K and V out, then free the shell without running drop.
1207                let k = unsafe { core::ptr::read(&(*node).key) };
1208                let v = unsafe { core::ptr::read(&(*node).value) };
1209                unsafe {
1210                    alloc::alloc::dealloc(
1211                        node as *mut u8,
1212                        core::alloc::Layout::new::<Node<K, V>>(),
1213                    );
1214                }
1215                return Some((k, v));
1216            }
1217            if self.bucket_idx >= self.table.capacity() {
1218                return None;
1219            }
1220            let bucket = self.table.bucket(self.bucket_idx);
1221            self.bucket_idx += 1;
1222            self.current = bucket.load(Ordering::Acquire, &self.guard).as_raw();
1223        }
1224    }
1225}
1226
1227impl<K, V> Drop for IntoIter<K, V> {
1228    fn drop(&mut self) {
1229        while self.next().is_some() {} // drop remaining live K/V + free shells
1230        unsafe { self.table.free_array_only() };
1231    }
1232}
1233
1234impl<K, V, S> IntoIterator for HashMap<K, V, S>
1235where
1236    K: 'static,
1237    V: 'static,
1238{
1239    type Item = (K, V);
1240    type IntoIter = IntoIter<K, V>;
1241
1242    fn into_iter(self) -> IntoIter<K, V> {
1243        let mut me = core::mem::ManuallyDrop::new(self);
1244        let guard = pin();
1245        let table = TableRef::<K, V>::from_raw(me.table.load(Ordering::Relaxed, &guard).as_raw());
1246        // Suppress HashMap::drop (we own the table now); drop only the hasher —
1247        // the remaining fields are atomics / usize / ZST marker.
1248        unsafe { core::ptr::drop_in_place(&mut me.hasher) };
1249        IntoIter {
1250            table,
1251            bucket_idx: 0,
1252            current: core::ptr::null_mut(),
1253            guard,
1254        }
1255    }
1256}
1257
1258impl<K, V, S> core::iter::FromIterator<(K, V)> for HashMap<K, V, S>
1259where
1260    K: Hash + Eq + Clone + Send + 'static,
1261    V: Clone + Send + 'static,
1262    S: BuildHasher + Default,
1263{
1264    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
1265        let map = Self::with_capacity_and_hasher(MIN_CAPACITY, S::default());
1266        for (k, v) in iter {
1267            map.insert(k, v);
1268        }
1269        map
1270    }
1271}
1272
1273#[cfg(feature = "std")]
1274impl<K, V> Default for HashMap<K, V, FixedState>
1275where
1276    K: Hash + Eq + Clone + 'static,
1277    V: Clone + 'static,
1278{
1279    fn default() -> Self {
1280        Self::new()
1281    }
1282}
1283
1284// SAFETY: HashMap is Send if K, V, S are Send (moving ownership between threads).
1285// HashMap is Sync if K, V, S are Send+Sync. The stronger bound on Sync is needed
1286// because concurrent `get()` calls clone V through a `&V` reference across threads;
1287// if V were Send but not Sync, sharing `&HashMap` could transmit a non-Sync V reference
1288// to another thread via clone(), violating thread-safety.
1289unsafe impl<K: Send, V: Send, S: Send> Send for HashMap<K, V, S> {}
1290unsafe impl<K: Send + Sync, V: Send + Sync, S: Send + Sync> Sync for HashMap<K, V, S> {}
1291
1292impl<K: 'static, V: 'static, S> Drop for HashMap<K, V, S> {
1293    fn drop(&mut self) {
1294        // SAFETY: `drop(&mut self)` guarantees exclusive ownership — no concurrent
1295        // readers can exist.  Rust's type system enforces this: `Iter<'a, …>` borrows
1296        // `&'a HashMap`, so it cannot outlive the `HashMap`.  The Table's destructor
1297        // frees its chains.
1298        let guard = pin();
1299        let table = TableRef::<K, V>::from_raw(self.table.load(Ordering::Relaxed, &guard).as_raw());
1300        drop(guard);
1301        // SAFETY: exclusive access; frees remaining chains + the allocation.
1302        unsafe { table.free() };
1303
1304        // Flush nodes/tables previously retired by concurrent operations
1305        kovan::flush();
1306    }
1307}
1308
1309#[cfg(test)]
1310mod tests {
1311    use super::*;
1312
1313    #[test]
1314    fn test_insert_and_get() {
1315        let map = HashMap::new();
1316        assert_eq!(map.insert(1, 100), None);
1317        assert_eq!(map.get(&1), Some(100));
1318        assert_eq!(map.get(&2), None);
1319    }
1320
1321    #[test]
1322    fn test_insert_replace() {
1323        let map = HashMap::new();
1324        assert_eq!(map.insert(1, 100), None);
1325        assert_eq!(map.insert(1, 200), Some(100));
1326        assert_eq!(map.get(&1), Some(200));
1327    }
1328
1329    #[test]
1330    fn test_grow() {
1331        let map = HashMap::with_capacity(64);
1332        assert_eq!(map.capacity(), 64);
1333        for i in 0..1000u64 {
1334            map.insert(i, i * 2);
1335        }
1336        assert!(map.capacity() > 64, "map should have grown");
1337        for i in 0..1000u64 {
1338            assert_eq!(map.get(&i), Some(i * 2));
1339        }
1340        assert_eq!(map.len(), 1000);
1341    }
1342
1343    #[test]
1344    fn test_shrink() {
1345        let map = HashMap::with_capacity(64);
1346        for i in 0..1000u64 {
1347            map.insert(i, i);
1348        }
1349        let grown = map.capacity();
1350        assert!(grown > 64);
1351        for i in 0..1000u64 {
1352            map.remove(&i);
1353        }
1354        assert!(
1355            map.capacity() < grown,
1356            "map should have shrunk (capacity {} -> {})",
1357            grown,
1358            map.capacity()
1359        );
1360        assert!(map.capacity() >= 64, "never below the initial capacity");
1361        assert_eq!(map.len(), 0);
1362    }
1363
1364    #[test]
1365    fn test_no_shrink_below_floor() {
1366        let map = HashMap::with_capacity(4096);
1367        for i in 0..100u64 {
1368            map.insert(i, i);
1369        }
1370        for i in 0..100u64 {
1371            map.remove(&i);
1372        }
1373        assert_eq!(map.capacity(), 4096, "floor preserves sizing intent");
1374    }
1375
1376    #[test]
1377    fn test_concurrent_inserts() {
1378        use alloc::sync::Arc;
1379        extern crate std;
1380        use std::thread;
1381
1382        let map = Arc::new(HashMap::new());
1383        let mut handles = alloc::vec::Vec::new();
1384
1385        for thread_id in 0..4 {
1386            let map_clone = Arc::clone(&map);
1387            let handle = thread::spawn(move || {
1388                for i in 0..1000 {
1389                    let key = thread_id * 1000 + i;
1390                    map_clone.insert(key, key * 2);
1391                }
1392            });
1393            handles.push(handle);
1394        }
1395
1396        for handle in handles {
1397            handle.join().unwrap();
1398        }
1399
1400        for thread_id in 0..4 {
1401            for i in 0..1000 {
1402                let key = thread_id * 1000 + i;
1403                assert_eq!(map.get(&key), Some(key * 2));
1404            }
1405        }
1406    }
1407
1408    #[test]
1409    fn test_concurrent_grow() {
1410        use alloc::sync::Arc;
1411        extern crate std;
1412        use std::thread;
1413
1414        let map = Arc::new(HashMap::with_capacity(64));
1415        let mut handles = alloc::vec::Vec::new();
1416
1417        for thread_id in 0..8u64 {
1418            let map_clone = Arc::clone(&map);
1419            handles.push(thread::spawn(move || {
1420                for i in 0..2000u64 {
1421                    let key = thread_id * 10_000 + i;
1422                    map_clone.insert(key, key);
1423                }
1424            }));
1425        }
1426        for handle in handles {
1427            handle.join().unwrap();
1428        }
1429
1430        for thread_id in 0..8u64 {
1431            for i in 0..2000u64 {
1432                let key = thread_id * 10_000 + i;
1433                assert_eq!(map.get(&key), Some(key), "lost key {key} during growth");
1434            }
1435        }
1436        assert!(map.capacity() >= 16_000);
1437    }
1438}