oximedia_proxy/cache/
manager.rs1use super::strategy::CacheStrategy;
4use std::collections::HashMap;
5use std::path::PathBuf;
6
7pub struct CacheManager {
9 #[allow(dead_code)]
11 cache_dir: PathBuf,
12
13 strategy: CacheStrategy,
15
16 cache: HashMap<PathBuf, CacheEntry>,
18
19 max_size: u64,
21
22 current_size: u64,
24}
25
26impl CacheManager {
27 #[must_use]
29 pub fn new(cache_dir: PathBuf, max_size: u64) -> Self {
30 Self {
31 cache_dir,
32 strategy: CacheStrategy::Lru,
33 cache: HashMap::new(),
34 max_size,
35 current_size: 0,
36 }
37 }
38
39 pub fn set_strategy(&mut self, strategy: CacheStrategy) {
41 self.strategy = strategy;
42 }
43
44 pub fn add(&mut self, path: PathBuf, size: u64) {
46 if self.current_size + size > self.max_size {
47 self.evict(size);
48 }
49
50 let entry = CacheEntry {
51 path: path.clone(),
52 size,
53 access_count: 0,
54 last_access: current_timestamp(),
55 };
56
57 self.cache.insert(path, entry);
58 self.current_size += size;
59 }
60
61 #[must_use]
63 pub fn contains(&self, path: &PathBuf) -> bool {
64 self.cache.contains_key(path)
65 }
66
67 pub fn access(&mut self, path: &PathBuf) {
69 if let Some(entry) = self.cache.get_mut(path) {
70 entry.access_count += 1;
71 entry.last_access = current_timestamp();
72 }
73 }
74
75 fn evict(&mut self, needed_size: u64) {
77 let mut freed = 0u64;
78 let mut to_remove = Vec::new();
79
80 let mut entries: Vec<_> = self.cache.values().collect();
82 entries.sort_by(|a, b| match self.strategy {
83 CacheStrategy::Lru => a.last_access.cmp(&b.last_access),
84 CacheStrategy::Lfu => a.access_count.cmp(&b.access_count),
85 CacheStrategy::Fifo => a.last_access.cmp(&b.last_access),
86 });
87
88 for entry in entries {
89 if freed >= needed_size {
90 break;
91 }
92 to_remove.push(entry.path.clone());
93 freed += entry.size;
94 }
95
96 for path in to_remove {
97 if let Some(entry) = self.cache.remove(&path) {
98 self.current_size -= entry.size;
99 }
100 }
101 }
102
103 #[must_use]
105 pub const fn current_size(&self) -> u64 {
106 self.current_size
107 }
108
109 #[must_use]
111 pub fn utilization(&self) -> f64 {
112 if self.max_size == 0 {
113 0.0
114 } else {
115 (self.current_size as f64 / self.max_size as f64) * 100.0
116 }
117 }
118}
119
120#[derive(Debug, Clone)]
122struct CacheEntry {
123 path: PathBuf,
124 size: u64,
125 access_count: u64,
126 last_access: i64,
127}
128
129fn current_timestamp() -> i64 {
130 std::time::SystemTime::now()
131 .duration_since(std::time::UNIX_EPOCH)
132 .expect("infallible: system clock is always after UNIX_EPOCH")
133 .as_secs() as i64
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_cache_manager() {
142 let temp_dir = std::env::temp_dir();
143 let mut manager = CacheManager::new(temp_dir, 1000);
144
145 manager.add(PathBuf::from("proxy1.mp4"), 100);
146 assert_eq!(manager.current_size(), 100);
147
148 manager.add(PathBuf::from("proxy2.mp4"), 200);
149 assert_eq!(manager.current_size(), 300);
150 }
151
152 #[test]
153 fn test_cache_eviction() {
154 let temp_dir = std::env::temp_dir();
155 let mut manager = CacheManager::new(temp_dir, 500);
156
157 manager.add(PathBuf::from("proxy1.mp4"), 200);
158 manager.add(PathBuf::from("proxy2.mp4"), 200);
159 manager.add(PathBuf::from("proxy3.mp4"), 200);
160
161 assert!(manager.current_size() <= 500);
163 }
164}