use core::{
cell::UnsafeCell,
mem::MaybeUninit,
ptr,
sync::atomic::{AtomicU64, Ordering},
};
#[cfg(all(feature = "alloc", not(feature = "std")))]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std as alloc;
use alloc::boxed::Box;
use alloc::vec::Vec;
pub const EMPTY: u64 = 0;
pub const IN_PROGRESS: u64 = 1 << 63;
pub const ZERO_OFFSET: usize = 0;
pub const IMMEDIATE: usize = 1;
pub const MAX: usize = usize::MAX;
#[inline]
pub fn key_bits(k: u64) -> u64 {
k & !IN_PROGRESS
}
#[inline]
pub fn is_in_progress(k: u64) -> bool {
(k & IN_PROGRESS) != 0 && k != EMPTY
}
pub struct SimpleLPHashMap<V> {
mask: usize,
slots: Box<[Slot<V>]>,
}
pub struct Slot<V> {
pub key: AtomicU64, pub value: UnsafeCell<MaybeUninit<V>>,
}
impl<V> Slot<V> {
fn new() -> Self {
Self {
key: AtomicU64::new(EMPTY),
value: UnsafeCell::new(MaybeUninit::uninit()),
}
}
#[inline]
fn load_key(&self) -> u64 {
self.key.load(Ordering::Acquire)
}
}
unsafe impl<V: Send + Sync> Sync for SimpleLPHashMap<V> {}
unsafe impl<V: Send> Send for SimpleLPHashMap<V> {}
impl<V> SimpleLPHashMap<V> {
pub fn with_capacity(capacity: usize) -> Self {
assert!(capacity.is_power_of_two() && capacity > 0);
let mut v = Vec::with_capacity(capacity);
v.resize_with(capacity, Slot::new);
Self {
mask: capacity - 1,
slots: v.into_boxed_slice(),
}
}
pub fn with_capacity_and_init<F>(capacity: usize, mut init_value: F) -> Self
where
F: FnMut() -> V,
{
assert!(capacity.is_power_of_two() && capacity > 0);
let mut v = Vec::with_capacity(capacity);
for _ in 0..capacity {
v.push(Slot {
key: AtomicU64::new(EMPTY),
value: UnsafeCell::new(MaybeUninit::new(init_value())),
});
}
Self {
mask: capacity - 1,
slots: v.into_boxed_slice(),
}
}
}
impl<V> SimpleLPHashMap<V> {
#[inline]
pub fn capacity(&self) -> usize {
self.mask + 1
}
#[inline]
pub unsafe fn raw_slots(&self) -> &[Slot<V>] {
&self.slots
}
#[inline]
pub unsafe fn slot_at(&self, index: usize) -> &Slot<V> {
&self.slots[index]
}
pub unsafe fn scan_slots<F>(&self, mut visitor: F)
where
F: FnMut(usize, u64, *const V),
{
for (index, slot) in self.slots.iter().enumerate() {
let k = slot.load_key();
if k != EMPTY {
let ptr = self.value_ptr(index);
visitor(index, k, ptr);
}
}
}
#[inline]
fn index_for(&self, key: u64, placement_offset: usize) -> usize {
((key as usize) + placement_offset) & self.mask
}
#[inline]
fn value_ptr(&self, index: usize) -> *const V {
unsafe { (*self.slots[index].value.get()).as_ptr() }
}
pub unsafe fn get_or_insert_concurrent(
&self,
key: u64,
placement_offset: usize,
mut n: usize,
insert: bool,
fold: bool,
) -> (*const V, bool, usize, u64, usize) {
debug_assert!(key != EMPTY, "key must be non-zero");
debug_assert!(key & IN_PROGRESS == 0, "keys must have MSB clear");
debug_assert!(
insert || !fold,
"fold has no effect when insert == false; this is probably a bug"
);
let mut idx = self.index_for(key, placement_offset);
if n == MAX {
n = self.mask + 1;
}
for i in 0..n {
let k = self.slots[idx].load_key();
if insert && k == EMPTY {
let expected = k;
let new_key = if !fold { key | IN_PROGRESS } else {
let mut logical_key = key.wrapping_add(placement_offset as u64).wrapping_add(i as u64);
if logical_key == 0 || logical_key == IN_PROGRESS {
logical_key = logical_key.wrapping_add((self.mask + 1) as u64);
}
logical_key | IN_PROGRESS
};
match self.slots[idx].key.compare_exchange(
expected,
new_key,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => {
return (self.value_ptr(idx), true, i, new_key, idx);
}
Err(actual) => {
if !fold && actual != EMPTY && key_bits(actual) == key {
let ptr = self.value_ptr(idx);
return (ptr, false, i, actual, idx);
}
}
}
}
if key_bits(k) == key {
let ptr = self.value_ptr(idx);
return (ptr, false, i, k, idx);
}
idx = (idx + 1) & self.mask;
}
(ptr::null_mut(), false, usize::MAX, EMPTY, usize::MAX)
}
pub unsafe fn finish_init_at(&self, index: usize) {
let slot = &self.slots[index];
let k = slot.key.load(Ordering::Acquire);
debug_assert!(k != EMPTY);
if is_in_progress(k) {
slot.key.fetch_and(!IN_PROGRESS, Ordering::Release);
}
}
pub unsafe fn remove_concurrent(&self, key: u64, placement_offset: usize, mut n: usize) -> bool {
debug_assert!(key != EMPTY, "key must be non-zero");
debug_assert!(key & IN_PROGRESS == 0, "keys must have MSB clear");
let mut idx = self.index_for(key, placement_offset);
if n == MAX {
n = self.mask + 1;
}
for _ in 0..n {
let k = self.slots[idx].load_key();
if key_bits(k) == key {
if self.slots[idx].key.compare_exchange(
k,
EMPTY,
Ordering::AcqRel,
Ordering::Acquire,
).is_ok() {
return true;
} else {
return false;
}
}
idx = (idx + 1) & self.mask;
}
false
}
}