use core::hash::{BuildHasher, Hash, Hasher};
use ahash::RandomState;
#[derive(Debug)]
pub struct CacheSlot<K, V> {
lock: core::sync::atomic::AtomicBool,
data: core::cell::UnsafeCell<Option<(K, V)>>,
}
unsafe impl<K: Send, V: Send> Send for CacheSlot<K, V> {}
unsafe impl<K: Sync, V: Sync> Sync for CacheSlot<K, V> {}
impl<K, V> CacheSlot<K, V> {
#[inline]
pub const fn new() -> Self {
Self {
lock: core::sync::atomic::AtomicBool::new(false),
data: core::cell::UnsafeCell::new(None),
}
}
}
#[derive(Debug)]
pub struct StaticDualCache<K, V, const N: usize, S = RandomState> {
hasher: S,
slots: [CacheSlot<K, V>; N],
}
unsafe impl<K: Send, V: Send, const N: usize, S: Send> Send for StaticDualCache<K, V, N, S> {}
unsafe impl<K: Sync, V: Sync, const N: usize, S: Sync> Sync for StaticDualCache<K, V, N, S> {}
impl<K, V, const N: usize> StaticDualCache<K, V, N, RandomState> {
#[inline]
pub fn new(_config: crate::Config) -> Self {
Self {
hasher: RandomState::new(),
slots: [const { CacheSlot::new() }; N],
}
}
#[inline]
pub fn new_headless(_config: crate::Config) -> (Self, ()) {
(
Self::new(_config),
(),
)
}
}
impl<K, V, const N: usize, S> StaticDualCache<K, V, N, S>
where
K: Hash + Eq + Clone,
V: Clone,
S: BuildHasher,
{
#[inline]
pub fn with_hasher(hasher: S) -> Self {
Self {
hasher,
slots: [const { CacheSlot::new() }; N],
}
}
#[inline(always)]
fn get_slot_idx(&self, key: &K) -> usize {
let mut s = self.hasher.build_hasher();
key.hash(&mut s);
(s.finish() as usize) % N
}
pub fn get(&self, key: &K) -> Option<V> {
let idx = self.get_slot_idx(key);
let slot = &self.slots[idx];
while slot.lock.compare_exchange_weak(
false,
true,
core::sync::atomic::Ordering::Acquire,
core::sync::atomic::Ordering::Relaxed,
).is_err() {
core::hint::spin_loop();
}
let data = unsafe { &*slot.data.get() };
let res = match data {
Some((k, v)) if k == key => Some(v.clone()),
_ => None,
};
slot.lock.store(false, core::sync::atomic::Ordering::Release);
res
}
pub fn insert(&self, key: K, value: V) {
let idx = self.get_slot_idx(&key);
let slot = &self.slots[idx];
while slot.lock.compare_exchange_weak(
false,
true,
core::sync::atomic::Ordering::Acquire,
core::sync::atomic::Ordering::Relaxed,
).is_err() {
core::hint::spin_loop();
}
let data = unsafe { &mut *slot.data.get() };
*data = Some((key, value));
slot.lock.store(false, core::sync::atomic::Ordering::Release);
}
pub fn remove(&self, key: &K) {
let idx = self.get_slot_idx(key);
let slot = &self.slots[idx];
while slot.lock.compare_exchange_weak(
false,
true,
core::sync::atomic::Ordering::Acquire,
core::sync::atomic::Ordering::Relaxed,
).is_err() {
core::hint::spin_loop();
}
let data = unsafe { &mut *slot.data.get() };
if let Some((k, _)) = data {
if k == key {
*data = None;
}
}
slot.lock.store(false, core::sync::atomic::Ordering::Release);
}
pub fn clear(&self) {
for slot in &self.slots {
while slot.lock.compare_exchange_weak(
false,
true,
core::sync::atomic::Ordering::Acquire,
core::sync::atomic::Ordering::Relaxed,
).is_err() {
core::hint::spin_loop();
}
let data = unsafe { &mut *slot.data.get() };
*data = None;
slot.lock.store(false, core::sync::atomic::Ordering::Release);
}
}
#[inline(always)]
pub fn sync(&self) {}
}