extern crate alloc;
use super::node::{Node, is_marked, ptr};
use super::table::TableRef;
use super::walk::still_links;
use super::{HashMap, MIN_CAPACITY};
use crate::MaybeSend;
use alloc::vec::Vec;
use core::hash::{BuildHasher, Hash};
use core::sync::atomic::Ordering;
use kovan::{Atomic, drop_node, free_node, pin};
impl<K: 'static, V: 'static, S> HashMap<K, V, S> {
pub fn iter(&self) -> Iter<'_, K, V, S> {
let guard = pin();
let table = self.table_ref(&guard);
Iter {
_map: self,
table,
bucket_idx: 0,
batch: Batch::new(),
pos: 0,
guard,
}
}
pub fn keys(&self) -> Keys<'_, K, V, S> {
Keys { iter: self.iter() }
}
pub fn values(&self) -> Values<'_, K, V, S> {
Values { iter: self.iter() }
}
}
const BATCH_INLINE: usize = 8;
struct Batch<K, V> {
inline: [*const Node<K, V>; BATCH_INLINE],
spill: Vec<*const Node<K, V>>,
len: usize,
}
impl<K, V> Batch<K, V> {
fn new() -> Self {
Self {
inline: [core::ptr::null(); BATCH_INLINE],
spill: Vec::new(),
len: 0,
}
}
#[inline]
fn clear(&mut self) {
self.len = 0;
self.spill.clear();
}
#[inline]
fn push(&mut self, node: *const Node<K, V>) {
if self.len < BATCH_INLINE {
self.inline[self.len] = node;
} else {
self.spill.push(node);
}
self.len += 1;
}
#[inline]
fn get(&self, i: usize) -> Option<*const Node<K, V>> {
match i {
_ if i >= self.len => None,
_ if i < BATCH_INLINE => Some(self.inline[i]),
_ => Some(self.spill[i - BATCH_INLINE]),
}
}
}
pub struct Iter<'a, K: 'static, V: 'static, S> {
_map: &'a HashMap<K, V, S>,
table: TableRef<K, V>,
bucket_idx: usize,
batch: Batch<K, V>,
pos: usize,
guard: kovan::Guard,
}
impl<K, V, S> Iter<'_, K, V, S> {
fn collect(&mut self, b: usize) {
let guard = &self.guard;
'bucket: loop {
self.batch.clear();
let mut prev: &Atomic<Node<K, V>> = self.table.bucket(b);
let mut cur = ptr(prev.load(Ordering::Acquire, guard).as_raw());
let mut run: *mut Node<K, V> = core::ptr::null_mut();
while !cur.is_null() {
let node = unsafe { &*cur };
let next = node.next.load(Ordering::Acquire, guard).as_raw();
if !is_marked(next) {
self.batch.push(cur);
prev = &node.next;
run = core::ptr::null_mut();
} else {
if run.is_null() {
run = cur;
}
if !still_links(prev, run, guard) {
continue 'bucket;
}
}
cur = ptr(next);
}
self.pos = 0;
return;
}
}
}
impl<'a, K, V, S> Iterator for Iter<'a, K, V, S>
where
K: Clone,
V: Clone,
{
type Item = (K, V);
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(node) = self.batch.get(self.pos) {
self.pos += 1;
let node = unsafe { &*node };
return Some((node.key.clone(), node.value.clone()));
}
if self.bucket_idx >= self.table.capacity() {
return None;
}
let b = self.bucket_idx;
self.bucket_idx += 1;
self.collect(b);
}
}
}
pub struct Keys<'a, K: 'static, V: 'static, S> {
iter: Iter<'a, K, V, S>,
}
impl<'a, K, V, S> Iterator for Keys<'a, K, V, S>
where
K: Clone,
V: Clone,
{
type Item = K;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(k, _)| k)
}
}
impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S>
where
K: Clone + 'static,
V: Clone + 'static,
{
type Item = (K, V);
type IntoIter = Iter<'a, K, V, S>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub struct Values<'a, K: 'static, V: 'static, S> {
iter: Iter<'a, K, V, S>,
}
impl<'a, K, V, S> Iterator for Values<'a, K, V, S>
where
K: Clone,
V: Clone,
{
type Item = V;
#[inline]
fn next(&mut self) -> Option<V> {
self.iter.next().map(|(_, v)| v)
}
}
pub struct IntoIter<K: 'static, V: 'static> {
table: TableRef<K, V>,
bucket_idx: usize,
current: *mut Node<K, V>,
guard: kovan::Guard,
}
impl<K, V> Iterator for IntoIter<K, V> {
type Item = (K, V);
fn next(&mut self) -> Option<(K, V)> {
loop {
if !self.current.is_null() {
let node = self.current;
let next = unsafe { (*node).next.load(Ordering::Acquire, &self.guard).as_raw() };
self.current = ptr(next);
if is_marked(next) {
unsafe { drop_node(node) };
continue;
}
let k = unsafe { core::ptr::read(&(*node).key) };
let v = unsafe { core::ptr::read(&(*node).value) };
unsafe { free_node(node) };
return Some((k, v));
}
if self.bucket_idx >= self.table.capacity() {
return None;
}
let bucket = self.table.bucket(self.bucket_idx);
self.bucket_idx += 1;
self.current = ptr(bucket.load(Ordering::Acquire, &self.guard).as_raw());
}
}
}
impl<K, V> Drop for IntoIter<K, V> {
fn drop(&mut self) {
while self.next().is_some() {} unsafe { self.table.free_array_only() };
}
}
impl<K, V, S> IntoIterator for HashMap<K, V, S>
where
K: 'static,
V: 'static,
{
type Item = (K, V);
type IntoIter = IntoIter<K, V>;
fn into_iter(self) -> IntoIter<K, V> {
let mut me = core::mem::ManuallyDrop::new(self);
let guard = pin();
let table = TableRef::<K, V>::from_raw(me.table.load(Ordering::Relaxed, &guard).as_raw());
unsafe {
core::ptr::drop_in_place(&mut me.hasher);
core::ptr::drop_in_place(&mut me.resizing);
}
IntoIter {
table,
bucket_idx: 0,
current: core::ptr::null_mut(),
guard,
}
}
}
impl<K, V, S> core::iter::FromIterator<(K, V)> for HashMap<K, V, S>
where
K: Hash + Eq + Clone + MaybeSend + 'static,
V: Clone + MaybeSend + 'static,
S: BuildHasher + Default,
{
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
let map = Self::with_capacity_and_hasher(MIN_CAPACITY, S::default());
for (k, v) in iter {
map.insert(k, v);
}
map
}
}