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