Skip to main content

kovan_map/hopscotch/
iter.rs

1//! Walking the map: a borrowed walk that keeps the table it started on and yields a key once
2//! (it recognizes a key a displacement carried ahead of it), and the owned walk that consumes
3//! the map.
4
5extern crate alloc;
6
7use super::table::{Entry, Table, Walk, Word};
8use super::{HopscotchMap, NEIGHBORHOOD_SIZE};
9use crate::MaybeSend;
10use core::hash::{BuildHasher, Hash};
11use core::marker::PhantomData;
12use core::sync::atomic::Ordering;
13use kovan::{drop_node, free_node, pin};
14
15// Construction: none of `iter`/`keys`/`values` hashes or clones a value (the walks that do
16// live in the `Iterator` impls below). `K: Eq` is captured here as the walk's key comparison
17// (how it recognizes a key it already met), so the `Iterator` impls ask only for the clones they
18// make: `K: Clone` and `V: Clone` for entries, `K: Clone` for keys, `V: Clone` for values.
19impl<K: Eq + 'static, V: 'static, S> HopscotchMap<K, V, S> {
20    /// Returns an iterator over the map entries.
21    ///
22    /// The iterator walks the table that is current when it is created, to
23    /// its end, even when a resize replaces that table meanwhile (the walk's
24    /// position means nothing in another table, whose layout differs). An
25    /// entry present from the iterator's creation to its end is yielded
26    /// exactly once: a resize neither skips nor repeats it, and a
27    /// displacement, which moves an entry to a higher slot of its
28    /// neighborhood, links it there before unlinking it from its old slot (so
29    /// the walk meets it) and the walk recognizes a key it already met there
30    /// (so it is not repeated). An insert publishes its entry's hop bit before
31    /// it links the entry, so an entry the walk finds in a slot is one a
32    /// lookup finds too. An entry inserted, removed or updated concurrently may
33    /// or may not be reflected, and no key is yielded twice. The iterator's
34    /// own loop (`fold`, and `for_each`, `count` and the other folds built on
35    /// it) reads a group of slots before it yields their entries, so a write
36    /// its closure makes is one of those concurrent writes too.
37    pub fn iter(&self) -> HopscotchIter<'_, K, V, S> {
38        let guard = pin();
39        let table = self.table.load(Ordering::Acquire, &guard).as_raw();
40        // SAFETY: loaded under `guard`, which the iterator keeps.
41        let current = unsafe { &*table };
42        let (slots, mask) = (current.buckets.len(), current.home_mask());
43        HopscotchIter {
44            table,
45            slots,
46            mask,
47            bucket_idx: 0,
48            seen: Seen::new(<K as PartialEq>::eq),
49            guard,
50            _map: PhantomData,
51        }
52    }
53
54    /// Returns an iterator over the map keys.
55    pub fn keys(&self) -> HopscotchKeys<'_, K, V, S> {
56        HopscotchKeys { iter: self.iter() }
57    }
58
59    /// Returns an iterator over the map values (clones `V`).
60    pub fn values(&self) -> HopscotchValues<'_, K, V, S> {
61        HopscotchValues { iter: self.iter() }
62    }
63}
64
65/// Iterator over HopscotchMap entries ([`HopscotchMap::iter`]).
66pub struct HopscotchIter<'a, K: 'static, V: 'static, S> {
67    /// The table the walk started on, loaded under `guard`.
68    table: *const Table<K, V>,
69    /// `table`'s slot count and home mask, read once: the walk never reloads them.
70    slots: usize,
71    mask: usize,
72    bucket_idx: usize,
73    seen: Seen<K, V>,
74    guard: kovan::Guard,
75    _map: PhantomData<&'a HopscotchMap<K, V, S>>,
76}
77
78/// Slots a walk's `fold` reads before it yields their entries, and a group whose entries `next`
79/// prefetches when it enters it: one bit each of a `u64`.
80const GROUP: usize = u64::BITS as usize;
81
82/// What a walk remembers of the slots it met: how it recognizes a key it already met.
83struct Seen<K, V> {
84    /// The entry the walk read in each of its last `NEIGHBORHOOD_SIZE` slots (slot `i` at
85    /// `i % NEIGHBORHOOD_SIZE`, null until the walk meets an entry there), loaded under the
86    /// walk's guard.
87    recent: [*const Entry<K, V>; NEIGHBORHOOD_SIZE],
88    /// For each home the walk met an entry of (home `h` at `h % NEIGHBORHOOD_SIZE`): the home's
89    /// low 32 bits over a 32-bit filter of the hashes of the home's entries the walk met, bit
90    /// `hash >> 59` of each. A walk meets a key again only as an entry of the same hash, so the
91    /// same home and the same bit: an entry whose bit is clear in its home's filter is one the
92    /// walk has not met, known without a scan.
93    homes: [u64; NEIGHBORHOOD_SIZE],
94    /// `K`'s equality, taken where `iter` is built.
95    same_key: fn(&K, &K) -> bool,
96}
97
98impl<K, V> Seen<K, V> {
99    /// Nothing met yet.
100    fn new(same_key: fn(&K, &K) -> bool) -> Self {
101        Self {
102            recent: [core::ptr::null(); NEIGHBORHOOD_SIZE],
103            homes: [0; NEIGHBORHOOD_SIZE],
104            same_key,
105        }
106    }
107
108    /// Whether the walk yields `entry`, which it read at slot `idx` (the slots it read before
109    /// are all lower) of a table whose home mask is `mask`: `false` for a key it already met.
110    /// Every walk step, `next` and `fold` alike, is this call.
111    #[inline(always)]
112    fn first_meeting(&mut self, entry: &Entry<K, V>, idx: usize, mask: usize) -> bool {
113        self.recent[idx % NEIGHBORHOOD_SIZE] = entry;
114        let home = (entry.hash as usize) & mask;
115        // The filter of `home` as the walk left it at its last entry of `home`, or an empty one
116        // when the cell names another home. Every entry of `home` lies in its neighborhood,
117        // `home..home + NEIGHBORHOOD_SIZE`, so no entry of another home of this cell lies
118        // between two entries of `home` in slot order: the cell holds `home`'s filter from the
119        // walk's first entry of `home` to its last. (A home equal to `home` in its low 32 bits
120        // only is `NEIGHBORHOOD_SIZE` homes away or more: its bits can only add a scan.)
121        let cell = &mut self.homes[home % NEIGHBORHOOD_SIZE];
122        let bit = 1u32 << (entry.hash >> 59);
123        let filter =
124            core::hint::select_unpredictable((*cell >> 32) as u32 == home as u32, *cell as u32, 0);
125        *cell = (u64::from(home as u32) << 32) | u64::from(filter | bit);
126        filter & bit == 0 || !self.met_before(home, idx, entry)
127    }
128
129    /// Whether the walk already met the key of `entry`, read at slot `idx`, in a lower slot of
130    /// the key's neighborhood (which starts at `home`). A move carries an entry to a higher slot
131    /// of its neighborhood, so the walk can meet it a second time there (or a newer entry of its
132    /// key, after an update or a re-insert). Every slot of that neighborhood below `idx` is still
133    /// in `recent`: the neighborhood spans `NEIGHBORHOOD_SIZE` slots. A cell the walk last wrote
134    /// at least `NEIGHBORHOOD_SIZE` slots back (a free slot leaves its cell as it was) holds an
135    /// entry of a lower home, whose hash differs; the walk's guard keeps it allocated.
136    #[cold]
137    #[inline(never)]
138    fn met_before(&self, home: usize, idx: usize, entry: &Entry<K, V>) -> bool {
139        let lowest = home.max(idx.saturating_sub(NEIGHBORHOOD_SIZE - 1));
140        (lowest..idx).any(|seen_idx| {
141            let seen = self.recent[seen_idx % NEIGHBORHOOD_SIZE];
142            // SAFETY: null for a slot where the walk met no entry yet, else loaded under the
143            // walk's guard, which keeps it from being freed.
144            unsafe { seen.as_ref() }.is_some_and(|seen| {
145                seen.hash == entry.hash
146                    && (core::ptr::eq(seen, entry) || (self.same_key)(&seen.key, &entry.key))
147            })
148        })
149    }
150}
151
152impl<K, V, S> HopscotchIter<'_, K, V, S> {
153    /// The next entry the walk yields: the step of every `next` of the map's walks.
154    #[inline(always)]
155    fn next_entry(&mut self) -> Option<&Entry<K, V>> {
156        // SAFETY: owned by this iterator's `guard`, which was pinned before `table` was loaded
157        // and dies with the iterator: a resize that retires the table cannot free it (or its
158        // entries) while the guard is held.
159        let table = unsafe { &*self.table };
160        while self.bucket_idx < self.slots {
161            let idx = self.bucket_idx;
162            self.bucket_idx += 1;
163            if idx.is_multiple_of(GROUP) {
164                // The entries of the group of slots the walk enters reach the cache while it
165                // walks them. Only a hint: the walk reads each slot again when it gets there.
166                // Relaxed: no entry is read through these words.
167                for bucket in &table.buckets[idx..self.slots.min(idx + GROUP)] {
168                    table.prefetch_entry(bucket.load(Ordering::Relaxed, &self.guard).ptr());
169                }
170            }
171            let entry = table
172                .get_bucket(idx)
173                .load(Ordering::Acquire, &self.guard)
174                .entry();
175            if let Some(entry) = entry
176                && self.seen.first_meeting(entry, idx, self.mask)
177            {
178                return Some(entry);
179            }
180        }
181        None
182    }
183
184    /// The rest of the walk, `f` on each entry it yields, in one loop whose position and
185    /// accumulator are locals: the loop of every `fold` of the map's walks. It reads the slots
186    /// a group of `GROUP` at a time, in order, and then yields the entries it read there: each
187    /// entry's line, prefetched when its slot is read, reaches the cache while the rest of the
188    /// group is read, and the group's free slots cost no branch. It reads each slot once, in
189    /// order, as `next` does, so what it yields keeps the guarantees of [`HopscotchMap::iter`];
190    /// it reads a slot up to `GROUP - 1` slots before it yields the slot's entry.
191    #[inline(always)]
192    fn fold_entries<B>(self, init: B, mut f: impl FnMut(B, &Entry<K, V>) -> B) -> B {
193        let Self {
194            table,
195            slots,
196            mask,
197            bucket_idx,
198            mut seen,
199            guard,
200            ..
201        } = self;
202        // SAFETY: as in `next_entry`: `guard` was pinned before `table` was loaded and lives to
203        // the end of this call.
204        let table = unsafe { &*table };
205        let mut acc = init;
206        let mut group = [core::ptr::null::<Entry<K, V>>(); GROUP];
207        let mut first = bucket_idx;
208        for buckets in table.buckets[bucket_idx..slots].chunks(GROUP) {
209            let mut occupied = 0u64;
210            for (offset, (bucket, cell)) in buckets.iter().zip(&mut group).enumerate() {
211                let entry: *const Entry<K, V> = bucket.load(Ordering::Acquire, &guard).ptr();
212                *cell = entry;
213                occupied |= u64::from(!entry.is_null()) << offset;
214                table.prefetch_entry(entry);
215            }
216            while occupied != 0 {
217                let offset = occupied.trailing_zeros() as usize;
218                occupied &= occupied - 1;
219                // SAFETY: a slot's non-null word, loaded under `guard`, which keeps its entry from
220                // being freed.
221                let entry = unsafe { &*group[offset] };
222                if seen.first_meeting(entry, first + offset, mask) {
223                    acc = f(acc, entry);
224                }
225            }
226            first += buckets.len();
227        }
228        acc
229    }
230}
231
232impl<'a, K, V, S> Iterator for HopscotchIter<'a, K, V, S>
233where
234    K: Clone,
235    V: Clone,
236{
237    type Item = (K, V);
238
239    fn next(&mut self) -> Option<Self::Item> {
240        self.next_entry()
241            .map(|entry| (entry.key.clone(), entry.value.clone()))
242    }
243
244    /// The rest of the walk in the walk's own loop (`for_each`, `count`, `sum` and the other
245    /// folds of `Iterator` come here).
246    fn fold<B, F>(self, init: B, mut f: F) -> B
247    where
248        F: FnMut(B, Self::Item) -> B,
249    {
250        self.fold_entries(init, |acc, entry| {
251            f(acc, (entry.key.clone(), entry.value.clone()))
252        })
253    }
254}
255
256/// Iterator over HopscotchMap keys (clones `K`).
257pub struct HopscotchKeys<'a, K: 'static, V: 'static, S> {
258    iter: HopscotchIter<'a, K, V, S>,
259}
260
261impl<'a, K, V, S> Iterator for HopscotchKeys<'a, K, V, S>
262where
263    K: Clone,
264{
265    type Item = K;
266
267    fn next(&mut self) -> Option<K> {
268        self.iter.next_entry().map(|entry| entry.key.clone())
269    }
270
271    fn fold<B, F>(self, init: B, mut f: F) -> B
272    where
273        F: FnMut(B, K) -> B,
274    {
275        self.iter
276            .fold_entries(init, |acc, entry| f(acc, entry.key.clone()))
277    }
278}
279
280/// Iterator over HopscotchMap values (clones `V`).
281pub struct HopscotchValues<'a, K: 'static, V: 'static, S> {
282    iter: HopscotchIter<'a, K, V, S>,
283}
284
285impl<'a, K, V, S> Iterator for HopscotchValues<'a, K, V, S>
286where
287    V: Clone,
288{
289    type Item = V;
290
291    fn next(&mut self) -> Option<V> {
292        self.iter.next_entry().map(|entry| entry.value.clone())
293    }
294
295    fn fold<B, F>(self, init: B, mut f: F) -> B
296    where
297        F: FnMut(B, V) -> B,
298    {
299        self.iter
300            .fold_entries(init, |acc, entry| f(acc, entry.value.clone()))
301    }
302}
303
304/// Owned iterator yielding `(K, V)` by value - moves out of the entries, no
305/// clone. Each drained slot is nulled so the table destructor stays a no-op.
306pub struct HopscotchIntoIter<K: 'static, V: 'static> {
307    table: *mut Table<K, V>,
308    walk: Walk<K, V>,
309    guard: kovan::Guard,
310}
311
312impl<K, V> Iterator for HopscotchIntoIter<K, V> {
313    type Item = (K, V);
314
315    fn next(&mut self) -> Option<(K, V)> {
316        let table = unsafe { &*self.table };
317        while let Some((idx, entry)) = self.walk.next(table, &self.guard) {
318            if !entry.is_null() {
319                table.get_bucket(idx).store(Word::free(), Ordering::Relaxed);
320                let k = unsafe { core::ptr::read(&(*entry).key) };
321                let v = unsafe { core::ptr::read(&(*entry).value) };
322                unsafe { free_node(entry) };
323                return Some((k, v));
324            }
325        }
326        None
327    }
328}
329
330impl<K, V> Drop for HopscotchIntoIter<K, V> {
331    fn drop(&mut self) {
332        while self.next().is_some() {}
333        // All slots nulled above; Table::drop frees only the bucket array.
334        unsafe { drop_node(self.table) };
335    }
336}
337
338impl<K, V, S> IntoIterator for HopscotchMap<K, V, S>
339where
340    K: 'static,
341    V: 'static,
342{
343    type Item = (K, V);
344    type IntoIter = HopscotchIntoIter<K, V>;
345
346    fn into_iter(self) -> HopscotchIntoIter<K, V> {
347        let mut me = core::mem::ManuallyDrop::new(self);
348        let guard = pin();
349        let table = me.table.load(Ordering::Relaxed, &guard).as_raw();
350        unsafe { core::ptr::drop_in_place(&mut me.hasher) };
351        // SAFETY: the map is consumed, so the table is this iterator's alone.
352        let walk = Walk::new(unsafe { &*table }, &guard);
353        HopscotchIntoIter { table, walk, guard }
354    }
355}
356
357// `K: Send, V: Send`: an entry an insert replaces is retired, and its destructor may run on
358// another thread.
359impl<K, V, S> core::iter::FromIterator<(K, V)> for HopscotchMap<K, V, S>
360where
361    K: Hash + Eq + Clone + MaybeSend + 'static,
362    V: Clone + MaybeSend + 'static,
363    S: BuildHasher + Default,
364{
365    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
366        let map = Self::with_hasher(S::default());
367        for (k, v) in iter {
368            map.insert(k, v);
369        }
370        map
371    }
372}
373
374// A concurrent walk yields owned clones (`K: Clone`, `V: Clone`) and recognizes a key it already
375// met (`K: Eq`, taken by `iter`), so these bounds are unavoidable here, unlike std's
376// unconstrained `IntoIterator for &HashMap`, which yields borrowed `(&K, &V)` and hashes nothing
377// at this bound-checked level either.
378impl<'a, K, V, S> IntoIterator for &'a HopscotchMap<K, V, S>
379where
380    K: Eq + Clone + 'static,
381    V: Clone + 'static,
382{
383    type Item = (K, V);
384    type IntoIter = HopscotchIter<'a, K, V, S>;
385
386    fn into_iter(self) -> Self::IntoIter {
387        self.iter()
388    }
389}