use std::hash::Hash;
pub mod policies;
pub mod prefetch;
pub trait CachePolicy<K, V> {
fn get(&mut self, key: &K) -> Option<&V>;
fn insert(&mut self, key: K, value: V);
fn remove(&mut self, key: &K) -> Option<V>;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn clear(&mut self);
fn capacity(&self) -> usize;
}
pub trait PrefetchStrategy<K> {
fn predict_next(&mut self, accessed_key: &K) -> Vec<K>;
fn update_access_pattern(&mut self, key: &K);
fn reset(&mut self);
}
#[derive(Debug, Clone, Default)]
pub struct CacheStats {
pub hits: u64,
pub misses: u64,
pub prefetch_hits: u64,
pub total_accesses: u64,
}
impl CacheStats {
pub fn hit_ratio(&self) -> f64 {
if self.total_accesses == 0 {
0.0
} else {
self.hits as f64 / self.total_accesses as f64
}
}
pub fn prefetch_efficiency(&self) -> f64 {
if self.prefetch_hits == 0 {
0.0
} else {
self.prefetch_hits as f64 / (self.prefetch_hits + self.misses) as f64
}
}
pub fn reset(&mut self) {
*self = Self::default();
}
}
pub struct FulgranceCache<K, V, C, P>
where
K: Clone + Hash + Eq,
V: Clone,
C: CachePolicy<K, V>,
P: PrefetchStrategy<K>,
{
cache: C,
prefetch_strategy: P,
prefetch_fn: Option<Box<dyn Fn(&K) -> Option<V>>>, stats: CacheStats,
_phantom: std::marker::PhantomData<(K, V)>,
}
impl<K, V, C, P> FulgranceCache<K, V, C, P>
where
K: Clone + Hash + Eq,
V: Clone,
C: CachePolicy<K, V>,
P: PrefetchStrategy<K>,
{
pub fn new(cache: C, prefetch_strategy: P) -> Self {
Self {
cache,
prefetch_strategy,
prefetch_fn: None,
stats: CacheStats::default(),
_phantom: std::marker::PhantomData,
}
}
pub fn with_prefetch_fn<F>(mut self, f: F) -> Self
where
F: Fn(&K) -> Option<V> + 'static,
{
self.prefetch_fn = Some(Box::new(f));
self
}
pub fn get(&mut self, key: &K) -> Option<V> {
self.stats.total_accesses += 1;
self.prefetch_strategy.update_access_pattern(key);
if let Some(value) = self.cache.get(key) {
self.stats.hits += 1;
let result = value.clone();
self.prefetch_predicted_keys(key);
return Some(result);
}
self.stats.misses += 1;
if let Some(ref prefetch_fn) = self.prefetch_fn {
if let Some(value) = prefetch_fn(key) {
self.cache.insert(key.clone(), value.clone());
self.prefetch_predicted_keys(key);
return Some(value);
}
}
None
}
pub fn insert(&mut self, key: K, value: V) {
self.cache.insert(key, value);
}
pub fn remove(&mut self, key: &K) -> Option<V> {
self.cache.remove(key)
}
pub fn stats(&self) -> &CacheStats {
&self.stats
}
pub fn reset_stats(&mut self) {
self.stats.reset();
self.prefetch_strategy.reset();
}
pub fn len(&self) -> usize {
self.cache.len()
}
pub fn is_empty(&self) -> bool {
self.cache.is_empty()
}
pub fn capacity(&self) -> usize {
self.cache.capacity()
}
pub fn clear(&mut self) {
self.cache.clear();
self.reset_stats();
}
fn prefetch_predicted_keys(&mut self, accessed_key: &K) {
if let Some(ref prefetch_fn) = self.prefetch_fn {
let predicted_keys = self.prefetch_strategy.predict_next(accessed_key);
for key in predicted_keys {
if self.cache.get(&key).is_none() {
if let Some(value) = prefetch_fn(&key) {
self.cache.insert(key, value);
self.stats.prefetch_hits += 1;
}
}
}
}
}
}
pub mod prelude {
pub use super::{CachePolicy, PrefetchStrategy, FulgranceCache, CacheStats};
pub use super::policies::{LruCache, MruCache, PolicyType};
pub use super::prefetch::{SequentialPrefetch, PrefetchType};
}