1extern crate alloc;
41
42use crate::config::LruCacheConfig;
43use crate::list::{Entry, List};
44use crate::metrics::{CacheMetrics, LruCacheMetrics};
45use alloc::boxed::Box;
46use alloc::collections::BTreeMap;
47use alloc::string::String;
48use core::borrow::Borrow;
49use core::hash::{BuildHasher, Hash};
50use core::mem;
51use core::num::NonZeroUsize;
52
53#[cfg(feature = "hashbrown")]
54use hashbrown::hash_map::DefaultHashBuilder;
55#[cfg(feature = "hashbrown")]
56use hashbrown::HashMap;
57
58#[cfg(not(feature = "hashbrown"))]
59use std::collections::hash_map::RandomState as DefaultHashBuilder;
60#[cfg(not(feature = "hashbrown"))]
61use std::collections::HashMap;
62
63pub(crate) struct LruSegment<K, V, S = DefaultHashBuilder> {
77 config: LruCacheConfig,
78 list: List<(K, V)>,
79 map: HashMap<K, *mut Entry<(K, V)>, S>,
80 metrics: LruCacheMetrics,
81}
82
83unsafe impl<K: Send, V: Send, S: Send> Send for LruSegment<K, V, S> {}
86
87unsafe impl<K: Send, V: Send, S: Sync> Sync for LruSegment<K, V, S> {}
89
90impl<K: Hash + Eq, V: Clone, S: BuildHasher> LruSegment<K, V, S> {
91 pub(crate) fn with_hasher(cap: NonZeroUsize, hash_builder: S) -> Self {
92 Self::with_hasher_and_size(cap, hash_builder, cap.get() as u64 * 1024)
93 }
94
95 pub(crate) fn with_hasher_and_size(
96 cap: NonZeroUsize,
97 hash_builder: S,
98 max_size_bytes: u64,
99 ) -> Self {
100 let map_capacity = cap.get().next_power_of_two();
101 let config = LruCacheConfig::new(cap);
102 LruSegment {
103 config,
104 list: List::new(cap),
105 map: HashMap::with_capacity_and_hasher(map_capacity, hash_builder),
106 metrics: LruCacheMetrics::new(max_size_bytes),
107 }
108 }
109
110 #[inline]
111 pub(crate) fn cap(&self) -> NonZeroUsize {
112 self.config.capacity()
113 }
114
115 #[inline]
116 pub(crate) fn len(&self) -> usize {
117 self.map.len()
118 }
119
120 #[inline]
121 pub(crate) fn is_empty(&self) -> bool {
122 self.map.is_empty()
123 }
124
125 #[inline]
126 pub(crate) fn metrics(&self) -> &LruCacheMetrics {
127 &self.metrics
128 }
129
130 fn estimate_object_size(&self, _key: &K, _value: &V) -> u64 {
131 mem::size_of::<K>() as u64 + mem::size_of::<V>() as u64 + 64
132 }
133
134 pub(crate) fn get<Q>(&mut self, key: &Q) -> Option<&V>
135 where
136 K: Borrow<Q>,
137 Q: ?Sized + Hash + Eq,
138 {
139 if let Some(node) = self.map.get(key).copied() {
140 unsafe {
141 self.list.move_to_front(node);
143 let (k, v) = (*node).get_value();
144 let object_size = self.estimate_object_size(k, v);
145 self.metrics.core.record_hit(object_size);
146 Some(v)
147 }
148 } else {
149 None
150 }
151 }
152
153 #[inline]
154 pub(crate) fn record_miss(&mut self, object_size: u64) {
155 self.metrics.core.record_miss(object_size);
156 }
157
158 pub(crate) fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
159 where
160 K: Borrow<Q>,
161 Q: ?Sized + Hash + Eq,
162 {
163 let node = self.map.get(key).copied()?;
164 unsafe {
165 self.list.move_to_front(node);
167 let (k, v) = (*node).get_value_mut();
168 let object_size = self.estimate_object_size(k, v);
169 self.metrics.core.record_hit(object_size);
170 Some(v)
171 }
172 }
173
174 pub(crate) fn put(&mut self, key: K, value: V) -> Option<(K, V)>
175 where
176 K: Clone,
177 {
178 let mut evicted = None;
179 let object_size = self.estimate_object_size(&key, &value);
180
181 if let Some(&node) = self.map.get(&key) {
182 unsafe {
183 self.list.move_to_front(node);
185 let (k, old_value) = self.list.update(node, (key, value), true).0?;
186 return Some((k, old_value));
187 }
188 }
189
190 if self.map.len() >= self.cap().get() {
191 if let Some(old_entry) = self.list.remove_last() {
192 unsafe {
193 let entry_ptr = Box::into_raw(old_entry);
194 let key_ref = &(*entry_ptr).get_value().0;
195 self.map.remove(key_ref);
196 let key = (*entry_ptr).get_value().0.clone();
197 let value = (*entry_ptr).get_value().1.clone();
198 let evicted_size = self.estimate_object_size(&key, &value);
199 self.metrics.core.record_eviction(evicted_size);
200 evicted = Some((key, value));
201 let _ = Box::from_raw(entry_ptr);
202 }
203 }
204 }
205
206 if let Some(node) = self.list.add((key.clone(), value)) {
207 self.map.insert(key, node);
208 self.metrics.core.record_insertion(object_size);
209 }
210
211 evicted
212 }
213
214 pub(crate) fn remove<Q>(&mut self, key: &Q) -> Option<V>
215 where
216 K: Borrow<Q>,
217 Q: ?Sized + Hash + Eq,
218 V: Clone,
219 {
220 let node = self.map.remove(key)?;
221 unsafe {
222 let (k, v) = (*node).get_value();
224 let object_size = self.estimate_object_size(k, v);
225 let value = v.clone();
226 self.list.remove(node);
227 self.metrics.core.record_eviction(object_size);
228 Some(value)
229 }
230 }
231
232 pub(crate) fn clear(&mut self) {
233 self.metrics.core.cache_size_bytes = 0;
234 self.map.clear();
235 self.list.clear();
236 }
237}
238
239impl<K, V, S> core::fmt::Debug for LruSegment<K, V, S> {
240 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
241 f.debug_struct("LruSegment")
242 .field("capacity", &self.config.capacity())
243 .field("len", &self.map.len())
244 .finish()
245 }
246}
247
248#[derive(Debug)]
276pub struct LruCache<K, V, S = DefaultHashBuilder> {
277 segment: LruSegment<K, V, S>,
278}
279
280impl<K: Hash + Eq, V: Clone, S: BuildHasher> LruCache<K, V, S> {
281 pub fn with_hasher(cap: NonZeroUsize, hash_builder: S) -> Self {
283 Self {
284 segment: LruSegment::with_hasher(cap, hash_builder),
285 }
286 }
287
288 pub fn with_hasher_and_size(cap: NonZeroUsize, hash_builder: S, max_size_bytes: u64) -> Self {
290 Self {
291 segment: LruSegment::with_hasher_and_size(cap, hash_builder, max_size_bytes),
292 }
293 }
294
295 #[inline]
296 pub fn cap(&self) -> NonZeroUsize {
297 self.segment.cap()
298 }
299
300 #[inline]
301 pub fn len(&self) -> usize {
302 self.segment.len()
303 }
304
305 #[inline]
306 pub fn is_empty(&self) -> bool {
307 self.segment.is_empty()
308 }
309
310 #[inline]
311 pub fn get<Q>(&mut self, key: &Q) -> Option<&V>
312 where
313 K: Borrow<Q>,
314 Q: ?Sized + Hash + Eq,
315 {
316 self.segment.get(key)
317 }
318
319 #[inline]
320 pub fn record_miss(&mut self, object_size: u64) {
321 self.segment.record_miss(object_size);
322 }
323
324 #[inline]
325 pub fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
326 where
327 K: Borrow<Q>,
328 Q: ?Sized + Hash + Eq,
329 {
330 self.segment.get_mut(key)
331 }
332}
333
334impl<K: Hash + Eq + Clone, V: Clone, S: BuildHasher> LruCache<K, V, S> {
335 #[inline]
336 pub fn put(&mut self, key: K, value: V) -> Option<(K, V)> {
337 self.segment.put(key, value)
338 }
339
340 #[inline]
341 pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
342 where
343 K: Borrow<Q>,
344 Q: ?Sized + Hash + Eq,
345 {
346 self.segment.remove(key)
347 }
348
349 #[inline]
350 pub fn clear(&mut self) {
351 self.segment.clear()
352 }
353
354 pub fn iter(&self) -> Iter<'_, K, V> {
355 unimplemented!("Iteration not yet implemented")
356 }
357
358 pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
359 unimplemented!("Mutable iteration not yet implemented")
360 }
361}
362
363impl<K: Hash + Eq, V> LruCache<K, V>
364where
365 V: Clone,
366{
367 pub fn new(cap: NonZeroUsize) -> LruCache<K, V, DefaultHashBuilder> {
368 LruCache::with_hasher(cap, DefaultHashBuilder::default())
369 }
370}
371
372impl<K: Hash + Eq, V: Clone, S: BuildHasher> CacheMetrics for LruCache<K, V, S> {
373 fn metrics(&self) -> BTreeMap<String, f64> {
374 self.segment.metrics().metrics()
375 }
376
377 fn algorithm_name(&self) -> &'static str {
378 self.segment.metrics().algorithm_name()
379 }
380}
381
382pub struct Iter<'a, K, V> {
383 _marker: core::marker::PhantomData<&'a (K, V)>,
384}
385
386pub struct IterMut<'a, K, V> {
387 _marker: core::marker::PhantomData<&'a mut (K, V)>,
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393 use alloc::string::String;
394
395 #[test]
396 fn test_lru_get_put() {
397 let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
398 assert_eq!(cache.put("apple", 1), None);
399 assert_eq!(cache.put("banana", 2), None);
400 assert_eq!(cache.get(&"apple"), Some(&1));
401 assert_eq!(cache.get(&"banana"), Some(&2));
402 assert_eq!(cache.get(&"cherry"), None);
403 assert_eq!(cache.put("apple", 3).unwrap().1, 1);
404 assert_eq!(cache.get(&"apple"), Some(&3));
405 assert_eq!(cache.put("cherry", 4).unwrap().1, 2);
406 assert_eq!(cache.get(&"banana"), None);
407 assert_eq!(cache.get(&"apple"), Some(&3));
408 assert_eq!(cache.get(&"cherry"), Some(&4));
409 }
410
411 #[test]
412 fn test_lru_get_mut() {
413 let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
414 cache.put("apple", 1);
415 cache.put("banana", 2);
416 if let Some(v) = cache.get_mut(&"apple") {
417 *v = 3;
418 }
419 assert_eq!(cache.get(&"apple"), Some(&3));
420 cache.put("cherry", 4);
421 assert_eq!(cache.get(&"banana"), None);
422 assert_eq!(cache.get(&"apple"), Some(&3));
423 assert_eq!(cache.get(&"cherry"), Some(&4));
424 }
425
426 #[test]
427 fn test_lru_remove() {
428 let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
429 cache.put("apple", 1);
430 cache.put("banana", 2);
431 assert_eq!(cache.get(&"apple"), Some(&1));
432 assert_eq!(cache.get(&"banana"), Some(&2));
433 assert_eq!(cache.get(&"cherry"), None);
434 assert_eq!(cache.remove(&"apple"), Some(1));
435 assert_eq!(cache.get(&"apple"), None);
436 assert_eq!(cache.len(), 1);
437 assert_eq!(cache.remove(&"cherry"), None);
438 let evicted = cache.put("cherry", 3);
439 assert_eq!(evicted, None);
440 assert_eq!(cache.get(&"banana"), Some(&2));
441 assert_eq!(cache.get(&"cherry"), Some(&3));
442 }
443
444 #[test]
445 fn test_lru_clear() {
446 let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
447 cache.put("apple", 1);
448 cache.put("banana", 2);
449 assert_eq!(cache.len(), 2);
450 cache.clear();
451 assert_eq!(cache.len(), 0);
452 assert!(cache.is_empty());
453 cache.put("cherry", 3);
454 assert_eq!(cache.get(&"cherry"), Some(&3));
455 }
456
457 #[test]
458 fn test_lru_capacity_limits() {
459 let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
460 cache.put("apple", 1);
461 cache.put("banana", 2);
462 cache.put("cherry", 3);
463 assert_eq!(cache.len(), 2);
464 assert_eq!(cache.get(&"apple"), None);
465 assert_eq!(cache.get(&"banana"), Some(&2));
466 assert_eq!(cache.get(&"cherry"), Some(&3));
467 }
468
469 #[test]
470 fn test_lru_string_keys() {
471 let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
472 let key1 = String::from("apple");
473 let key2 = String::from("banana");
474 cache.put(key1.clone(), 1);
475 cache.put(key2.clone(), 2);
476 assert_eq!(cache.get(&key1), Some(&1));
477 assert_eq!(cache.get(&key2), Some(&2));
478 assert_eq!(cache.get("apple"), Some(&1));
479 assert_eq!(cache.get("banana"), Some(&2));
480 drop(cache);
481 }
482
483 #[derive(Debug, Clone, Eq, PartialEq)]
484 struct ComplexValue {
485 val: i32,
486 description: String,
487 }
488
489 #[test]
490 fn test_lru_complex_values() {
491 let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
492 let key1 = String::from("apple");
493 let key2 = String::from("banana");
494 let fruit1 = ComplexValue {
495 val: 1,
496 description: String::from("First fruit"),
497 };
498 let fruit2 = ComplexValue {
499 val: 2,
500 description: String::from("Second fruit"),
501 };
502 let fruit3 = ComplexValue {
503 val: 3,
504 description: String::from("Third fruit"),
505 };
506 cache.put(key1.clone(), fruit1.clone());
507 cache.put(key2.clone(), fruit2.clone());
508 assert_eq!(cache.get(&key1).unwrap().val, fruit1.val);
509 assert_eq!(cache.get(&key2).unwrap().val, fruit2.val);
510 let evicted = cache.put(String::from("cherry"), fruit3.clone());
511 let evicted_fruit = evicted.unwrap();
512 assert_eq!(evicted_fruit.1, fruit1);
513 let removed = cache.remove(&key1);
514 assert_eq!(removed, None);
515 }
516
517 #[test]
518 fn test_lru_metrics() {
519 use crate::metrics::CacheMetrics;
520 let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
521 let metrics = cache.metrics();
522 assert_eq!(metrics.get("requests").unwrap(), &0.0);
523 assert_eq!(metrics.get("cache_hits").unwrap(), &0.0);
524 assert_eq!(metrics.get("cache_misses").unwrap(), &0.0);
525 cache.put("apple", 1);
526 cache.put("banana", 2);
527 cache.get(&"apple");
528 cache.get(&"banana");
529 let metrics = cache.metrics();
530 assert_eq!(metrics.get("cache_hits").unwrap(), &2.0);
531 cache.record_miss(64);
532 let metrics = cache.metrics();
533 assert_eq!(metrics.get("cache_misses").unwrap(), &1.0);
534 assert_eq!(metrics.get("requests").unwrap(), &3.0);
535 cache.put("cherry", 3);
536 let metrics = cache.metrics();
537 assert_eq!(metrics.get("evictions").unwrap(), &1.0);
538 assert!(metrics.get("bytes_written_to_cache").unwrap() > &0.0);
539 assert_eq!(cache.algorithm_name(), "LRU");
540 }
541
542 #[test]
543 fn test_lru_segment_directly() {
544 let mut segment: LruSegment<&str, i32, DefaultHashBuilder> =
545 LruSegment::with_hasher(NonZeroUsize::new(2).unwrap(), DefaultHashBuilder::default());
546 assert_eq!(segment.len(), 0);
547 assert!(segment.is_empty());
548 assert_eq!(segment.cap().get(), 2);
549 segment.put("a", 1);
550 segment.put("b", 2);
551 assert_eq!(segment.len(), 2);
552 assert_eq!(segment.get(&"a"), Some(&1));
553 assert_eq!(segment.get(&"b"), Some(&2));
554 }
555
556 #[test]
557 fn test_lru_concurrent_access() {
558 extern crate std;
559 use std::sync::{Arc, Mutex};
560 use std::thread;
561 use std::vec::Vec;
562
563 let cache = Arc::new(Mutex::new(LruCache::new(NonZeroUsize::new(100).unwrap())));
564 let num_threads = 4;
565 let ops_per_thread = 100;
566
567 let mut handles: Vec<std::thread::JoinHandle<()>> = Vec::new();
568
569 for t in 0..num_threads {
571 let cache = Arc::clone(&cache);
572 handles.push(thread::spawn(move || {
573 for i in 0..ops_per_thread {
574 let key = std::format!("thread_{}_key_{}", t, i);
575 let mut guard = cache.lock().unwrap();
576 guard.put(key, t * 1000 + i);
577 }
578 }));
579 }
580
581 for t in 0..num_threads {
583 let cache = Arc::clone(&cache);
584 handles.push(thread::spawn(move || {
585 for i in 0..ops_per_thread {
586 let key = std::format!("thread_{}_key_{}", t, i);
587 let mut guard = cache.lock().unwrap();
588 let _ = guard.get(&key);
589 }
590 }));
591 }
592
593 for handle in handles {
594 handle.join().unwrap();
595 }
596
597 let mut guard = cache.lock().unwrap();
598 assert!(guard.len() <= 100);
599 assert!(!guard.is_empty());
600 guard.clear(); }
602
603 #[test]
604 fn test_lru_high_contention() {
605 extern crate std;
606 use std::sync::{Arc, Mutex};
607 use std::thread;
608 use std::vec::Vec;
609
610 let cache = Arc::new(Mutex::new(LruCache::new(NonZeroUsize::new(50).unwrap())));
611 let num_threads = 8;
612 let ops_per_thread = 500;
613
614 let mut handles: Vec<std::thread::JoinHandle<()>> = Vec::new();
615
616 for t in 0..num_threads {
617 let cache = Arc::clone(&cache);
618 handles.push(thread::spawn(move || {
619 for i in 0..ops_per_thread {
620 let key = std::format!("key_{}", i % 100); let mut guard = cache.lock().unwrap();
622 if i % 2 == 0 {
623 guard.put(key, t * 1000 + i);
624 } else {
625 let _ = guard.get(&key);
626 }
627 }
628 }));
629 }
630
631 for handle in handles {
632 handle.join().unwrap();
633 }
634
635 let mut guard = cache.lock().unwrap();
636 assert!(guard.len() <= 50);
637 guard.clear(); }
639
640 #[test]
641 fn test_lru_concurrent_mixed_operations() {
642 extern crate std;
643 use std::sync::{Arc, Mutex};
644 use std::thread;
645 use std::vec::Vec;
646
647 let cache = Arc::new(Mutex::new(LruCache::new(NonZeroUsize::new(100).unwrap())));
648 let num_threads = 8;
649 let ops_per_thread = 1000;
650
651 let mut handles: Vec<std::thread::JoinHandle<()>> = Vec::new();
652
653 for t in 0..num_threads {
654 let cache = Arc::clone(&cache);
655 handles.push(thread::spawn(move || {
656 for i in 0..ops_per_thread {
657 let key = std::format!("key_{}", i % 200);
658 let mut guard = cache.lock().unwrap();
659
660 match i % 4 {
661 0 => {
662 guard.put(key, i);
663 }
664 1 => {
665 let _ = guard.get(&key);
666 }
667 2 => {
668 let _ = guard.get_mut(&key);
669 }
670 3 => {
671 let _ = guard.remove(&key);
672 }
673 _ => unreachable!(),
674 }
675
676 if i == 500 && t == 0 {
677 guard.clear();
678 }
679 }
680 }));
681 }
682
683 for handle in handles {
684 handle.join().unwrap();
685 }
686
687 let mut guard = cache.lock().unwrap();
688 assert!(guard.len() <= 100);
689 guard.clear(); }
691}