use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use std::time::{Duration, Instant, SystemTime};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum EvictionPolicy {
#[default]
LRU,
LFU,
ARC,
Clock,
Fixed,
}
impl EvictionPolicy {
#[must_use]
pub const fn description(&self) -> &'static str {
match self {
Self::LRU => "Least Recently Used - evicts oldest accessed entry",
Self::LFU => "Least Frequently Used - evicts least accessed entry",
Self::ARC => "Adaptive Replacement Cache - balances recency and frequency",
Self::Clock => "Clock algorithm - efficient LRU approximation",
Self::Fixed => "Fixed - no eviction, deterministic memory",
}
}
#[must_use]
pub const fn supports_eviction(&self) -> bool {
!matches!(self, Self::Fixed)
}
#[must_use]
pub const fn recommended_use_case(&self) -> &'static str {
match self {
Self::LRU => "Sequential inference, time-series models",
Self::LFU => "Random access, sparse models",
Self::ARC => "Mixed workloads, production deployments",
Self::Clock => "Embedded systems with limited CPU",
Self::Fixed => "Deterministic embedded, NASA Level A",
}
}
}
#[derive(Debug, Clone)]
pub struct MemoryBudget {
pub max_pages: usize,
pub high_watermark: usize,
pub low_watermark: usize,
pub reserved_pages: HashSet<u64>,
}
impl MemoryBudget {
#[must_use]
pub fn new(max_pages: usize) -> Self {
Self {
max_pages,
high_watermark: (max_pages as f64 * 0.9) as usize,
low_watermark: (max_pages as f64 * 0.7) as usize,
reserved_pages: HashSet::new(),
}
}
#[must_use]
pub fn with_watermarks(max_pages: usize, high_pct: f64, low_pct: f64) -> Self {
Self {
max_pages,
high_watermark: (max_pages as f64 * high_pct) as usize,
low_watermark: (max_pages as f64 * low_pct) as usize,
reserved_pages: HashSet::new(),
}
}
pub fn reserve_page(&mut self, page_id: u64) {
self.reserved_pages.insert(page_id);
}
pub fn release_page(&mut self, page_id: u64) {
self.reserved_pages.remove(&page_id);
}
#[must_use]
pub fn needs_eviction(&self, current_pages: usize) -> bool {
current_pages >= self.high_watermark
}
#[must_use]
pub fn can_stop_eviction(&self, current_pages: usize) -> bool {
current_pages <= self.low_watermark
}
#[must_use]
pub fn can_evict(&self, page_id: u64) -> bool {
!self.reserved_pages.contains(&page_id)
}
}
#[derive(Debug, Clone, Default)]
pub struct AccessStats {
pub hit_count: u64,
pub miss_count: u64,
pub last_access: u64,
pub total_access_time_ns: u64,
pub prefetch_hits: u64,
}
impl AccessStats {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn record_hit(&mut self, access_time_ns: u64, timestamp: u64) {
self.hit_count += 1;
self.total_access_time_ns += access_time_ns;
self.last_access = timestamp;
}
pub fn record_miss(&mut self, timestamp: u64) {
self.miss_count += 1;
self.last_access = timestamp;
}
pub fn record_prefetch_hit(&mut self) {
self.prefetch_hits += 1;
}
#[must_use]
pub fn hit_rate(&self) -> f64 {
let total = self.hit_count + self.miss_count;
if total == 0 {
0.0
} else {
self.hit_count as f64 / total as f64
}
}
#[must_use]
pub fn avg_access_time_ns(&self) -> f64 {
if self.hit_count == 0 {
0.0
} else {
self.total_access_time_ns as f64 / self.hit_count as f64
}
}
#[must_use]
pub fn prefetch_effectiveness(&self) -> f64 {
if self.hit_count == 0 {
0.0
} else {
self.prefetch_hits as f64 / self.hit_count as f64
}
}
}
#[derive(Debug, Clone)]
pub struct CacheMetadata {
pub source_path: Option<PathBuf>,
pub source_mtime: Option<SystemTime>,
pub cached_at: SystemTime,
pub ttl: Option<Duration>,
pub size_bytes: usize,
pub compression_ratio: f32,
}
impl CacheMetadata {
#[must_use]
pub fn new(size_bytes: usize) -> Self {
Self {
source_path: None,
source_mtime: None,
cached_at: SystemTime::now(),
ttl: None,
size_bytes,
compression_ratio: 1.0,
}
}
#[must_use]
pub fn with_source(mut self, path: PathBuf, mtime: SystemTime) -> Self {
self.source_path = Some(path);
self.source_mtime = Some(mtime);
self
}
#[must_use]
pub fn with_ttl(mut self, ttl: Duration) -> Self {
self.ttl = Some(ttl);
self
}
#[must_use]
pub fn with_compression_ratio(mut self, ratio: f32) -> Self {
self.compression_ratio = ratio;
self
}
#[must_use]
pub fn is_expired(&self) -> bool {
if let Some(ttl) = self.ttl {
if let Ok(elapsed) = self.cached_at.elapsed() {
return elapsed > ttl;
}
}
false
}
#[must_use]
pub fn is_stale(&self, current_mtime: SystemTime) -> bool {
if let Some(cached_mtime) = self.source_mtime {
return current_mtime > cached_mtime;
}
false
}
#[must_use]
pub fn age(&self) -> Duration {
self.cached_at.elapsed().unwrap_or(Duration::ZERO)
}
}
#[derive(Debug, Clone)]
pub enum CacheData {
Compressed(Vec<u8>),
Decompressed(Vec<u8>),
Mapped {
path: PathBuf,
offset: u64,
length: usize,
},
}
impl CacheData {
#[must_use]
pub fn size(&self) -> usize {
match self {
Self::Compressed(data) | Self::Decompressed(data) => data.len(),
Self::Mapped { length, .. } => *length,
}
}
#[must_use]
pub fn is_compressed(&self) -> bool {
matches!(self, Self::Compressed(_))
}
#[must_use]
pub fn is_mapped(&self) -> bool {
matches!(self, Self::Mapped { .. })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ModelType(pub u16);
impl ModelType {
#[must_use]
pub const fn new(value: u16) -> Self {
Self(value)
}
}
#[derive(Debug, Clone)]
pub struct CacheEntry {
pub model_hash: [u8; 32],
pub model_type: ModelType,
pub data: CacheData,
pub metadata: CacheMetadata,
pub stats: AccessStats,
}
impl CacheEntry {
#[must_use]
pub fn new(model_hash: [u8; 32], model_type: ModelType, data: CacheData) -> Self {
let size = data.size();
Self {
model_hash,
model_type,
data,
metadata: CacheMetadata::new(size),
stats: AccessStats::new(),
}
}
#[must_use]
pub fn is_valid(&self) -> bool {
!self.metadata.is_expired()
}
#[must_use]
pub fn tier(&self) -> CacheTier {
match &self.data {
CacheData::Decompressed(_) => CacheTier::L1Hot,
CacheData::Compressed(_) | CacheData::Mapped { .. } => CacheTier::L2Warm,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CacheTier {
L1Hot,
L2Warm,
L3Cold,
}
impl CacheTier {
#[must_use]
pub const fn typical_latency(&self) -> Duration {
match self {
Self::L1Hot => Duration::from_nanos(100),
Self::L2Warm => Duration::from_micros(1000),
Self::L3Cold => Duration::from_millis(10),
}
}
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
Self::L1Hot => "L1 Hot Cache",
Self::L2Warm => "L2 Warm Cache",
Self::L3Cold => "L3 Cold Storage",
}
}
}
include!("cache_config.rs");