use std::{
cell::UnsafeCell,
fmt::Debug,
sync::atomic::{AtomicU64, AtomicUsize, Ordering},
};
use bitflags::bitflags;
use crate::eviction::Eviction;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Flags: u64 {
const IN_INDEXER = 0b00000001;
const IN_EVICTION = 0b00000010;
}
}
pub struct Data<E>
where
E: Eviction,
{
pub key: E::Key,
pub value: E::Value,
pub properties: E::Properties,
pub hash: u64,
pub weight: usize,
}
pub struct Record<E>
where
E: Eviction,
{
data: Data<E>,
state: UnsafeCell<E::State>,
refs: AtomicUsize,
flags: AtomicU64,
}
unsafe impl<E> Send for Record<E> where E: Eviction {}
unsafe impl<E> Sync for Record<E> where E: Eviction {}
impl<E> Debug for Record<E>
where
E: Eviction,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Record").field("hash", &self.data.hash).finish()
}
}
impl<E> Record<E>
where
E: Eviction,
{
pub const STATE_OFFSET: usize = std::mem::offset_of!(Self, state);
pub fn new(data: Data<E>) -> Self {
Record {
data,
state: Default::default(),
refs: AtomicUsize::new(0),
flags: AtomicU64::new(0),
}
}
pub fn key(&self) -> &E::Key {
&self.data.key
}
pub fn value(&self) -> &E::Value {
&self.data.value
}
pub fn properties(&self) -> &E::Properties {
&self.data.properties
}
pub fn hash(&self) -> u64 {
self.data.hash
}
pub fn weight(&self) -> usize {
self.data.weight
}
pub fn state(&self) -> &UnsafeCell<E::State> {
&self.state
}
pub fn set_in_eviction(&self, val: bool) {
self.set_flags(Flags::IN_EVICTION, val, Ordering::Release);
}
pub fn is_in_eviction(&self) -> bool {
self.get_flags(Flags::IN_EVICTION, Ordering::Acquire)
}
pub fn set_in_indexer(&self, val: bool) {
self.set_flags(Flags::IN_INDEXER, val, Ordering::Release);
}
pub fn is_in_indexer(&self) -> bool {
self.get_flags(Flags::IN_INDEXER, Ordering::Acquire)
}
pub fn set_flags(&self, flags: Flags, val: bool, order: Ordering) {
match val {
true => self.flags.fetch_or(flags.bits(), order),
false => self.flags.fetch_and(!flags.bits(), order),
};
}
pub fn get_flags(&self, flags: Flags, order: Ordering) -> bool {
self.flags.load(order) & flags.bits() == flags.bits()
}
pub fn refs(&self) -> usize {
self.refs.load(Ordering::Acquire)
}
pub fn inc_refs(&self, val: usize) -> usize {
let old = self.refs.fetch_add(val, Ordering::SeqCst);
tracing::trace!(
"[record]: inc record (hash: {}) refs: {} => {}",
self.hash(),
old,
old + val
);
old + val
}
pub fn dec_refs(&self, val: usize) -> usize {
let old = self.refs.fetch_sub(val, Ordering::SeqCst);
tracing::trace!(
"[record]: dec record (hash: {}) refs: {} => {}",
self.hash(),
old,
old - val
);
old - val
}
}