extern crate alloc;
use super::table::{Entry, Table, Walk, Word};
use super::{HopscotchMap, NEIGHBORHOOD_SIZE};
use core::hash::{BuildHasher, Hash};
use core::marker::PhantomData;
use core::sync::atomic::Ordering;
use kovan::{drop_node, free_node, pin};
impl<K: Eq + 'static, V: 'static, S> HopscotchMap<K, V, S> {
pub fn iter(&self) -> HopscotchIter<'_, K, V, S> {
let guard = pin();
let table = self.table.load(Ordering::Acquire, &guard).as_raw();
let current = unsafe { &*table };
let (slots, mask) = (current.buckets.len(), current.home_mask());
HopscotchIter {
table,
slots,
mask,
bucket_idx: 0,
seen: Seen::new(<K as PartialEq>::eq),
guard,
_map: PhantomData,
}
}
pub fn keys(&self) -> HopscotchKeys<'_, K, V, S> {
HopscotchKeys { iter: self.iter() }
}
pub fn values(&self) -> HopscotchValues<'_, K, V, S> {
HopscotchValues { iter: self.iter() }
}
}
pub struct HopscotchIter<'a, K: 'static, V: 'static, S> {
table: *const Table<K, V>,
slots: usize,
mask: usize,
bucket_idx: usize,
seen: Seen<K, V>,
guard: kovan::Guard,
_map: PhantomData<&'a HopscotchMap<K, V, S>>,
}
const GROUP: usize = u64::BITS as usize;
struct Seen<K, V> {
recent: [*const Entry<K, V>; NEIGHBORHOOD_SIZE],
homes: [u64; NEIGHBORHOOD_SIZE],
same_key: fn(&K, &K) -> bool,
}
impl<K, V> Seen<K, V> {
fn new(same_key: fn(&K, &K) -> bool) -> Self {
Self {
recent: [core::ptr::null(); NEIGHBORHOOD_SIZE],
homes: [0; NEIGHBORHOOD_SIZE],
same_key,
}
}
#[inline(always)]
fn first_meeting(&mut self, entry: &Entry<K, V>, idx: usize, mask: usize) -> bool {
self.recent[idx % NEIGHBORHOOD_SIZE] = entry;
let home = (entry.hash as usize) & mask;
let cell = &mut self.homes[home % NEIGHBORHOOD_SIZE];
let bit = 1u32 << (entry.hash >> 59);
let filter =
core::hint::select_unpredictable((*cell >> 32) as u32 == home as u32, *cell as u32, 0);
*cell = (u64::from(home as u32) << 32) | u64::from(filter | bit);
filter & bit == 0 || !self.met_before(home, idx, entry)
}
#[cold]
#[inline(never)]
fn met_before(&self, home: usize, idx: usize, entry: &Entry<K, V>) -> bool {
let lowest = home.max(idx.saturating_sub(NEIGHBORHOOD_SIZE - 1));
(lowest..idx).any(|seen_idx| {
let seen = self.recent[seen_idx % NEIGHBORHOOD_SIZE];
unsafe { seen.as_ref() }.is_some_and(|seen| {
seen.hash == entry.hash
&& (core::ptr::eq(seen, entry) || (self.same_key)(&seen.key, &entry.key))
})
})
}
}
impl<K, V, S> HopscotchIter<'_, K, V, S> {
#[inline(always)]
fn next_entry(&mut self) -> Option<&Entry<K, V>> {
let table = unsafe { &*self.table };
while self.bucket_idx < self.slots {
let idx = self.bucket_idx;
self.bucket_idx += 1;
if idx.is_multiple_of(GROUP) {
for bucket in &table.buckets[idx..self.slots.min(idx + GROUP)] {
table.prefetch_entry(bucket.load(Ordering::Relaxed, &self.guard).ptr());
}
}
let entry = table
.get_bucket(idx)
.load(Ordering::Acquire, &self.guard)
.entry();
if let Some(entry) = entry
&& self.seen.first_meeting(entry, idx, self.mask)
{
return Some(entry);
}
}
None
}
#[inline(always)]
fn fold_entries<B>(self, init: B, mut f: impl FnMut(B, &Entry<K, V>) -> B) -> B {
let Self {
table,
slots,
mask,
bucket_idx,
mut seen,
guard,
..
} = self;
let table = unsafe { &*table };
let mut acc = init;
let mut group = [core::ptr::null::<Entry<K, V>>(); GROUP];
let mut first = bucket_idx;
for buckets in table.buckets[bucket_idx..slots].chunks(GROUP) {
let mut occupied = 0u64;
for (offset, (bucket, cell)) in buckets.iter().zip(&mut group).enumerate() {
let entry: *const Entry<K, V> = bucket.load(Ordering::Acquire, &guard).ptr();
*cell = entry;
occupied |= u64::from(!entry.is_null()) << offset;
table.prefetch_entry(entry);
}
while occupied != 0 {
let offset = occupied.trailing_zeros() as usize;
occupied &= occupied - 1;
let entry = unsafe { &*group[offset] };
if seen.first_meeting(entry, first + offset, mask) {
acc = f(acc, entry);
}
}
first += buckets.len();
}
acc
}
}
impl<'a, K, V, S> Iterator for HopscotchIter<'a, K, V, S>
where
K: Clone,
V: Clone,
{
type Item = (K, V);
fn next(&mut self) -> Option<Self::Item> {
self.next_entry()
.map(|entry| (entry.key.clone(), entry.value.clone()))
}
fn fold<B, F>(self, init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
self.fold_entries(init, |acc, entry| {
f(acc, (entry.key.clone(), entry.value.clone()))
})
}
}
pub struct HopscotchKeys<'a, K: 'static, V: 'static, S> {
iter: HopscotchIter<'a, K, V, S>,
}
impl<'a, K, V, S> Iterator for HopscotchKeys<'a, K, V, S>
where
K: Clone,
{
type Item = K;
fn next(&mut self) -> Option<K> {
self.iter.next_entry().map(|entry| entry.key.clone())
}
fn fold<B, F>(self, init: B, mut f: F) -> B
where
F: FnMut(B, K) -> B,
{
self.iter
.fold_entries(init, |acc, entry| f(acc, entry.key.clone()))
}
}
pub struct HopscotchValues<'a, K: 'static, V: 'static, S> {
iter: HopscotchIter<'a, K, V, S>,
}
impl<'a, K, V, S> Iterator for HopscotchValues<'a, K, V, S>
where
V: Clone,
{
type Item = V;
fn next(&mut self) -> Option<V> {
self.iter.next_entry().map(|entry| entry.value.clone())
}
fn fold<B, F>(self, init: B, mut f: F) -> B
where
F: FnMut(B, V) -> B,
{
self.iter
.fold_entries(init, |acc, entry| f(acc, entry.value.clone()))
}
}
pub struct HopscotchIntoIter<K: 'static, V: 'static> {
table: *mut Table<K, V>,
walk: Walk<K, V>,
guard: kovan::Guard,
}
impl<K, V> Iterator for HopscotchIntoIter<K, V> {
type Item = (K, V);
fn next(&mut self) -> Option<(K, V)> {
let table = unsafe { &*self.table };
while let Some((idx, entry)) = self.walk.next(table, &self.guard) {
if !entry.is_null() {
table.get_bucket(idx).store(Word::free(), Ordering::Relaxed);
let k = unsafe { core::ptr::read(&(*entry).key) };
let v = unsafe { core::ptr::read(&(*entry).value) };
unsafe { free_node(entry) };
return Some((k, v));
}
}
None
}
}
impl<K, V> Drop for HopscotchIntoIter<K, V> {
fn drop(&mut self) {
while self.next().is_some() {}
unsafe { drop_node(self.table) };
}
}
impl<K, V, S> IntoIterator for HopscotchMap<K, V, S>
where
K: 'static,
V: 'static,
{
type Item = (K, V);
type IntoIter = HopscotchIntoIter<K, V>;
fn into_iter(self) -> HopscotchIntoIter<K, V> {
let mut me = core::mem::ManuallyDrop::new(self);
let guard = pin();
let table = me.table.load(Ordering::Relaxed, &guard).as_raw();
unsafe { core::ptr::drop_in_place(&mut me.hasher) };
let walk = Walk::new(unsafe { &*table }, &guard);
HopscotchIntoIter { table, walk, guard }
}
}
impl<K, V, S> core::iter::FromIterator<(K, V)> for HopscotchMap<K, V, S>
where
K: Hash + Eq + Clone + Send + 'static,
V: Clone + Send + 'static,
S: BuildHasher + Default,
{
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
let map = Self::with_hasher(S::default());
for (k, v) in iter {
map.insert(k, v);
}
map
}
}
impl<'a, K, V, S> IntoIterator for &'a HopscotchMap<K, V, S>
where
K: Eq + Clone + 'static,
V: Clone + 'static,
{
type Item = (K, V);
type IntoIter = HopscotchIter<'a, K, V, S>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}