1use std::collections::VecDeque;
12use std::hash::Hash;
13use std::sync::atomic::{AtomicBool, Ordering};
14
15use hashbrown::HashMap;
16
17pub struct SecondChanceLru<K, V> {
42 cache: HashMap<K, (V, AtomicBool)>,
44 queue: VecDeque<K>,
46 capacity: usize,
48}
49
50impl<K: Hash + Eq + Clone, V> SecondChanceLru<K, V> {
51 #[must_use]
56 pub fn new(capacity: usize) -> Self {
57 Self {
58 cache: HashMap::with_capacity(capacity),
59 queue: VecDeque::with_capacity(capacity),
60 capacity,
61 }
62 }
63
64 #[must_use]
68 pub fn get(&self, key: &K) -> Option<&V> {
69 self.cache.get(key).map(|(val, accessed)| {
70 accessed.store(true, Ordering::Relaxed);
72 val
73 })
74 }
75
76 pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
80 self.cache.get_mut(key).map(|(val, accessed)| {
81 accessed.store(true, Ordering::Relaxed);
82 val
83 })
84 }
85
86 #[must_use]
90 pub fn contains_key(&self, key: &K) -> bool {
91 self.cache.contains_key(key)
92 }
93
94 pub fn insert(&mut self, key: K, value: V) {
100 if let Some((existing, accessed)) = self.cache.get_mut(&key) {
102 *existing = value;
103 accessed.store(true, Ordering::Relaxed);
104 return;
105 }
106
107 if self.cache.len() >= self.capacity {
109 self.evict_one();
110 }
111
112 self.cache
114 .insert(key.clone(), (value, AtomicBool::new(false)));
115 self.queue.push_back(key);
116 }
117
118 pub fn remove(&mut self, key: &K) -> Option<V> {
122 self.cache.remove(key).map(|(v, _)| v)
123 }
124
125 fn evict_one(&mut self) {
131 let max_iterations = self.queue.len() * 2;
133
134 for _ in 0..max_iterations {
135 if let Some(key) = self.queue.pop_front() {
136 if let Some((_, accessed)) = self.cache.get(&key) {
137 if accessed.swap(false, Ordering::Relaxed) {
138 self.queue.push_back(key);
140 } else {
141 self.cache.remove(&key);
143 return;
144 }
145 }
146 } else {
148 return; }
150 }
151
152 if let Some(key) = self.queue.pop_front() {
154 self.cache.remove(&key);
155 }
156 }
157
158 #[must_use]
160 pub fn len(&self) -> usize {
161 self.cache.len()
162 }
163
164 #[must_use]
166 pub fn is_empty(&self) -> bool {
167 self.cache.is_empty()
168 }
169
170 pub fn clear(&mut self) {
172 self.cache.clear();
173 self.queue.clear();
174 }
175
176 #[must_use]
178 pub fn capacity(&self) -> usize {
179 self.capacity
180 }
181
182 pub fn keys(&self) -> impl Iterator<Item = &K> {
186 self.cache.keys()
187 }
188
189 pub fn values(&self) -> impl Iterator<Item = &V> {
193 self.cache.values().map(|(v, _)| v)
194 }
195
196 pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
200 self.cache.iter().map(|(k, (v, _))| (k, v))
201 }
202}
203
204impl<K: Hash + Eq + Clone, V> Default for SecondChanceLru<K, V> {
205 fn default() -> Self {
206 Self::new(16)
207 }
208}
209
210impl<K: Hash + Eq + Clone, V: Clone> Clone for SecondChanceLru<K, V> {
211 fn clone(&self) -> Self {
212 let mut cache = HashMap::with_capacity(self.capacity);
213 for (k, (v, accessed)) in &self.cache {
214 cache.insert(
215 k.clone(),
216 (v.clone(), AtomicBool::new(accessed.load(Ordering::Relaxed))),
217 );
218 }
219 Self {
220 cache,
221 queue: self.queue.clone(),
222 capacity: self.capacity,
223 }
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230
231 #[test]
232 fn test_basic_operations() {
233 let mut cache = SecondChanceLru::new(3);
234
235 cache.insert("a", 1);
236 cache.insert("b", 2);
237 cache.insert("c", 3);
238
239 assert_eq!(cache.len(), 3);
240 assert_eq!(*cache.get(&"a").unwrap(), 1);
241 assert_eq!(*cache.get(&"b").unwrap(), 2);
242 assert_eq!(*cache.get(&"c").unwrap(), 3);
243 assert!(cache.get(&"d").is_none());
244 }
245
246 #[test]
247 fn test_second_chance_eviction() {
248 let mut cache = SecondChanceLru::new(3);
249
250 cache.insert("a", 1);
251 cache.insert("b", 2);
252 cache.insert("c", 3);
253
254 let _ = cache.get(&"a");
256
257 cache.insert("d", 4);
259
260 assert!(
261 cache.get(&"a").is_some(),
262 "a should survive (second chance)"
263 );
264 assert!(cache.get(&"b").is_none(), "b should be evicted");
265 assert!(cache.get(&"c").is_some(), "c should survive");
266 assert!(cache.get(&"d").is_some(), "d was just inserted");
267 }
268
269 #[test]
270 fn test_capacity_respected() {
271 let mut cache = SecondChanceLru::new(2);
272
273 cache.insert("a", 1);
274 cache.insert("b", 2);
275 cache.insert("c", 3);
276
277 assert_eq!(cache.len(), 2);
278 assert_eq!(cache.capacity(), 2);
279 }
280
281 #[test]
282 fn test_update_existing() {
283 let mut cache = SecondChanceLru::new(2);
284
285 cache.insert("a", 1);
286 cache.insert("a", 2);
287
288 assert_eq!(cache.len(), 1);
289 assert_eq!(*cache.get(&"a").unwrap(), 2);
290 }
291
292 #[test]
293 fn test_remove() {
294 let mut cache = SecondChanceLru::new(3);
295
296 cache.insert("a", 1);
297 cache.insert("b", 2);
298
299 let removed = cache.remove(&"a");
300 assert_eq!(removed, Some(1));
301 assert!(cache.get(&"a").is_none());
302 assert_eq!(cache.len(), 1);
303 }
304
305 #[test]
306 fn test_clear() {
307 let mut cache = SecondChanceLru::new(3);
308
309 cache.insert("a", 1);
310 cache.insert("b", 2);
311 cache.clear();
312
313 assert!(cache.is_empty());
314 assert_eq!(cache.len(), 0);
315 }
316
317 #[test]
318 fn test_get_mut() {
319 let mut cache = SecondChanceLru::new(3);
320
321 cache.insert("a", 1);
322
323 if let Some(val) = cache.get_mut(&"a") {
324 *val = 10;
325 }
326
327 assert_eq!(*cache.get(&"a").unwrap(), 10);
328 }
329
330 #[test]
331 fn test_contains_key() {
332 let mut cache = SecondChanceLru::new(3);
333
334 cache.insert("a", 1);
335
336 assert!(cache.contains_key(&"a"));
337 assert!(!cache.contains_key(&"b"));
338 }
339
340 #[test]
341 fn test_all_accessed_eviction() {
342 let mut cache = SecondChanceLru::new(2);
343
344 cache.insert("a", 1);
345 cache.insert("b", 2);
346
347 let _ = cache.get(&"a");
349 let _ = cache.get(&"b");
350
351 cache.insert("c", 3);
353
354 assert_eq!(cache.len(), 2);
355 assert!(cache.get(&"c").is_some());
357 }
358
359 #[test]
360 fn test_iterators() {
361 let mut cache = SecondChanceLru::new(3);
362
363 cache.insert("a", 1);
364 cache.insert("b", 2);
365
366 let keys: Vec<_> = cache.keys().collect();
367 assert_eq!(keys.len(), 2);
368
369 let values: Vec<_> = cache.values().collect();
370 assert_eq!(values.len(), 2);
371
372 let pairs: Vec<_> = cache.iter().collect();
373 assert_eq!(pairs.len(), 2);
374 }
375
376 #[test]
377 fn test_clone() {
378 let mut cache = SecondChanceLru::new(3);
379
380 cache.insert("a", 1);
381 cache.insert("b", 2);
382 let _ = cache.get(&"a"); let cloned = cache.clone();
385
386 assert_eq!(cloned.len(), 2);
387 assert_eq!(*cloned.get(&"a").unwrap(), 1);
388 assert_eq!(*cloned.get(&"b").unwrap(), 2);
389 }
390}