Skip to main content

mcraw_tui/preview/
state.rs

1use std::collections::HashMap;
2use std::time::Instant;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub struct FrameKey {
6    pub timestamp_ns: i64,
7}
8
9#[derive(Debug)]
10pub struct CachedFrame {
11    pub timestamp_ns: i64,
12    pub bayer: Vec<u16>,
13    pub last_access: Instant,
14}
15
16impl CachedFrame {
17    pub fn byte_size(&self) -> usize {
18        self.bayer.len() * 2
19    }
20}
21
22pub struct FrameCache {
23    entries: HashMap<FrameKey, CachedFrame>,
24    max_bytes: usize,
25    current_bytes: usize,
26}
27
28impl FrameCache {
29    pub fn new(max_bytes_mb: usize) -> Self {
30        Self {
31            entries: HashMap::new(),
32            max_bytes: max_bytes_mb * 1024 * 1024,
33            current_bytes: 0,
34        }
35    }
36
37    pub fn get(&mut self, key: &FrameKey) -> Option<&CachedFrame> {
38        if let Some(frame) = self.entries.get_mut(key) {
39            frame.last_access = Instant::now();
40            return Some(frame);
41        }
42        None
43    }
44
45    pub fn insert(&mut self, frame: CachedFrame) {
46        let key = FrameKey { timestamp_ns: frame.timestamp_ns };
47        let frame_bytes = frame.byte_size();
48
49        if let Some(old) = self.entries.remove(&key) {
50            self.current_bytes -= old.byte_size();
51        }
52
53        while self.current_bytes + frame_bytes > self.max_bytes && !self.entries.is_empty() {
54            if let Some(oldest_key) = self.entries
55                .iter()
56                .min_by_key(|(_, v)| v.last_access)
57                .map(|(k, _)| *k)
58            {
59                if let Some(old) = self.entries.remove(&oldest_key) {
60                    self.current_bytes -= old.byte_size();
61                }
62            }
63        }
64
65        self.current_bytes += frame_bytes;
66        self.entries.insert(key, frame);
67    }
68
69    pub fn clear(&mut self) {
70        self.entries.clear();
71        self.current_bytes = 0;
72    }
73
74    pub fn current_bytes(&self) -> usize {
75        self.current_bytes
76    }
77
78    pub fn len(&self) -> usize {
79        self.entries.len()
80    }
81
82    pub fn is_empty(&self) -> bool {
83        self.entries.is_empty()
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn cache_eviction() {
93        let mut cache = FrameCache::new(1); // 1 MB
94        let small_frame = CachedFrame {
95            timestamp_ns: 0,
96            bayer: vec![0u16; 100], // 200 bytes
97            last_access: Instant::now(),
98        };
99        cache.insert(small_frame);
100        assert_eq!(cache.len(), 1);
101
102        let key = FrameKey { timestamp_ns: 0 };
103        assert!(cache.get(&key).is_some());
104    }
105
106    #[test]
107    fn cache_clear() {
108        let mut cache = FrameCache::new(256);
109        let frame = CachedFrame {
110            timestamp_ns: 42,
111            bayer: vec![0u16; 1000],
112            last_access: Instant::now(),
113        };
114        cache.insert(frame);
115        assert!(!cache.is_empty());
116        cache.clear();
117        assert!(cache.is_empty());
118        assert_eq!(cache.current_bytes(), 0);
119    }
120}