use std::hash::Hash;
use hashlink::LinkedHashMap;
use crate::{Cache, CacheEntry};
pub struct LruCache<K, V> {
map: LinkedHashMap<K, CacheEntry<V>, ahash::RandomState>,
cap: usize,
}
impl<K, V> LruCache<K, V>
where
K: Eq + Hash,
{
#[must_use]
pub fn new(cap: usize) -> Self {
LruCache {
map: LinkedHashMap::with_hasher(ahash::RandomState::default()),
cap,
}
}
fn insert_entry(&mut self, key: K, entry: CacheEntry<V>) -> Option<V> {
if self.map.contains_key(&key) {
self.map.insert(key, entry);
return None;
}
let evicted = if self.cap > 0 && self.map.len() >= self.cap {
self.map.pop_front().map(|(_, e)| e.value)
} else {
None
};
self.map.insert(key, entry);
evicted
}
pub fn get(&mut self, key: &K) -> Option<&V> {
let expired = self.map.get(key).map(|e| e.is_expired()).unwrap_or(false);
if expired {
self.map.remove(key);
return None;
}
match self.map.raw_entry_mut().from_key(key) {
hashlink::linked_hash_map::RawEntryMut::Occupied(mut occ) => {
occ.to_back();
Some(&occ.into_mut().value)
}
hashlink::linked_hash_map::RawEntryMut::Vacant(_) => None,
}
}
pub fn put(&mut self, key: K, value: V) -> Option<V> {
self.insert_entry(key, CacheEntry::new(value))
}
pub fn put_with_ttl(&mut self, key: K, value: V, ttl: std::time::Duration) -> Option<V> {
self.insert_entry(key, CacheEntry::with_ttl(value, ttl))
}
#[must_use]
pub fn len(&self) -> usize {
self.map.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
#[must_use]
pub fn cap(&self) -> usize {
self.cap
}
#[must_use]
pub fn contains(&self, key: &K) -> bool {
match self.map.get(key) {
Some(e) => !e.is_expired(),
None => false,
}
}
#[must_use]
pub fn peek(&self, key: &K) -> Option<&V> {
self.map
.get(key)
.and_then(|e| if e.is_expired() { None } else { Some(&e.value) })
}
pub fn remove(&mut self, key: &K) -> Option<V> {
self.map.remove(key).map(|e| e.value)
}
pub fn clear(&mut self) {
self.map.clear();
}
pub fn resize(&mut self, new_cap: usize) {
self.cap = new_cap;
while self.cap > 0 && self.map.len() > self.cap {
self.map.pop_front();
}
}
pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
self.map.iter().map(|(k, e)| (k, &e.value))
}
pub fn entry(&mut self, key: K) -> Entry<'_, K, V>
where
K: Clone,
{
if self.contains(&key) {
Entry::Occupied(OccupiedEntry { cache: self, key })
} else {
Entry::Vacant(VacantEntry { cache: self, key })
}
}
}
impl<K: std::fmt::Debug, V> std::fmt::Debug for LruCache<K, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LruCache")
.field("capacity", &self.cap)
.field("length", &self.map.len())
.finish_non_exhaustive()
}
}
impl<K: Eq + Hash, V> From<Vec<(K, V)>> for LruCache<K, V> {
fn from(pairs: Vec<(K, V)>) -> Self {
let cap = pairs.len().max(1);
let mut cache = LruCache::new(cap);
for (k, v) in pairs {
cache.put(k, v);
}
cache
}
}
pub enum Entry<'a, K, V> {
Occupied(OccupiedEntry<'a, K, V>),
Vacant(VacantEntry<'a, K, V>),
}
pub struct OccupiedEntry<'a, K, V> {
cache: &'a mut LruCache<K, V>,
key: K,
}
pub struct VacantEntry<'a, K, V> {
cache: &'a mut LruCache<K, V>,
key: K,
}
impl<'a, K: Eq + Hash + Clone, V> OccupiedEntry<'a, K, V> {
pub fn key(&self) -> &K {
&self.key
}
pub fn get(&self) -> &V {
self.cache.peek(&self.key).expect("occupied entry exists")
}
pub fn remove(self) -> V {
self.cache.remove(&self.key).expect("occupied entry exists")
}
}
impl<'a, K: Eq + Hash + Clone, V> VacantEntry<'a, K, V> {
pub fn key(&self) -> &K {
&self.key
}
pub fn insert(self, value: V) -> &'a V {
let VacantEntry { cache, key } = self;
cache.put(key.clone(), value);
cache.peek(&key).expect("key was just inserted")
}
}
impl<K, V> Cache<K, V> for LruCache<K, V>
where
K: Eq + Hash,
{
fn get(&mut self, key: &K) -> Option<&V> {
LruCache::get(self, key)
}
fn put(&mut self, key: K, value: V) -> Option<V> {
LruCache::put(self, key, value)
}
fn put_with_ttl(&mut self, key: K, value: V, ttl: std::time::Duration) -> Option<V> {
LruCache::put_with_ttl(self, key, value, ttl)
}
fn len(&self) -> usize {
self.map.len()
}
fn cap(&self) -> usize {
self.cap
}
fn remove(&mut self, key: &K) -> Option<V> {
LruCache::remove(self, key)
}
fn clear(&mut self) {
LruCache::clear(self);
}
fn peek(&self, key: &K) -> Option<&V> {
LruCache::peek(self, key)
}
fn contains_key(&self, key: &K) -> bool {
LruCache::contains(self, key)
}
fn resize(&mut self, new_cap: usize) {
LruCache::resize(self, new_cap);
}
fn values(&self) -> Vec<&V> {
self.map
.values()
.filter(|e| !e.is_expired())
.map(|e| &e.value)
.collect()
}
}