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> {
58 if let Some(&idx) = self.index.get(&key) {
59 self.entries[idx].visited = true;
60 Some(&self.entries[idx].value)
61 } else {
62 None
63 }
64 }
65
66 pub fn get_mut(&mut self, key: u64) -> Option<&mut V> {
68 if let Some(&idx) = self.index.get(&key) {
69 self.entries[idx].visited = true;
70 Some(&mut self.entries[idx].value)
71 } else {
72 None
73 }
74 }
75
76 pub fn contains(&self, key: u64) -> bool {
78 self.index.contains_key(&key)
79 }
80
81 #[allow(clippy::result_unit_err)]
87 pub fn insert(&mut self, key: u64, value: V) -> Result<Option<(u64, V)>, ()> {
88 if let Some(&idx) = self.index.get(&key) {
90 self.entries[idx].value = value;
91 self.entries[idx].visited = true;
92 return Ok(None);
93 }
94
95 if self.len < self.capacity {
97 let idx = self.find_empty_slot();
98 self.entries[idx].key = key;
99 self.entries[idx].value = value;
100 self.entries[idx].visited = true;
101 self.entries[idx].dirty = false;
102 self.entries[idx].occupied = true;
103 self.index.insert(key, idx);
104 self.len += 1;
105 return Ok(None);
106 }
107
108 let evicted = self.evict()?;
110
111 let idx = self.find_empty_slot();
113 self.entries[idx].key = key;
114 self.entries[idx].value = value;
115 self.entries[idx].visited = true;
116 self.entries[idx].dirty = false;
117 self.entries[idx].occupied = true;
118 self.index.insert(key, idx);
119 self.len += 1;
120
121 Ok(Some(evicted))
122 }
123
124 fn evict(&mut self) -> Result<(u64, V), ()> {
128 let mut scanned = 0;
129
130 loop {
131 if scanned >= self.capacity * 2 {
132 return Err(());
134 }
135
136 let idx = self.hand;
137 self.hand = (self.hand + 1) % self.capacity;
138 scanned += 1;
139
140 if !self.entries[idx].occupied {
141 continue;
142 }
143
144 if self.entries[idx].dirty {
145 continue;
147 }
148
149 if self.entries[idx].visited {
150 self.entries[idx].visited = false;
152 continue;
153 }
154
155 let evicted_key = self.entries[idx].key;
157 let evicted_value = std::mem::take(&mut self.entries[idx].value);
158 self.entries[idx].occupied = false;
159 self.index.remove(&evicted_key);
160 self.len -= 1;
161
162 return Ok((evicted_key, evicted_value));
163 }
164 }
165
166 fn find_empty_slot(&self) -> usize {
167 for (i, entry) in self.entries.iter().enumerate() {
168 if !entry.occupied {
169 return i;
170 }
171 }
172 unreachable!("find_empty_slot called when cache is full");
173 }
174
175 pub fn set_dirty(&mut self, key: u64) {
177 if let Some(&idx) = self.index.get(&key) {
178 self.entries[idx].dirty = true;
179 }
180 }
181
182 pub fn clear_dirty(&mut self, key: u64) {
184 if let Some(&idx) = self.index.get(&key) {
185 self.entries[idx].dirty = false;
186 }
187 }
188
189 pub fn is_dirty(&self, key: u64) -> bool {
191 self.index
192 .get(&key)
193 .map(|&idx| self.entries[idx].dirty)
194 .unwrap_or(false)
195 }
196
197 pub fn dirty_entries(&self) -> impl Iterator<Item = (u64, &V)> {
199 self.entries
200 .iter()
201 .filter(|e| e.occupied && e.dirty)
202 .map(|e| (e.key, &e.value))
203 }
204
205 pub fn dirty_entries_mut(&mut self) -> impl Iterator<Item = (u64, &mut V)> {
207 self.entries
208 .iter_mut()
209 .filter(|e| e.occupied && e.dirty)
210 .map(|e| (e.key, &mut e.value))
211 }
212
213 pub fn clear_all_dirty(&mut self) {
215 for entry in &mut self.entries {
216 if entry.occupied {
217 entry.dirty = false;
218 }
219 }
220 }
221
222 pub fn remove(&mut self, key: u64) -> Option<V> {
224 if let Some(idx) = self.index.remove(&key) {
225 let value = std::mem::take(&mut self.entries[idx].value);
226 self.entries[idx].occupied = false;
227 self.len -= 1;
228 Some(value)
229 } else {
230 None
231 }
232 }
233
234 pub fn len(&self) -> usize {
235 self.len
236 }
237
238 pub fn is_empty(&self) -> bool {
239 self.len == 0
240 }
241
242 pub fn capacity(&self) -> usize {
243 self.capacity
244 }
245
246 pub fn dirty_count(&self) -> usize {
248 self.entries
249 .iter()
250 .filter(|e| e.occupied && e.dirty)
251 .count()
252 }
253
254 pub fn clear(&mut self) {
256 for entry in &mut self.entries {
257 entry.occupied = false;
258 entry.visited = false;
259 entry.dirty = false;
260 }
261 self.index.clear();
262 self.len = 0;
263 self.hand = 0;
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270
271 #[test]
272 fn basic_insert_and_get() {
273 let mut cache = SieveCache::<u32>::new(4);
274 cache.insert(1, 100).unwrap();
275 cache.insert(2, 200).unwrap();
276
277 assert_eq!(cache.get(1), Some(&100));
278 assert_eq!(cache.get(2), Some(&200));
279 assert_eq!(cache.get(3), None);
280 }
281
282 #[test]
283 fn eviction_when_full() {
284 let mut cache = SieveCache::<u32>::new(3);
285 cache.insert(1, 100).unwrap();
286 cache.insert(2, 200).unwrap();
287 cache.insert(3, 300).unwrap();
288 assert_eq!(cache.len(), 3);
289
290 for entry in &mut cache.entries {
293 entry.visited = false;
294 }
295
296 let result = cache.insert(4, 400).unwrap();
297 assert!(result.is_some());
298 assert_eq!(cache.len(), 3);
299 }
300
301 #[test]
302 fn visited_entries_survive_eviction() {
303 let mut cache = SieveCache::<u32>::new(3);
304 cache.insert(1, 100).unwrap();
305 cache.insert(2, 200).unwrap();
306 cache.insert(3, 300).unwrap();
307
308 for entry in &mut cache.entries {
310 entry.visited = false;
311 }
312
313 cache.get(2);
315
316 let evicted = cache.insert(4, 400).unwrap().unwrap();
318 assert_ne!(evicted.0, 2, "visited entry should not be evicted");
319 assert!(cache.contains(2));
320 assert!(cache.contains(4));
321 }
322
323 #[test]
324 fn dirty_entries_not_evicted() {
325 let mut cache = SieveCache::<u32>::new(2);
326 cache.insert(1, 100).unwrap();
327 cache.insert(2, 200).unwrap();
328
329 for entry in &mut cache.entries {
331 entry.visited = false;
332 }
333
334 cache.set_dirty(1);
336
337 let evicted = cache.insert(3, 300).unwrap().unwrap();
339 assert_eq!(evicted.0, 2);
340 assert!(cache.contains(1));
341 assert!(cache.contains(3));
342 }
343
344 #[test]
345 fn all_dirty_returns_err() {
346 let mut cache = SieveCache::<u32>::new(2);
347 cache.insert(1, 100).unwrap();
348 cache.insert(2, 200).unwrap();
349
350 cache.set_dirty(1);
351 cache.set_dirty(2);
352
353 for entry in &mut cache.entries {
355 entry.visited = false;
356 }
357
358 let result = cache.insert(3, 300);
359 assert!(result.is_err());
360 }
361
362 #[test]
363 fn clear_dirty() {
364 let mut cache = SieveCache::<u32>::new(2);
365 cache.insert(1, 100).unwrap();
366 cache.set_dirty(1);
367 assert!(cache.is_dirty(1));
368
369 cache.clear_dirty(1);
370 assert!(!cache.is_dirty(1));
371 }
372
373 #[test]
374 fn dirty_entries_iterator() {
375 let mut cache = SieveCache::<u32>::new(4);
376 cache.insert(1, 100).unwrap();
377 cache.insert(2, 200).unwrap();
378 cache.insert(3, 300).unwrap();
379
380 cache.set_dirty(1);
381 cache.set_dirty(3);
382
383 let dirty: Vec<_> = cache.dirty_entries().collect();
384 assert_eq!(dirty.len(), 2);
385 assert_eq!(cache.dirty_count(), 2);
386 }
387
388 #[test]
389 fn clear_all_dirty() {
390 let mut cache = SieveCache::<u32>::new(3);
391 cache.insert(1, 100).unwrap();
392 cache.insert(2, 200).unwrap();
393 cache.set_dirty(1);
394 cache.set_dirty(2);
395
396 cache.clear_all_dirty();
397 assert_eq!(cache.dirty_count(), 0);
398 }
399
400 #[test]
401 fn remove_entry() {
402 let mut cache = SieveCache::<u32>::new(4);
403 cache.insert(1, 100).unwrap();
404 cache.insert(2, 200).unwrap();
405
406 let removed = cache.remove(1);
407 assert_eq!(removed, Some(100));
408 assert!(!cache.contains(1));
409 assert_eq!(cache.len(), 1);
410 }
411
412 #[test]
413 fn update_existing_key() {
414 let mut cache = SieveCache::<u32>::new(4);
415 cache.insert(1, 100).unwrap();
416 cache.insert(1, 200).unwrap();
417
418 assert_eq!(cache.get(1), Some(&200));
419 assert_eq!(cache.len(), 1);
420 }
421}