use lru::LruCache;
use std::{hash::Hash, num::NonZeroUsize};
enum Value<V> {
Detached,
Pinned(V),
Regular(V),
}
impl<V> Value<V> {
fn get(&self) -> &V {
match self {
Value::Detached => panic!("Value is detached"),
Value::Pinned(v) => v,
Value::Regular(v) => v,
}
}
fn get_mut(&mut self) -> &mut V {
match self {
Value::Detached => panic!("Value is detached"),
Value::Pinned(v) => v,
Value::Regular(v) => v,
}
}
fn out(self, pin_count: &mut usize) -> V {
match self {
Value::Detached => panic!("Value is detached"),
Value::Pinned(v) => {
*pin_count -= 1;
v
}
Value::Regular(v) => v,
}
}
}
pub struct Cache<K: Clone + PartialEq + Eq + Hash, V> {
entries: LruCache<K, Value<V>>,
detach_count: usize,
pin_count: usize,
}
impl<K: Clone + PartialEq + Eq + Hash, V> Cache<K, V> {
pub fn attach(&mut self, key: &K, val: V) {
let (key, d_val) = self
.entries
.pop_entry(key)
.expect("Cannot attach: Requested entry is not cached");
assert!(
matches!(d_val, Value::Detached),
"Only detached entries can be attached"
);
self.entries.push(key, Value::Regular(val));
self.detach_count -= 1;
}
pub fn detach(&mut self, key: &K) -> V {
let (key, val) = self
.entries
.pop_entry(key)
.expect("Cannot detach: Requested entry is not cached");
self.entries.push(key, Value::Detached);
self.detach_count += 1;
val.out(&mut self.pin_count)
}
pub fn empty(&mut self) -> Vec<(K, V)> {
let mut entries = Vec::new();
while let Some((key, val)) = self.entries.pop_lru() {
entries.push((key, val.out(&mut self.pin_count)))
}
entries
}
pub fn get(&mut self, key: &K) -> &V {
self.entries
.get(key)
.expect("Cannot get: Requested entry is not cached")
.get()
}
pub fn get_mut(&mut self, key: &K) -> &mut V {
self.entries
.get_mut(key)
.expect("Cannot get_mut: Requested entry is not cached")
.get_mut()
}
pub fn is_cached(&self, key: &K) -> bool {
self.entries.contains(key)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn margin(&self) -> usize {
self.entries.cap().get() - self.detach_count - self.pin_count
}
pub fn new(cap: NonZeroUsize) -> Self {
Self {
entries: LruCache::new(cap),
detach_count: 0,
pin_count: 0,
}
}
pub fn pin(&mut self, key: &K) {
let (key, val) = self
.entries
.pop_entry(key)
.expect("Cannot pin: Requested entry is not cached");
self.entries
.push(key, Value::Pinned(val.out(&mut self.pin_count)));
self.pin_count += 1;
}
pub fn pop(&mut self, key: &K) -> V {
self.entries
.pop(key)
.expect("Cannot pop: Requested entry is not cached")
.out(&mut self.pin_count)
}
pub fn pop_lru(&mut self) -> Option<(K, V)> {
self.refresh();
self.entries.pop_lru().map(|(k, v)| (k, v.out(&mut 0)))
}
pub fn push(&mut self, key: K, val: V) -> Option<(K, V)> {
self.refresh();
self.entries
.push(key, Value::Regular(val))
.map(|(k, v)| (k, v.out(&mut 0)))
}
fn refresh(&mut self) {
if self.entries.len() < self.entries.cap().get() {
return;
}
assert!(
self.margin() > 0,
"Cache Overflow -- Capacity: {}, Detached: {}, Pinned: {}",
self.entries.cap().get(),
self.detach_count,
self.pin_count
);
loop {
let key = match self.entries.peek_lru() {
Some((key, val)) if !matches!(val, Value::Regular(_)) => key.clone(),
_ => break,
};
self.entries.promote(&key);
}
}
pub fn unpin(&mut self, key: &K) {
let (key, val) = self
.entries
.pop_entry(key)
.expect("Cannot unpin: Requested entry is not cached");
self.entries
.push(key, Value::Regular(val.out(&mut self.pin_count)));
}
}