use parking_lot::RwLock;
use rustc_hash::FxHashMap;
use std::collections::VecDeque;
pub struct CachePredictor<K>
where
K: Clone + Eq + std::hash::Hash,
{
access_history: RwLock<VecDeque<K>>,
patterns: RwLock<FxHashMap<Vec<K>, f64>>,
confidence_threshold: f64,
}
impl<K> CachePredictor<K>
where
K: Clone + Eq + std::hash::Hash,
{
#[must_use]
pub fn new(confidence_threshold: f64) -> Self {
Self {
access_history: RwLock::new(VecDeque::new()),
patterns: RwLock::new(FxHashMap::default()),
confidence_threshold,
}
}
pub fn record_access(&self, key: K) {
let mut history = self.access_history.write();
history.push_back(key);
if history.len() > 1000 {
history.pop_front();
}
self.update_patterns(&history);
}
pub fn predict_next(&self, current_sequence: &[K]) -> Vec<K> {
let patterns = self.patterns.read();
let mut predictions = Vec::new();
for (pattern, confidence) in patterns.iter() {
if *confidence > self.confidence_threshold
&& pattern.len() > current_sequence.len()
&& pattern.starts_with(current_sequence)
{
predictions.push(pattern[current_sequence.len()].clone());
}
}
predictions
}
pub fn predict_value(&self, _key: &K) -> Option<()> {
None
}
fn update_patterns(&self, history: &VecDeque<K>) {
let mut patterns = self.patterns.write();
for window_size in 2..=5.min(history.len()) {
for window in history.iter().collect::<Vec<_>>().windows(window_size) {
let pattern: Vec<K> = window.iter().map(|k| (*k).clone()).collect();
*patterns.entry(pattern).or_insert(0.0) += 1.0;
}
}
let total_patterns = patterns.len() as f64;
for confidence in patterns.values_mut() {
*confidence /= total_patterns;
}
}
#[cfg(test)]
pub fn access_history_len(&self) -> usize {
self.access_history.read().len()
}
}