use lru::LruCache;
use std::{hash::Hash, num::NonZeroUsize};
#[derive(Debug)]
pub(crate) struct Cache<K: Eq + Hash, V> {
cache: LruCache<K, V>,
}
impl<K: Eq + Hash + Clone, V> Cache<K, V> {
pub(crate) fn new(capacity: usize) -> Self {
let mut cache = LruCache::new(NonZeroUsize::new(1024).unwrap());
cache.resize(NonZeroUsize::new(capacity).expect("Capacity must be non-zero"));
Self { cache }
}
pub(crate) fn promote(&mut self, key: &K) -> bool {
self.cache.promote(key)
}
pub(crate) fn unbounded() -> Self {
Self {
cache: LruCache::unbounded(),
}
}
pub(crate) fn set(&mut self, key: K, value: V) -> Option<(K, V)> {
let evicted = self.cache.push(key.clone(), value);
match evicted {
Some((k, _)) if k == key => None,
_ => evicted,
}
}
pub(crate) fn update_in_place(&mut self, key: K, value: V) {
assert!(self.cache.contains(&key));
self.cache.put(key, value);
}
pub(crate) fn remove(&mut self, key: &K) -> Option<(K, V)> {
self.cache.pop_entry(key)
}
pub(crate) fn _get(&mut self, key: &K) -> Option<&V> {
self.cache.get(key)
}
pub(crate) fn peek(&self, key: &K) -> Option<&V> {
self.cache.peek(key)
}
pub(crate) fn peek_mut(&mut self, key: &K) -> Option<&mut V> {
self.cache.peek_mut(key)
}
pub(crate) fn clear(&mut self) {
self.cache.clear();
}
pub(crate) fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
self.cache.iter()
}
pub(crate) fn len(&self) -> usize {
self.cache.len()
}
pub(crate) fn pop_lru(&mut self) -> Option<(K, V)> {
self.cache.pop_lru()
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::seq::SliceRandom;
#[test]
fn lru_val_gets_dropped() {
let mut cache = Cache::new(2);
assert_eq!(cache.set(1, 1), None);
assert_eq!(cache.set(2, 2), None);
assert_eq!(cache.set(3, 3), Some((1, 1)));
assert_eq!(cache._get(&1), None);
assert_eq!(cache._get(&2), Some(&2));
assert_eq!(cache._get(&3), Some(&3));
assert_eq!(cache.set(4, 4), Some((2, 2)));
assert_eq!(cache._get(&2), None);
assert_eq!(cache._get(&3), Some(&3));
assert_eq!(cache._get(&4), Some(&4));
}
#[test]
fn updating_key_returns_none() {
let mut cache = Cache::new(2);
cache.set(1, 1);
assert_eq!(cache.set(1, 2), None);
}
const CACHE_SIZE: usize = 20000;
const TIME_BOUND: f64 = 0.6;
#[test]
fn iterated_get_lru() {
let mut cache = Cache::new(CACHE_SIZE);
let time = std::time::Instant::now();
for i in 0..CACHE_SIZE {
cache.set(i, i);
}
for i in 0..CACHE_SIZE {
assert_eq!(cache._get(&i), Some(&i));
}
let duration = time.elapsed().as_secs_f64();
assert!(
duration < TIME_BOUND,
"Cache is slow! Duration: {}",
duration
)
}
#[test]
fn iterated_get_random() {
let mut rng = rand::thread_rng();
let mut shuffled: std::vec::Vec<usize> = (0..CACHE_SIZE).collect();
shuffled.shuffle(&mut rng);
let mut cache = Cache::new(CACHE_SIZE);
let time = std::time::Instant::now();
for i in 0..CACHE_SIZE {
cache.set(i, i);
}
for i in shuffled {
assert_eq!(cache._get(&i), Some(&i));
}
let duration = time.elapsed().as_secs_f64();
assert!(
duration < TIME_BOUND,
"Cache is slow! Duration: {}",
duration
)
}
#[test]
fn lazy_allocation() {
let mut cache = Cache::<u32, u32>::new(1 << 50);
cache.set(0, 0);
}
}