use crate::ast::Program;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
#[derive(Debug)]
pub struct ASTCache {
cache: HashMap<u64, Program>,
max_size: usize,
hits: usize,
misses: usize,
}
impl ASTCache {
pub fn new() -> Self {
Self::with_capacity(100)
}
pub fn with_capacity(max_size: usize) -> Self {
ASTCache {
cache: HashMap::with_capacity(max_size.min(100)),
max_size,
hits: 0,
misses: 0,
}
}
fn hash_code(code: &str) -> u64 {
let mut hasher = DefaultHasher::new();
code.hash(&mut hasher);
hasher.finish()
}
pub fn get(&mut self, code: &str) -> Option<Program> {
let hash = Self::hash_code(code);
if let Some(program) = self.cache.get(&hash) {
self.hits += 1;
Some(program.clone())
} else {
self.misses += 1;
None
}
}
pub fn insert(&mut self, code: &str, program: Program) {
let hash = Self::hash_code(code);
if self.cache.len() >= self.max_size {
let to_remove = (self.max_size / 10).max(1);
let keys_to_remove: Vec<u64> = self.cache.keys().take(to_remove).copied().collect();
for key in keys_to_remove {
self.cache.remove(&key);
}
}
self.cache.insert(hash, program);
}
pub fn clear(&mut self) {
self.cache.clear();
self.hits = 0;
self.misses = 0;
}
pub fn stats(&self) -> CacheStats {
CacheStats {
size: self.cache.len(),
max_size: self.max_size,
hits: self.hits,
misses: self.misses,
hit_rate: if self.hits + self.misses > 0 {
self.hits as f64 / (self.hits + self.misses) as f64
} else {
0.0
},
}
}
}
impl Default for ASTCache {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheStats {
pub size: usize,
pub max_size: usize,
pub hits: usize,
pub misses: usize,
pub hit_rate: f64,
}
impl std::fmt::Display for CacheStats {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"Cache Stats: size={}/{}, hits={}, misses={}, hit_rate={:.2}%",
self.size,
self.max_size,
self.hits,
self.misses,
self.hit_rate * 100.0
)
}
}