use core::hash::Hash;
use std::collections::{HashMap, VecDeque};
use std::num::NonZeroUsize;
use std::sync::{Mutex, MutexGuard};
use crate::cache::Cache;
use crate::error::CacheError;
pub struct LruCache<K, V> {
capacity: NonZeroUsize,
inner: Mutex<Inner<K, V>>,
}
struct Inner<K, V> {
map: HashMap<K, V>,
order: VecDeque<K>,
}
impl<K, V> LruCache<K, V>
where
K: Eq + Hash + Clone,
V: Clone,
{
pub fn new(capacity: usize) -> Result<Self, CacheError> {
let cap = NonZeroUsize::new(capacity).ok_or(CacheError::InvalidCapacity)?;
Ok(Self::with_capacity(cap))
}
pub fn with_capacity(capacity: NonZeroUsize) -> Self {
let cap = capacity.get();
Self {
capacity,
inner: Mutex::new(Inner {
map: HashMap::with_capacity(cap),
order: VecDeque::with_capacity(cap),
}),
}
}
fn lock_inner(&self) -> MutexGuard<'_, Inner<K, V>> {
match self.inner.lock() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
}
}
}
impl<K, V> Cache<K, V> for LruCache<K, V>
where
K: Eq + Hash + Clone,
V: Clone,
{
fn get(&self, key: &K) -> Option<V> {
let mut inner = self.lock_inner();
let value = inner.map.get(key)?.clone();
promote(&mut inner.order, key);
Some(value)
}
fn insert(&self, key: K, value: V) -> Option<V> {
let mut inner = self.lock_inner();
let old = inner.map.insert(key.clone(), value);
if old.is_some() {
promote(&mut inner.order, &key);
} else {
inner.order.push_front(key);
while inner.order.len() > self.capacity.get() {
if let Some(evicted) = inner.order.pop_back() {
let _ = inner.map.remove(&evicted);
}
}
}
old
}
fn remove(&self, key: &K) -> Option<V> {
let mut inner = self.lock_inner();
let value = inner.map.remove(key)?;
if let Some(pos) = inner.order.iter().position(|k| k == key) {
let _ = inner.order.remove(pos);
}
Some(value)
}
fn contains_key(&self, key: &K) -> bool {
self.lock_inner().map.contains_key(key)
}
fn len(&self) -> usize {
self.lock_inner().map.len()
}
fn clear(&self) {
let mut inner = self.lock_inner();
inner.map.clear();
inner.order.clear();
}
fn capacity(&self) -> usize {
self.capacity.get()
}
}
fn promote<K: Eq>(order: &mut VecDeque<K>, key: &K) {
if let Some(pos) = order.iter().position(|k| k == key) {
if let Some(k) = order.remove(pos) {
order.push_front(k);
}
}
}