extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use alloc::boxed::Box;
use core::borrow::Borrow;
use core::hash::{BuildHasher, Hash};
use core::sync::atomic::{AtomicBool, AtomicIsize, Ordering, fence};
use foldhash::fast::FixedState;
use kovan::{Atomic, RetiredNode, Shared, pin, retire};
#[inline(always)]
fn resize_spin_hint() {
#[cfg(feature = "shuttle")]
{
shuttle::hint::spin_loop();
}
#[cfg(not(feature = "shuttle"))]
{
core::hint::spin_loop();
}
}
const DEFAULT_CAPACITY: usize = 524_288;
const MIN_CAPACITY: usize = 64;
struct Backoff {
step: u32,
}
impl Backoff {
#[inline(always)]
fn new() -> Self {
Self { step: 0 }
}
#[inline(always)]
fn spin(&mut self) {
for _ in 0..(1 << self.step.min(6)) {
core::hint::spin_loop();
}
if self.step <= 6 {
self.step += 1;
}
}
}
#[repr(C)]
struct Node<K, V> {
retired: RetiredNode,
hash: u64,
key: K,
value: V,
next: Atomic<Node<K, V>>,
}
unsafe impl<K: Send, V: Send> Send for Node<K, V> {}
unsafe impl<K: Send + Sync, V: Send + Sync> Sync for Node<K, V> {}
#[inline(always)]
fn tagged<K, V>(p: *mut Node<K, V>) -> *mut Node<K, V> {
(p as usize | 1) as *mut Node<K, V>
}
#[inline(always)]
fn untag<K, V>(p: *mut Node<K, V>) -> *mut Node<K, V> {
(p as usize & !1) as *mut Node<K, V>
}
#[inline(always)]
fn is_tagged<K, V>(p: *const Node<K, V>) -> bool {
(p as usize) & 1 != 0
}
#[repr(C)]
struct TableHeader {
mask: usize,
capacity: usize,
proxy: usize,
}
struct TableRef<K: 'static, V: 'static> {
header: *mut TableHeader,
_marker: core::marker::PhantomData<(K, V)>,
}
impl<K, V> Clone for TableRef<K, V> {
fn clone(&self) -> Self {
*self
}
}
impl<K, V> Copy for TableRef<K, V> {}
impl<K: 'static, V: 'static> TableRef<K, V> {
#[inline(always)]
fn from_raw(header: *mut TableHeader) -> Self {
Self {
header,
_marker: core::marker::PhantomData,
}
}
fn layout(capacity: usize) -> (core::alloc::Layout, usize) {
let header = core::alloc::Layout::new::<TableHeader>();
let buckets = core::alloc::Layout::array::<Atomic<Node<K, V>>>(capacity)
.expect("bucket array layout overflow");
let (layout, offset) = header.extend(buckets).expect("table layout overflow");
(layout.pad_to_align(), offset)
}
fn alloc(capacity: usize) -> Self {
let capacity = capacity.next_power_of_two().max(MIN_CAPACITY);
let (layout, _) = Self::layout(capacity);
let header = unsafe { alloc::alloc::alloc_zeroed(layout) as *mut TableHeader };
assert!(!header.is_null(), "table allocation failed");
unsafe {
(*header).mask = capacity - 1;
(*header).capacity = capacity;
}
let table = Self::from_raw(header);
let proxy = Box::into_raw(Box::new(TableProxy {
retired: RetiredNode::new(),
table,
}));
unsafe { (*header).proxy = proxy as usize };
table
}
#[inline(always)]
fn take_proxy(self) -> *mut TableProxy<K, V> {
let proxy = unsafe { (*self.header).proxy };
assert_ne!(proxy, 0, "kovan-map: table proxy already taken");
unsafe { (*self.header).proxy = 0 };
proxy as *mut TableProxy<K, V>
}
#[inline(always)]
unsafe fn drop_unused_proxy(self) {
let proxy = unsafe { (*self.header).proxy };
if proxy != 0 {
unsafe {
(*self.header).proxy = 0;
alloc::alloc::dealloc(
proxy as *mut u8,
core::alloc::Layout::new::<TableProxy<K, V>>(),
);
}
}
}
unsafe fn free_array_only(self) {
unsafe { self.drop_unused_proxy() };
let capacity = unsafe { (*self.header).capacity };
let (layout, _) = Self::layout(capacity);
unsafe { alloc::alloc::dealloc(self.header as *mut u8, layout) };
}
unsafe fn free(self) {
unsafe { self.drop_unused_proxy() };
let capacity = unsafe { (*self.header).capacity };
let guard = pin();
for i in 0..capacity {
let mut current = self.bucket(i).load(Ordering::Relaxed, &guard).as_raw();
while !current.is_null() {
unsafe {
let next = (*current).next.load(Ordering::Relaxed, &guard).as_raw();
if !is_tagged(next) {
drop(Box::from_raw(current));
}
current = untag(next);
}
}
}
drop(guard);
let (layout, _) = Self::layout(capacity);
unsafe { alloc::alloc::dealloc(self.header as *mut u8, layout) };
}
#[inline(always)]
fn as_raw(self) -> *mut TableHeader {
self.header
}
#[inline(always)]
fn capacity(self) -> usize {
unsafe { (*self.header).capacity }
}
#[inline(always)]
fn buckets(self) -> *const Atomic<Node<K, V>> {
let (_, offset) = Self::layout_offset();
unsafe { (self.header as *const u8).add(offset) as *const Atomic<Node<K, V>> }
}
#[inline(always)]
fn layout_offset() -> ((), usize) {
let header = core::alloc::Layout::new::<TableHeader>();
let one = core::alloc::Layout::new::<Atomic<Node<K, V>>>();
let (_, offset) = header.extend(one).expect("layout");
((), offset)
}
#[inline(always)]
fn bucket_index(self, hash: u64) -> usize {
(hash as usize) & unsafe { (*self.header).mask }
}
#[inline(always)]
fn bucket(self, idx: usize) -> &'static Atomic<Node<K, V>> {
unsafe { &*self.buckets().add(idx) }
}
}
#[repr(C)]
struct TableProxy<K: 'static, V: 'static> {
retired: RetiredNode,
table: TableRef<K, V>,
}
unsafe impl<K: Send, V: Send> Send for TableProxy<K, V> {}
unsafe impl<K: Send + Sync, V: Send + Sync> Sync for TableProxy<K, V> {}
impl<K, V> Drop for TableProxy<K, V> {
fn drop(&mut self) {
unsafe { self.table.free() };
}
}
pub struct HashMap<K: 'static, V: 'static, S = FixedState> {
table: Atomic<TableHeader>,
count: AtomicIsize,
resizing: AtomicBool,
floor: usize,
hasher: S,
_marker: core::marker::PhantomData<(K, V)>,
}
#[cfg(feature = "std")]
impl<K, V> HashMap<K, V, FixedState>
where
K: Hash + Eq + Clone + 'static,
V: Clone + 'static,
{
pub fn new() -> Self {
Self::with_hasher(FixedState::default())
}
pub fn with_capacity(capacity: usize) -> Self {
Self::with_capacity_and_hasher(capacity, FixedState::default())
}
}
impl<K, V, S> HashMap<K, V, S>
where
K: Hash + Eq + Clone + 'static,
V: Clone + 'static,
S: BuildHasher,
{
pub fn with_hasher(hasher: S) -> Self {
Self::with_capacity_and_hasher(DEFAULT_CAPACITY, hasher)
}
pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> Self {
let table = TableRef::<K, V>::alloc(capacity);
let floor = table.capacity();
Self {
table: Atomic::new(table.as_raw()),
count: AtomicIsize::new(0),
resizing: AtomicBool::new(false),
floor,
hasher,
_marker: core::marker::PhantomData,
}
}
pub fn capacity(&self) -> usize {
let guard = pin();
let table = TableRef::<K, V>::from_raw(self.table.load(Ordering::Acquire, &guard).as_raw());
table.capacity()
}
#[inline]
fn wait_for_resize(&self) {
while self.resizing.load(Ordering::Acquire) {
resize_spin_hint();
}
}
pub fn get<Q>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.hasher.hash_one(key);
let guard = pin();
let table = TableRef::<K, V>::from_raw(self.table.load(Ordering::Acquire, &guard).as_raw());
let bucket = table.bucket(table.bucket_index(hash));
let mut current = bucket.load(Ordering::Acquire, &guard).as_raw();
while !current.is_null() {
unsafe {
let node = &*current;
if node.hash == hash && node.key.borrow() == key {
return Some(node.value.clone());
}
current = untag(node.next.load(Ordering::Acquire, &guard).as_raw());
}
}
None
}
pub fn contains_key<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.get(key).is_some()
}
pub fn insert(&self, key: K, value: V) -> Option<V> {
let hash = self.hasher.hash_one(&key);
let mut backoff = Backoff::new();
let mut counted = false;
let mut result: Option<Option<V>> = None;
'outer: loop {
self.wait_for_resize();
let guard = pin();
let table_raw = self.table.load(Ordering::Acquire, &guard).as_raw();
let table = TableRef::<K, V>::from_raw(table_raw);
if self.resizing.load(Ordering::Acquire) {
continue;
}
let bucket = table.bucket(table.bucket_index(hash));
let mut prev_link = bucket;
let mut current = prev_link.load(Ordering::Acquire, &guard).as_raw();
while !current.is_null() {
unsafe {
let node = &*current;
let next = node.next.load(Ordering::Acquire, &guard).as_raw();
if is_tagged(next) {
if prev_link
.compare_exchange(
Shared::from_raw(current),
Shared::from_raw(untag(next)),
Ordering::AcqRel,
Ordering::Relaxed,
&guard,
)
.is_err()
{
backoff.spin();
continue 'outer;
}
current = untag(next);
continue;
}
if node.hash == hash && node.key == key {
let old_value = node.value.clone();
if node
.next
.compare_exchange(
Shared::from_raw(next),
Shared::from_raw(tagged(next)),
Ordering::AcqRel,
Ordering::Relaxed,
&guard,
)
.is_err()
{
backoff.spin();
continue 'outer;
}
let new_node = Box::into_raw(Box::new(Node {
retired: RetiredNode::new(),
hash,
key: key.clone(),
value: value.clone(),
next: Atomic::new(next),
}));
let swapped = prev_link
.compare_exchange(
Shared::from_raw(current),
Shared::from_raw(new_node),
Ordering::AcqRel,
Ordering::Relaxed,
&guard,
)
.is_ok();
retire(current);
if result.is_none() {
result = Some(Some(old_value));
}
if !swapped {
drop(Box::from_raw(new_node));
backoff.spin();
continue 'outer;
}
fence(Ordering::SeqCst);
if self.resizing.load(Ordering::SeqCst)
|| self.table.load(Ordering::SeqCst, &guard).as_raw() != table_raw
{
continue 'outer;
}
return result.unwrap();
}
prev_link = &node.next;
current = next;
}
}
let new_node_ptr = Box::into_raw(Box::new(Node {
retired: RetiredNode::new(),
hash,
key: key.clone(),
value: value.clone(),
next: Atomic::null(),
}));
match prev_link.compare_exchange(
unsafe { Shared::from_raw(core::ptr::null_mut()) },
unsafe { Shared::from_raw(new_node_ptr) },
Ordering::Release,
Ordering::Relaxed,
&guard,
) {
Ok(_) => {
if !counted {
counted = true;
self.count.fetch_add(1, Ordering::Relaxed);
}
if result.is_none() {
result = Some(None);
}
fence(Ordering::SeqCst);
if self.resizing.load(Ordering::SeqCst)
|| self.table.load(Ordering::SeqCst, &guard).as_raw() != table_raw
{
continue 'outer;
}
let new_count = self.count.load(Ordering::Relaxed).max(0) as usize;
let capacity = table.capacity();
if 4 * new_count > 3 * capacity {
drop(guard);
self.try_resize(capacity * 2);
}
return result.unwrap();
}
Err(_) => {
unsafe {
drop(Box::from_raw(new_node_ptr));
}
backoff.spin();
continue 'outer;
}
}
}
}
pub fn insert_if_absent(&self, key: K, value: V) -> Option<V> {
let hash = self.hasher.hash_one(&key);
let mut backoff = Backoff::new();
let mut counted = false;
'outer: loop {
self.wait_for_resize();
let guard = pin();
let table_raw = self.table.load(Ordering::Acquire, &guard).as_raw();
let table = TableRef::<K, V>::from_raw(table_raw);
if self.resizing.load(Ordering::Acquire) {
continue;
}
let bucket = table.bucket(table.bucket_index(hash));
let mut prev_link = bucket;
let mut current = prev_link.load(Ordering::Acquire, &guard).as_raw();
while !current.is_null() {
unsafe {
let node = &*current;
let next = node.next.load(Ordering::Acquire, &guard).as_raw();
if is_tagged(next) {
if prev_link
.compare_exchange(
Shared::from_raw(current),
Shared::from_raw(untag(next)),
Ordering::AcqRel,
Ordering::Relaxed,
&guard,
)
.is_err()
{
backoff.spin();
continue 'outer;
}
current = untag(next);
continue;
}
if node.hash == hash && node.key == key {
return Some(node.value.clone());
}
prev_link = &node.next;
current = next;
}
}
let new_node_ptr = Box::into_raw(Box::new(Node {
retired: RetiredNode::new(),
hash,
key: key.clone(),
value: value.clone(),
next: Atomic::null(),
}));
match prev_link.compare_exchange(
unsafe { Shared::from_raw(core::ptr::null_mut()) },
unsafe { Shared::from_raw(new_node_ptr) },
Ordering::Release,
Ordering::Relaxed,
&guard,
) {
Ok(_) => {
if !counted {
counted = true;
self.count.fetch_add(1, Ordering::Relaxed);
}
fence(Ordering::SeqCst);
if self.resizing.load(Ordering::SeqCst)
|| self.table.load(Ordering::SeqCst, &guard).as_raw() != table_raw
{
continue 'outer;
}
let new_count = self.count.load(Ordering::Relaxed).max(0) as usize;
let capacity = table.capacity();
if 4 * new_count > 3 * capacity {
drop(guard);
self.try_resize(capacity * 2);
}
return None;
}
Err(actual_val) => {
unsafe {
let appended_ptr = actual_val.as_raw();
drop(Box::from_raw(new_node_ptr));
if !is_tagged(appended_ptr) && !appended_ptr.is_null() {
let appended = &*appended_ptr;
if appended.hash == hash && appended.key == key {
return Some(appended.value.clone());
}
}
}
backoff.spin();
continue 'outer;
}
}
}
}
pub fn get_or_insert(&self, key: K, value: V) -> V {
match self.insert_if_absent(key, value.clone()) {
Some(existing) => existing,
None => value,
}
}
pub fn remove<Q>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.hasher.hash_one(key);
let mut backoff = Backoff::new();
let mut result: Option<V> = None;
'outer: loop {
self.wait_for_resize();
let guard = pin();
let table_raw = self.table.load(Ordering::Acquire, &guard).as_raw();
let table = TableRef::<K, V>::from_raw(table_raw);
if self.resizing.load(Ordering::Acquire) {
continue;
}
let bucket = table.bucket(table.bucket_index(hash));
let mut prev_link = bucket;
let mut current = prev_link.load(Ordering::Acquire, &guard).as_raw();
while !current.is_null() {
unsafe {
let node = &*current;
let next = node.next.load(Ordering::Acquire, &guard).as_raw();
if is_tagged(next) {
if prev_link
.compare_exchange(
Shared::from_raw(current),
Shared::from_raw(untag(next)),
Ordering::AcqRel,
Ordering::Relaxed,
&guard,
)
.is_err()
{
backoff.spin();
continue 'outer;
}
current = untag(next);
continue;
}
if node.hash == hash && node.key.borrow() == key {
let old_value = node.value.clone();
if node
.next
.compare_exchange(
Shared::from_raw(next),
Shared::from_raw(tagged(next)),
Ordering::AcqRel,
Ordering::Relaxed,
&guard,
)
.is_err()
{
backoff.spin();
continue 'outer;
}
let _ = prev_link.compare_exchange(
Shared::from_raw(current),
Shared::from_raw(next),
Ordering::AcqRel,
Ordering::Relaxed,
&guard,
);
retire(current);
if result.is_none() {
result = Some(old_value);
}
let new_count =
(self.count.fetch_sub(1, Ordering::Relaxed) - 1).max(0) as usize;
let shrink_to = (4 * new_count < table.capacity()
&& table.capacity() > self.floor)
.then_some(table.capacity() / 2);
fence(Ordering::SeqCst);
if self.resizing.load(Ordering::SeqCst)
|| self.table.load(Ordering::SeqCst, &guard).as_raw() != table_raw
{
continue 'outer;
}
if let Some(cap) = shrink_to {
drop(guard);
self.try_resize(cap);
}
return result;
}
prev_link = &node.next;
current = next;
}
}
return result;
}
}
pub fn force_remove<Q>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let mut newest = None;
loop {
match self.remove(key) {
Some(v) => {
if newest.is_none() {
newest = Some(v);
}
}
None => return newest,
}
}
}
pub fn clear(&self) {
while self
.resizing
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
resize_spin_hint();
}
let guard = pin();
let table = TableRef::<K, V>::from_raw(self.table.load(Ordering::Acquire, &guard).as_raw());
for i in 0..table.capacity() {
let bucket = table.bucket(i);
loop {
let head = bucket.load(Ordering::Acquire, &guard);
if head.is_null() {
break;
}
match bucket.compare_exchange(
head,
unsafe { Shared::from_raw(core::ptr::null_mut()) },
Ordering::Release,
Ordering::Relaxed,
&guard,
) {
Ok(_) => {
unsafe {
let mut current = head.as_raw();
while !current.is_null() {
let next = (*current).next.load(Ordering::Relaxed, &guard).as_raw();
if !is_tagged(next) {
retire(current);
}
current = untag(next);
}
}
break;
}
Err(_) => {
continue;
}
}
}
}
self.count.store(0, Ordering::Release);
self.resizing.store(false, Ordering::Release);
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> usize {
self.count.load(Ordering::Relaxed).max(0) as usize
}
fn try_resize(&self, new_capacity: usize) {
if self
.resizing
.compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed)
.is_err()
{
return;
}
fence(Ordering::SeqCst);
let new_capacity = new_capacity
.next_power_of_two()
.max(MIN_CAPACITY)
.max(self.floor);
let guard = pin();
let old_raw = self.table.load(Ordering::Acquire, &guard).as_raw();
let old_table = TableRef::<K, V>::from_raw(old_raw);
if old_table.capacity() == new_capacity {
self.resizing.store(false, Ordering::Release);
return;
}
let new_table = TableRef::<K, V>::alloc(new_capacity);
for i in 0..old_table.capacity() {
let bucket = old_table.bucket(i);
let mut current = bucket.load(Ordering::Acquire, &guard).as_raw();
while !current.is_null() {
let node = unsafe { &*current };
let next = node.next.load(Ordering::Acquire, &guard).as_raw();
if !is_tagged(next) {
let dst = new_table.bucket(new_table.bucket_index(node.hash));
let head = dst.load(Ordering::Relaxed, &guard);
let clone = Box::into_raw(Box::new(Node {
retired: RetiredNode::new(),
hash: node.hash,
key: node.key.clone(),
value: node.value.clone(),
next: Atomic::new(head.as_raw()),
}));
dst.store(unsafe { Shared::from_raw(clone) }, Ordering::Relaxed);
}
current = untag(next);
}
}
match self.table.compare_exchange(
unsafe { Shared::from_raw(old_raw) },
unsafe { Shared::from_raw(new_table.as_raw()) },
Ordering::Release,
Ordering::Relaxed,
&guard,
) {
Ok(_) => {
let proxy = old_table.take_proxy();
unsafe { retire(proxy) };
}
Err(_) => {
unsafe { new_table.free() };
}
}
self.resizing.store(false, Ordering::Release);
}
pub fn iter(&self) -> Iter<'_, K, V, S> {
let guard = pin();
let table = TableRef::<K, V>::from_raw(self.table.load(Ordering::Acquire, &guard).as_raw());
Iter {
_map: self,
table,
bucket_idx: 0,
current: core::ptr::null(),
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() }
}
pub fn extend<I: IntoIterator<Item = (K, V)>>(&self, iter: I) {
for (k, v) in iter {
self.insert(k, v);
}
}
pub fn hasher(&self) -> &S {
&self.hasher
}
}
pub struct Iter<'a, K: 'static, V: 'static, S> {
_map: &'a HashMap<K, V, S>,
table: TableRef<K, V>,
bucket_idx: usize,
current: *const Node<K, V>,
guard: kovan::Guard,
}
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 !self.current.is_null() {
unsafe {
let node = &*self.current;
let next = node.next.load(Ordering::Acquire, &self.guard).as_raw();
self.current = untag(next);
if is_tagged(next) {
continue;
}
return Some((node.key.clone(), node.value.clone()));
}
}
let table = self.table;
if self.bucket_idx >= table.capacity() {
return None;
}
let bucket = table.bucket(self.bucket_idx);
self.bucket_idx += 1;
self.current = bucket.load(Ordering::Acquire, &self.guard).as_raw();
}
}
}
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: Hash + Eq + Clone + 'static,
V: Clone + 'static,
S: BuildHasher,
{
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 = untag(next);
if is_tagged(next) {
continue; }
let k = unsafe { core::ptr::read(&(*node).key) };
let v = unsafe { core::ptr::read(&(*node).value) };
unsafe {
alloc::alloc::dealloc(
node as *mut u8,
core::alloc::Layout::new::<Node<K, V>>(),
);
}
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 = 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) };
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 + Send + 'static,
V: Clone + Send + '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
}
}
#[cfg(feature = "std")]
impl<K, V> Default for HashMap<K, V, FixedState>
where
K: Hash + Eq + Clone + 'static,
V: Clone + 'static,
{
fn default() -> Self {
Self::new()
}
}
unsafe impl<K: Send, V: Send, S: Send> Send for HashMap<K, V, S> {}
unsafe impl<K: Send + Sync, V: Send + Sync, S: Send + Sync> Sync for HashMap<K, V, S> {}
impl<K: 'static, V: 'static, S> Drop for HashMap<K, V, S> {
fn drop(&mut self) {
let guard = pin();
let table = TableRef::<K, V>::from_raw(self.table.load(Ordering::Relaxed, &guard).as_raw());
drop(guard);
unsafe { table.free() };
kovan::flush();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_insert_and_get() {
let map = HashMap::new();
assert_eq!(map.insert(1, 100), None);
assert_eq!(map.get(&1), Some(100));
assert_eq!(map.get(&2), None);
}
#[test]
fn test_insert_replace() {
let map = HashMap::new();
assert_eq!(map.insert(1, 100), None);
assert_eq!(map.insert(1, 200), Some(100));
assert_eq!(map.get(&1), Some(200));
}
#[test]
fn test_grow() {
let map = HashMap::with_capacity(64);
assert_eq!(map.capacity(), 64);
for i in 0..1000u64 {
map.insert(i, i * 2);
}
assert!(map.capacity() > 64, "map should have grown");
for i in 0..1000u64 {
assert_eq!(map.get(&i), Some(i * 2));
}
assert_eq!(map.len(), 1000);
}
#[test]
fn test_shrink() {
let map = HashMap::with_capacity(64);
for i in 0..1000u64 {
map.insert(i, i);
}
let grown = map.capacity();
assert!(grown > 64);
for i in 0..1000u64 {
map.remove(&i);
}
assert!(
map.capacity() < grown,
"map should have shrunk (capacity {} -> {})",
grown,
map.capacity()
);
assert!(map.capacity() >= 64, "never below the initial capacity");
assert_eq!(map.len(), 0);
}
#[test]
fn test_no_shrink_below_floor() {
let map = HashMap::with_capacity(4096);
for i in 0..100u64 {
map.insert(i, i);
}
for i in 0..100u64 {
map.remove(&i);
}
assert_eq!(map.capacity(), 4096, "floor preserves sizing intent");
}
#[test]
fn test_concurrent_inserts() {
use alloc::sync::Arc;
extern crate std;
use std::thread;
let map = Arc::new(HashMap::new());
let mut handles = alloc::vec::Vec::new();
for thread_id in 0..4 {
let map_clone = Arc::clone(&map);
let handle = thread::spawn(move || {
for i in 0..1000 {
let key = thread_id * 1000 + i;
map_clone.insert(key, key * 2);
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
for thread_id in 0..4 {
for i in 0..1000 {
let key = thread_id * 1000 + i;
assert_eq!(map.get(&key), Some(key * 2));
}
}
}
#[test]
fn test_concurrent_grow() {
use alloc::sync::Arc;
extern crate std;
use std::thread;
let map = Arc::new(HashMap::with_capacity(64));
let mut handles = alloc::vec::Vec::new();
for thread_id in 0..8u64 {
let map_clone = Arc::clone(&map);
handles.push(thread::spawn(move || {
for i in 0..2000u64 {
let key = thread_id * 10_000 + i;
map_clone.insert(key, key);
}
}));
}
for handle in handles {
handle.join().unwrap();
}
for thread_id in 0..8u64 {
for i in 0..2000u64 {
let key = thread_id * 10_000 + i;
assert_eq!(map.get(&key), Some(key), "lost key {key} during growth");
}
}
assert!(map.capacity() >= 16_000);
}
}