use std::{borrow::Borrow, hash::Hash};
use super::bounded_lru::BoundedLruCache;
pub const IDLE_PASSES: u64 = 120;
struct Used<V> {
value: V,
pass: u64,
}
pub struct PassAgedCache<K, V> {
entries: BoundedLruCache<K, Used<V>>,
pass: u64,
}
impl<K, V> PassAgedCache<K, V>
where
K: Eq + Hash,
{
pub fn with_capacity_at_least_one(capacity: usize) -> Self {
Self {
entries: BoundedLruCache::with_capacity_at_least_one(capacity),
pass: 0,
}
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn clear(&mut self) {
self.entries.clear();
}
pub fn get<Q>(&mut self, key: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let pass = self.pass;
let used = self.entries.get_mut(key)?;
used.pass = pass;
Some(&used.value)
}
pub fn push(&mut self, key: K, value: V) -> Option<(K, V)> {
let pass = self.pass;
self.entries
.push(key, Used { value, pass })
.map(|(key, used)| (key, used.value))
}
pub fn pop_lru(&mut self) -> Option<(K, V)> {
self.entries.pop_lru().map(|(key, used)| (key, used.value))
}
pub fn begin_pass(&mut self, mut dropped: impl FnMut(V)) {
self.pass += 1;
let pass = self.pass;
while self
.entries
.peek_lru()
.is_some_and(|(_, used)| pass - used.pass > IDLE_PASSES)
{
let Some((_, used)) = self.entries.pop_lru() else {
break;
};
dropped(used.value);
}
}
}
#[cfg(test)]
#[path = "tests/pass_aged_tests.rs"]
mod tests;