use super::node::{HELD, MARK, Node, with, word};
use super::table::TableRef;
use super::walk::Found;
use super::{Backoff, HashMap, Pending};
use crate::MaybeSend;
use core::borrow::Borrow;
use core::hash::{BuildHasher, Hash};
use core::sync::atomic::Ordering;
use kovan::{Atomic, Guard, pin};
impl<K, V, S> HashMap<K, V, S>
where
K: Hash + Eq + Clone + MaybeSend + 'static,
V: Clone + MaybeSend + 'static,
S: BuildHasher,
{
pub fn remove_if<Q, F>(&self, key: &Q, pred: F) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
F: FnOnce(&V) -> bool,
{
self.remove_where::<Q, true>(key, pred)
}
pub fn compare_and_remove<Q>(&self, key: &Q, expected: &V) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
V: PartialEq,
{
self.remove_where::<Q, true>(key, |value| value == expected)
}
pub fn replace_if<F>(&self, key: K, value: V, pred: F) -> Result<V, Option<V>>
where
F: FnOnce(&V) -> bool,
{
let hash = self.hasher.hash_one(&key);
let mut backoff = Backoff::new();
loop {
let guard = pin();
let (_, found) = self.find(hash, &key, &guard);
let (prev, old, next) = match found {
Found::Frozen => {
drop(guard);
self.wait_for_resize();
continue;
}
Found::Miss { .. } => return Err(None),
Found::Hit { prev, node, next } => (prev, node, next),
};
let Some(held) = Hold::take(&old.next, next, &guard) else {
backoff.spin();
continue;
};
if !pred(&old.value) {
drop(held);
return Err(Some(old.value.clone()));
}
let previous = old.value.clone();
let node = Pending::Parts { key, value }.into_node(hash, next);
held.write(with(node, MARK));
self.unlink(prev, old, node, hash, &old.key, &guard);
return Ok(previous);
}
}
pub fn compare_and_swap(&self, key: K, expected: &V, value: V) -> Result<V, Option<V>>
where
V: PartialEq,
{
self.replace_if(key, value, |present| present == expected)
}
pub fn compute<F>(&self, key: K, f: F) -> Option<V>
where
F: FnOnce(Option<&V>) -> Option<V>,
{
let hash = self.hasher.hash_one(&key);
let mut backoff = Backoff::new();
loop {
let guard = pin();
let (table, found) = self.find(hash, &key, &guard);
match found {
Found::Frozen => {
drop(guard);
self.wait_for_resize();
}
Found::Hit {
prev,
node: old,
next,
} => {
let Some(held) = Hold::take(&old.next, next, &guard) else {
backoff.spin();
continue;
};
let Some(new) = f(Some(&old.value)) else {
held.write(with(next, MARK));
let shrink_to = self.count_out(table);
self.unlink(prev, old, next, hash, &key, &guard);
if let Some(capacity) = shrink_to {
drop(guard);
self.try_resize(capacity);
}
return None;
};
let answer = new.clone();
let node = Pending::Parts { key, value: new }.into_node(hash, next);
held.write(with(node, MARK));
self.unlink(prev, old, node, hash, &old.key, &guard);
return Some(answer);
}
Found::Miss { tail } => {
let Some(held) = Hold::take(tail, core::ptr::null_mut(), &guard) else {
backoff.spin();
continue;
};
let new = f(None)?;
let answer = new.clone();
let node =
Pending::Parts { key, value: new }.into_node(hash, core::ptr::null_mut());
held.write(node);
self.landed(table, guard);
return Some(answer);
}
}
}
}
#[inline(always)]
pub(super) fn remove_where<Q, const HOLD: bool>(
&self,
key: &Q,
pred: impl FnOnce(&V) -> bool,
) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.hasher.hash_one(key);
let mut backoff = Backoff::new();
loop {
let guard = pin();
let (table, found) = self.find(hash, key, &guard);
let (prev, node, next) = match found {
Found::Frozen => {
drop(guard);
self.wait_for_resize();
continue;
}
Found::Miss { .. } => return None,
Found::Hit { prev, node, next } => (prev, node, next),
};
let value = if HOLD {
let Some(held) = Hold::take(&node.next, next, &guard) else {
backoff.spin();
continue;
};
if !pred(&node.value) {
return None;
}
let value = node.value.clone();
held.write(with(next, MARK));
value
} else {
let value = node.value.clone();
if node
.next
.compare_exchange(
word(next),
word(with(next, MARK)),
Ordering::AcqRel,
Ordering::Relaxed,
&guard,
)
.is_err()
{
backoff.spin();
continue;
}
value
};
let shrink_to = self.count_out(table);
self.unlink(prev, node, next, hash, key, &guard);
if let Some(capacity) = shrink_to {
drop(guard);
self.try_resize(capacity);
}
return Some(value);
}
}
#[inline(always)]
fn count_out(&self, table: TableRef<K, V>) -> Option<usize> {
let remaining = table.count().fetch_sub(1, Ordering::Relaxed) - 1;
let capacity = table.capacity();
(4 * (remaining.max(0) as usize) < capacity && capacity > self.floor)
.then_some(capacity / 2)
}
}
struct Hold<'g, K, V> {
link: &'g Atomic<Node<K, V>>,
word: *mut Node<K, V>,
}
impl<'g, K, V> Hold<'g, K, V> {
#[inline]
fn take(
link: &'g Atomic<Node<K, V>>,
word_now: *mut Node<K, V>,
guard: &Guard,
) -> Option<Self> {
link.compare_exchange(
word(word_now),
word(with(word_now, HELD)),
Ordering::Acquire,
Ordering::Relaxed,
guard,
)
.ok()
.map(|_| Self {
link,
word: word_now,
})
}
#[inline]
fn write(self, decided: *mut Node<K, V>) {
self.link.store(word(decided), Ordering::Release);
core::mem::forget(self);
}
}
impl<K, V> Drop for Hold<'_, K, V> {
fn drop(&mut self) {
self.link.store(word(self.word), Ordering::Release);
}
}