use std::collections::VecDeque;
pub struct LruEviction {
capacity: usize,
access_order: VecDeque<u64>,
}
impl LruEviction {
pub fn new(capacity: usize) -> Self {
let cap = capacity.max(1);
Self {
capacity: cap,
access_order: VecDeque::with_capacity(cap + 1),
}
}
pub fn on_access(&mut self, key: u64) -> Option<u64> {
self.access_order.retain(|&k| k != key);
self.access_order.push_back(key);
if self.access_order.len() > self.capacity {
self.access_order.pop_front()
} else {
None
}
}
pub fn on_insert(&mut self, key: u64) -> Option<u64> {
self.on_access(key)
}
pub fn len(&self) -> usize {
self.access_order.len()
}
pub fn is_empty(&self) -> bool {
self.access_order.is_empty()
}
pub fn capacity(&self) -> usize {
self.capacity
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_eviction_within_capacity() {
let mut lru = LruEviction::new(3);
assert!(lru.on_insert(1).is_none());
assert!(lru.on_insert(2).is_none());
assert!(lru.on_insert(3).is_none());
assert_eq!(lru.len(), 3);
}
#[test]
fn evicts_lru_on_overflow() {
let mut lru = LruEviction::new(3);
lru.on_insert(1);
lru.on_insert(2);
lru.on_insert(3);
let evicted = lru.on_insert(4);
assert_eq!(evicted, Some(1));
}
#[test]
fn access_refreshes_mru_position() {
let mut lru = LruEviction::new(3);
lru.on_insert(1);
lru.on_insert(2);
lru.on_insert(3);
lru.on_access(1); let evicted = lru.on_insert(4);
assert_eq!(evicted, Some(2));
}
#[test]
fn duplicate_insert_does_not_grow() {
let mut lru = LruEviction::new(3);
lru.on_insert(1);
lru.on_insert(1);
lru.on_insert(1);
assert_eq!(lru.len(), 1);
}
#[test]
fn is_empty_on_new() {
let lru = LruEviction::new(10);
assert!(lru.is_empty());
}
#[test]
fn capacity_one_always_evicts_on_second_insert() {
let mut lru = LruEviction::new(1);
assert!(lru.on_insert(1).is_none());
let evicted = lru.on_insert(2);
assert_eq!(evicted, Some(1));
}
}