1use std::collections::HashMap;
2
3pub struct SieveCache<V> {
10 entries: Vec<SieveEntry<V>>,
11 index: HashMap<u64, usize>,
13 hand: usize,
15 len: usize,
17 capacity: usize,
18}
19
20struct SieveEntry<V> {
21 key: u64,
22 value: V,
23 visited: bool,
24 dirty: bool,
25 occupied: bool,
26}
27
28impl<V> SieveEntry<V> {
29 fn empty(value: V) -> Self {
30 Self {
31 key: 0,
32 value,
33 visited: false,
34 dirty: false,
35 occupied: false,
36 }
37 }
38}
39
40impl<V: Default> SieveCache<V> {
41 pub fn new(capacity: usize) -> Self {
42 assert!(capacity > 0, "cache capacity must be > 0");
43 let mut entries = Vec::with_capacity(capacity);
44 for _ in 0..capacity {
45 entries.push(SieveEntry::empty(V::default()));
46 }
47 Self {
48 entries,
49 index: HashMap::with_capacity(capacity),
50 hand: 0,
51 len: 0,
52 capacity,
53 }
54 }
55
56 pub fn get(&mut self, key: u64) -> Option<&V> {
57 if let Some(&idx) = self.index.get(&key) {
58 self.entries[idx].visited = true;
59 Some(&self.entries[idx].value)
60 } else {
61 None
62 }
63 }
64
65 pub fn get_mut(&mut self, key: u64) -> Option<&mut V> {
66 if let Some(&idx) = self.index.get(&key) {
67 self.entries[idx].visited = true;
68 Some(&mut self.entries[idx].value)
69 } else {
70 None
71 }
72 }
73
74 pub fn contains(&self, key: u64) -> bool {
75 self.index.contains_key(&key)
76 }
77
78 #[allow(clippy::result_unit_err)]
80 pub fn insert(&mut self, key: u64, value: V) -> Result<Option<(u64, V)>, ()> {
81 if let Some(&idx) = self.index.get(&key) {
82 self.entries[idx].value = value;
83 self.entries[idx].visited = true;
84 return Ok(None);
85 }
86
87 if self.len < self.capacity {
88 let idx = self.find_empty_slot();
89 self.entries[idx].key = key;
90 self.entries[idx].value = value;
91 self.entries[idx].visited = true;
92 self.entries[idx].dirty = false;
93 self.entries[idx].occupied = true;
94 self.index.insert(key, idx);
95 self.len += 1;
96 return Ok(None);
97 }
98
99 let evicted = self.evict()?;
100 let idx = self.find_empty_slot();
101 self.entries[idx].key = key;
102 self.entries[idx].value = value;
103 self.entries[idx].visited = true;
104 self.entries[idx].dirty = false;
105 self.entries[idx].occupied = true;
106 self.index.insert(key, idx);
107 self.len += 1;
108
109 Ok(Some(evicted))
110 }
111
112 fn evict(&mut self) -> Result<(u64, V), ()> {
113 let mut scanned = 0;
114
115 loop {
116 if scanned >= self.capacity * 2 {
117 return Err(());
119 }
120
121 let idx = self.hand;
122 self.hand = (self.hand + 1) % self.capacity;
123 scanned += 1;
124
125 if !self.entries[idx].occupied {
126 continue;
127 }
128
129 if self.entries[idx].dirty {
130 continue;
131 }
132
133 if self.entries[idx].visited {
134 self.entries[idx].visited = false;
135 continue;
136 }
137
138 let evicted_key = self.entries[idx].key;
139 let evicted_value = std::mem::take(&mut self.entries[idx].value);
140 self.entries[idx].occupied = false;
141 self.index.remove(&evicted_key);
142 self.len -= 1;
143
144 return Ok((evicted_key, evicted_value));
145 }
146 }
147
148 fn find_empty_slot(&self) -> usize {
149 for (i, entry) in self.entries.iter().enumerate() {
150 if !entry.occupied {
151 return i;
152 }
153 }
154 unreachable!("find_empty_slot called when cache is full");
155 }
156
157 pub fn set_dirty(&mut self, key: u64) {
158 if let Some(&idx) = self.index.get(&key) {
159 self.entries[idx].dirty = true;
160 }
161 }
162
163 pub fn clear_dirty(&mut self, key: u64) {
164 if let Some(&idx) = self.index.get(&key) {
165 self.entries[idx].dirty = false;
166 }
167 }
168
169 pub fn is_dirty(&self, key: u64) -> bool {
170 self.index
171 .get(&key)
172 .map(|&idx| self.entries[idx].dirty)
173 .unwrap_or(false)
174 }
175
176 pub fn dirty_entries(&self) -> impl Iterator<Item = (u64, &V)> {
177 self.entries
178 .iter()
179 .filter(|e| e.occupied && e.dirty)
180 .map(|e| (e.key, &e.value))
181 }
182
183 pub fn dirty_entries_mut(&mut self) -> impl Iterator<Item = (u64, &mut V)> {
184 self.entries
185 .iter_mut()
186 .filter(|e| e.occupied && e.dirty)
187 .map(|e| (e.key, &mut e.value))
188 }
189
190 pub fn clear_all_dirty(&mut self) {
191 for entry in &mut self.entries {
192 if entry.occupied {
193 entry.dirty = false;
194 }
195 }
196 }
197
198 pub fn remove(&mut self, key: u64) -> Option<V> {
199 if let Some(idx) = self.index.remove(&key) {
200 let value = std::mem::take(&mut self.entries[idx].value);
201 self.entries[idx].occupied = false;
202 self.len -= 1;
203 Some(value)
204 } else {
205 None
206 }
207 }
208
209 pub fn len(&self) -> usize {
210 self.len
211 }
212
213 pub fn is_empty(&self) -> bool {
214 self.len == 0
215 }
216
217 pub fn capacity(&self) -> usize {
218 self.capacity
219 }
220
221 pub fn dirty_count(&self) -> usize {
222 self.entries
223 .iter()
224 .filter(|e| e.occupied && e.dirty)
225 .count()
226 }
227
228 pub fn clear(&mut self) {
229 for entry in &mut self.entries {
230 entry.occupied = false;
231 entry.visited = false;
232 entry.dirty = false;
233 }
234 self.index.clear();
235 self.len = 0;
236 self.hand = 0;
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[test]
245 fn basic_insert_and_get() {
246 let mut cache = SieveCache::<u32>::new(4);
247 cache.insert(1, 100).unwrap();
248 cache.insert(2, 200).unwrap();
249
250 assert_eq!(cache.get(1), Some(&100));
251 assert_eq!(cache.get(2), Some(&200));
252 assert_eq!(cache.get(3), None);
253 }
254
255 #[test]
256 fn eviction_when_full() {
257 let mut cache = SieveCache::<u32>::new(3);
258 cache.insert(1, 100).unwrap();
259 cache.insert(2, 200).unwrap();
260 cache.insert(3, 300).unwrap();
261 assert_eq!(cache.len(), 3);
262
263 for entry in &mut cache.entries {
266 entry.visited = false;
267 }
268
269 let result = cache.insert(4, 400).unwrap();
270 assert!(result.is_some());
271 assert_eq!(cache.len(), 3);
272 }
273
274 #[test]
275 fn visited_entries_survive_eviction() {
276 let mut cache = SieveCache::<u32>::new(3);
277 cache.insert(1, 100).unwrap();
278 cache.insert(2, 200).unwrap();
279 cache.insert(3, 300).unwrap();
280
281 for entry in &mut cache.entries {
283 entry.visited = false;
284 }
285
286 cache.get(2);
288
289 let evicted = cache.insert(4, 400).unwrap().unwrap();
291 assert_ne!(evicted.0, 2, "visited entry should not be evicted");
292 assert!(cache.contains(2));
293 assert!(cache.contains(4));
294 }
295
296 #[test]
297 fn dirty_entries_not_evicted() {
298 let mut cache = SieveCache::<u32>::new(2);
299 cache.insert(1, 100).unwrap();
300 cache.insert(2, 200).unwrap();
301
302 for entry in &mut cache.entries {
304 entry.visited = false;
305 }
306
307 cache.set_dirty(1);
309
310 let evicted = cache.insert(3, 300).unwrap().unwrap();
312 assert_eq!(evicted.0, 2);
313 assert!(cache.contains(1));
314 assert!(cache.contains(3));
315 }
316
317 #[test]
318 fn all_dirty_returns_err() {
319 let mut cache = SieveCache::<u32>::new(2);
320 cache.insert(1, 100).unwrap();
321 cache.insert(2, 200).unwrap();
322
323 cache.set_dirty(1);
324 cache.set_dirty(2);
325
326 for entry in &mut cache.entries {
328 entry.visited = false;
329 }
330
331 let result = cache.insert(3, 300);
332 assert!(result.is_err());
333 }
334
335 #[test]
336 fn clear_dirty() {
337 let mut cache = SieveCache::<u32>::new(2);
338 cache.insert(1, 100).unwrap();
339 cache.set_dirty(1);
340 assert!(cache.is_dirty(1));
341
342 cache.clear_dirty(1);
343 assert!(!cache.is_dirty(1));
344 }
345
346 #[test]
347 fn dirty_entries_iterator() {
348 let mut cache = SieveCache::<u32>::new(4);
349 cache.insert(1, 100).unwrap();
350 cache.insert(2, 200).unwrap();
351 cache.insert(3, 300).unwrap();
352
353 cache.set_dirty(1);
354 cache.set_dirty(3);
355
356 let dirty: Vec<_> = cache.dirty_entries().collect();
357 assert_eq!(dirty.len(), 2);
358 assert_eq!(cache.dirty_count(), 2);
359 }
360
361 #[test]
362 fn clear_all_dirty() {
363 let mut cache = SieveCache::<u32>::new(3);
364 cache.insert(1, 100).unwrap();
365 cache.insert(2, 200).unwrap();
366 cache.set_dirty(1);
367 cache.set_dirty(2);
368
369 cache.clear_all_dirty();
370 assert_eq!(cache.dirty_count(), 0);
371 }
372
373 #[test]
374 fn remove_entry() {
375 let mut cache = SieveCache::<u32>::new(4);
376 cache.insert(1, 100).unwrap();
377 cache.insert(2, 200).unwrap();
378
379 let removed = cache.remove(1);
380 assert_eq!(removed, Some(100));
381 assert!(!cache.contains(1));
382 assert_eq!(cache.len(), 1);
383 }
384
385 #[test]
386 fn update_existing_key() {
387 let mut cache = SieveCache::<u32>::new(4);
388 cache.insert(1, 100).unwrap();
389 cache.insert(1, 200).unwrap();
390
391 assert_eq!(cache.get(1), Some(&200));
392 assert_eq!(cache.len(), 1);
393 }
394}