use std::collections::{HashMap, VecDeque};
use std::time::Instant;
use super::types::{CacheItem, CacheKey, EvictionStatistics};
pub trait EvictionPolicy: Send + Sync + std::fmt::Debug {
fn evict(&mut self, current_size: u64, target_size: u64, items: &[CacheItem]) -> Vec<CacheKey>;
fn on_access(&mut self, key: &CacheKey, access_time: Instant);
fn on_store(&mut self, key: &CacheKey, size: u64, store_time: Instant);
fn statistics(&self) -> EvictionStatistics;
}
#[derive(Debug)]
pub struct LRUEvictionPolicy {
access_order: VecDeque<CacheKey>,
}
impl Default for LRUEvictionPolicy {
fn default() -> Self {
Self::new()
}
}
impl LRUEvictionPolicy {
pub fn new() -> Self {
Self {
access_order: VecDeque::new(),
}
}
}
impl EvictionPolicy for LRUEvictionPolicy {
fn evict(
&mut self,
current_size: u64,
target_size: u64,
_items: &[CacheItem],
) -> Vec<CacheKey> {
let bytes_to_evict = current_size.saturating_sub(target_size);
let items_to_evict = (bytes_to_evict / 1024).max(1) as usize;
self.access_order
.iter()
.take(items_to_evict)
.cloned()
.collect()
}
fn on_access(&mut self, key: &CacheKey, _access_time: Instant) {
if let Some(pos) = self.access_order.iter().position(|k| k == key) {
let key = self
.access_order
.remove(pos)
.expect("position was just found, so remove must succeed");
self.access_order.push_back(key);
}
}
fn on_store(&mut self, key: &CacheKey, _size: u64, _store_time: Instant) {
self.access_order.push_back(key.clone());
}
fn statistics(&self) -> EvictionStatistics {
EvictionStatistics::default()
}
}
#[derive(Debug)]
pub struct LFUEvictionPolicy {
frequency_map: HashMap<CacheKey, u64>,
}
impl Default for LFUEvictionPolicy {
fn default() -> Self {
Self::new()
}
}
impl LFUEvictionPolicy {
pub fn new() -> Self {
Self {
frequency_map: HashMap::new(),
}
}
}
impl EvictionPolicy for LFUEvictionPolicy {
fn evict(
&mut self,
current_size: u64,
target_size: u64,
_items: &[CacheItem],
) -> Vec<CacheKey> {
let bytes_to_evict = current_size.saturating_sub(target_size);
let items_to_evict = (bytes_to_evict / 1024).max(1) as usize;
let mut frequency_pairs: Vec<_> = self.frequency_map.iter().collect();
frequency_pairs.sort_by_key(|&(_, &freq)| freq);
frequency_pairs
.iter()
.take(items_to_evict)
.map(|(key, _)| (*key).clone())
.collect()
}
fn on_access(&mut self, key: &CacheKey, _access_time: Instant) {
*self.frequency_map.entry(key.clone()).or_insert(0) += 1;
}
fn on_store(&mut self, key: &CacheKey, _size: u64, _store_time: Instant) {
self.frequency_map.insert(key.clone(), 0);
}
fn statistics(&self) -> EvictionStatistics {
EvictionStatistics::default()
}
}
#[derive(Debug)]
pub struct AdaptiveEvictionPolicy {
lru_component: LRUEvictionPolicy,
lfu_component: LFUEvictionPolicy,
lru_weight: f64,
}
impl Default for AdaptiveEvictionPolicy {
fn default() -> Self {
Self::new()
}
}
impl AdaptiveEvictionPolicy {
pub fn new() -> Self {
Self {
lru_component: LRUEvictionPolicy::new(),
lfu_component: LFUEvictionPolicy::new(),
lru_weight: 0.5,
}
}
}
impl EvictionPolicy for AdaptiveEvictionPolicy {
fn evict(&mut self, current_size: u64, target_size: u64, items: &[CacheItem]) -> Vec<CacheKey> {
let lru_candidates = self.lru_component.evict(current_size, target_size, items);
let lfu_candidates = self.lfu_component.evict(current_size, target_size, items);
let lru_count = (lru_candidates.len() as f64 * self.lru_weight) as usize;
let mut result = Vec::new();
result.extend(lru_candidates.into_iter().take(lru_count));
result.extend(lfu_candidates.into_iter().take(items.len() - lru_count));
result
}
fn on_access(&mut self, key: &CacheKey, access_time: Instant) {
self.lru_component.on_access(key, access_time);
self.lfu_component.on_access(key, access_time);
}
fn on_store(&mut self, key: &CacheKey, size: u64, store_time: Instant) {
self.lru_component.on_store(key, size, store_time);
self.lfu_component.on_store(key, size, store_time);
}
fn statistics(&self) -> EvictionStatistics {
EvictionStatistics::default()
}
}