extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use crate::sync::AtomicBool;
use core::borrow::Borrow;
use core::hash::{BuildHasher, Hash};
use core::sync::atomic::Ordering;
use foldhash::fast::FixedState;
use kovan::{Atomic, Guard, NodeBox, pin, retire_node};
use node::{MARK, Node, with, word};
use table::{TableHeader, TableRef};
use walk::Found;
pub use iter::{IntoIter, Iter, Keys, Values};
mod conditional;
mod iter;
mod node;
mod resize;
mod std_traits;
mod table;
mod walk;
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) {
#[cfg(feature = "shuttle")]
crate::sync::spin_hint();
#[cfg(not(feature = "shuttle"))]
for _ in 0..(1 << self.step.min(6)) {
core::hint::spin_loop();
}
if self.step <= 6 {
self.step += 1;
}
}
}
pub struct HashMap<K: 'static, V: 'static, S = FixedState> {
table: Atomic<TableHeader>,
resizing: AtomicBool,
floor: usize,
hasher: S,
_marker: core::marker::PhantomData<(K, V)>,
}
enum Pending<K, V> {
Parts { key: K, value: V },
Built(NodeBox<Node<K, V>>),
}
impl<K, V> Pending<K, V> {
#[inline(always)]
fn key(&self) -> &K {
match self {
Self::Parts { key, .. } => key,
Self::Built(node) => &node.key,
}
}
#[inline]
fn into_node(self, hash: u64, next: *mut Node<K, V>) -> *mut Node<K, V> {
let node = match self {
Self::Parts { key, value } => NodeBox::new(Node::new(hash, key, value)),
Self::Built(node) => node,
};
node.next.store(word(next), Ordering::Relaxed);
NodeBox::into_raw(node)
}
#[inline]
fn back(node: *mut Node<K, V>) -> Self {
Self::Built(unsafe { NodeBox::from_raw(node) })
}
}
enum Claim<R, V> {
Inserted(R),
Present(V),
}
impl<K: 'static, V: 'static, S> HashMap<K, V, S> {
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()),
resizing: AtomicBool::new(false),
floor,
hasher,
_marker: core::marker::PhantomData,
}
}
pub fn capacity(&self) -> usize {
let guard = pin();
self.table_ref(&guard).capacity()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> usize {
let guard = pin();
self.table_ref(&guard)
.count()
.load(Ordering::Relaxed)
.max(0) as usize
}
pub fn hasher(&self) -> &S {
&self.hasher
}
}
#[cfg(feature = "std")]
impl<K: 'static, V: 'static> HashMap<K, V, FixedState> {
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 + Send + 'static,
V: Clone + Send + 'static,
S: BuildHasher,
{
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();
self.lookup(hash, key, &guard)
.map(|node| node.value.clone())
}
pub fn contains_key<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.hasher.hash_one(key);
let guard = pin();
self.lookup(hash, key, &guard).is_some()
}
pub fn insert(&self, key: K, value: V) -> Option<V> {
let hash = self.hasher.hash_one(&key);
let mut pending = Pending::Parts { key, value };
let mut backoff = Backoff::new();
loop {
let guard = pin();
let (table, found) = self.find(hash, pending.key(), &guard);
match found {
Found::Frozen => {
drop(guard);
self.wait_for_resize();
}
Found::Miss { tail } => {
let node = pending.into_node(hash, core::ptr::null_mut());
match tail.compare_exchange(
word(core::ptr::null_mut()),
word(node),
Ordering::Release,
Ordering::Relaxed,
&guard,
) {
Ok(_) => {
self.landed(table, guard);
return None;
}
Err(_) => {
pending = Pending::back(node);
backoff.spin();
}
}
}
Found::Hit {
prev,
node: old,
next,
} => {
let previous = old.value.clone();
let node = pending.into_node(hash, next);
match old.next.compare_exchange(
word(next),
word(with(node, MARK)),
Ordering::AcqRel,
Ordering::Relaxed,
&guard,
) {
Ok(_) => {
self.unlink(prev, old, node, hash, &old.key, &guard);
return Some(previous);
}
Err(_) => {
pending = Pending::back(node);
backoff.spin();
}
}
}
}
}
}
pub fn insert_if_absent(&self, key: K, value: V) -> Option<V> {
match self.claim(key, value, |_| ()) {
Claim::Inserted(()) => None,
Claim::Present(existing) => Some(existing),
}
}
pub fn get_or_insert(&self, key: K, value: V) -> V {
match self.claim(key, value, V::clone) {
Claim::Inserted(own) | Claim::Present(own) => own,
}
}
fn claim<R>(&self, key: K, value: V, on_insert: impl FnOnce(&V) -> R) -> Claim<R, V> {
let hash = self.hasher.hash_one(&key);
let mut pending = Pending::Parts { key, value };
let mut backoff = Backoff::new();
loop {
let guard = pin();
let (table, found) = self.find(hash, pending.key(), &guard);
match found {
Found::Frozen => {
drop(guard);
self.wait_for_resize();
}
Found::Hit { node, .. } => return Claim::Present(node.value.clone()),
Found::Miss { tail } => {
let node = pending.into_node(hash, core::ptr::null_mut());
match tail.compare_exchange(
word(core::ptr::null_mut()),
word(node),
Ordering::Release,
Ordering::Relaxed,
&guard,
) {
Ok(_) => {
let answer = on_insert(unsafe { &(*node).value });
self.landed(table, guard);
return Claim::Inserted(answer);
}
Err(_) => {
pending = Pending::back(node);
backoff.spin();
}
}
}
}
}
}
pub fn remove<Q>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.remove_where::<Q, false>(key, |_| true)
}
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 extend<I: IntoIterator<Item = (K, V)>>(&self, iter: I) {
for (k, v) in iter {
self.insert(k, v);
}
}
#[inline]
fn landed(&self, table: TableRef<K, V>, guard: Guard) {
let count = table.count().fetch_add(1, Ordering::Relaxed) + 1;
let capacity = table.capacity();
if 4 * (count.max(0) as usize) > 3 * capacity {
drop(guard);
self.try_resize(capacity * 2);
}
}
#[inline]
fn unlink<Q>(
&self,
prev: &Atomic<Node<K, V>>,
node: &Node<K, V>,
succ: *mut Node<K, V>,
hash: u64,
key: &Q,
guard: &Guard,
) where
K: Borrow<Q>,
Q: Eq + ?Sized,
{
let raw = node as *const Node<K, V> as *mut Node<K, V>;
if prev
.compare_exchange(
word(raw),
word(succ),
Ordering::AcqRel,
Ordering::Relaxed,
guard,
)
.is_ok()
{
unsafe { retire_node(raw) };
} else {
self.cleanup(hash, key, guard);
}
}
}
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_pending();
}
}
#[cfg(test)]
mod tests;