use core::sync::atomic::{AtomicU8, Ordering};
use std::collections::VecDeque;
pub(crate) const FREQ_MAX: u8 = 3;
pub(crate) struct Policy<K> {
pub(crate) small: VecDeque<K>,
pub(crate) main: VecDeque<K>,
pub(crate) small_cap: usize,
pub(crate) stale_estimate: usize,
pub(crate) capacity: usize,
}
impl<K> Policy<K> {
pub(crate) fn new(capacity: usize) -> Self {
let small_cap = (capacity / 10).max(1);
let main_cap = capacity.saturating_sub(small_cap).max(1);
Self {
small: VecDeque::with_capacity(small_cap.saturating_add(1)),
main: VecDeque::with_capacity(main_cap.saturating_add(1)),
small_cap,
stale_estimate: 0,
capacity,
}
}
#[inline(always)]
pub(crate) fn admit_small(&mut self, key: K) {
self.small.push_back(key);
}
#[inline(always)]
pub(crate) fn admit_main(&mut self, key: K) {
self.main.push_back(key);
}
#[inline(always)]
pub(crate) fn mark_stale(&mut self) {
self.stale_estimate = self.stale_estimate.saturating_add(1);
}
#[inline(always)]
pub(crate) fn note_stale_popped(&mut self) {
self.stale_estimate = self.stale_estimate.saturating_sub(1);
}
pub(crate) fn compact<V>(&mut self, map: &axhash_map::HashMap<K, super::shard::Entry<V>>)
where
K: Eq + core::hash::Hash,
{
let threshold = self.capacity.saturating_mul(2);
let total = self.small.len() + self.main.len();
if total <= threshold {
return;
}
let mut budget = self.stale_estimate;
while budget > 0 {
if let Some(front) = self.small.front()
&& !map.contains_key(front)
{
self.small.pop_front();
budget -= 1;
continue;
}
if let Some(front) = self.main.front()
&& !map.contains_key(front)
{
self.main.pop_front();
budget -= 1;
continue;
}
break;
}
self.stale_estimate = budget;
}
}
#[inline(always)]
pub(crate) fn bump_freq(freq: &AtomicU8) {
let cur = freq.load(Ordering::Relaxed);
if cur < FREQ_MAX {
let _ = freq.compare_exchange(cur, cur + 1, Ordering::Relaxed, Ordering::Relaxed);
}
}