1#[cfg(not(feature = "std"))]
59use alloc::{vec, vec::Vec};
60use core::{
61 hash::Hash,
62 num::NonZeroUsize,
63 sync::atomic::{AtomicU8, Ordering},
64};
65use hashbrown::HashTable;
66
67type Hasher = ahash::RandomState;
69
70type Slot = usize;
72
73struct Entry<K, V> {
79 key: K,
80 value: V,
81 state: SlotState,
82 live: bool,
83}
84
85pub struct Cache<K, V> {
92 index: HashTable<Slot>,
95 hasher: Hasher,
97 slots: Vec<Entry<K, V>>,
99 free: Vec<Slot>,
101 capacity: usize,
103 topology: Vec<ResidentSlot>,
105 small: SmallQueue,
107 main: MainRing,
109 ghost: GhostQueue<K>,
111}
112
113impl<K: Hash + Eq, V> Cache<K, V> {
114 pub fn new(capacity: NonZeroUsize) -> Self {
116 let capacity = capacity.get();
117 let small_capacity = if capacity == 1 {
118 0
119 } else {
120 (capacity / 10).max(1)
121 };
122 Self {
123 index: HashTable::with_capacity(capacity.checked_mul(2).expect("capacity overflow")),
124 hasher: Hasher::default(),
125 slots: Vec::with_capacity(capacity),
126 free: Vec::new(),
127 capacity,
128 topology: vec![ResidentSlot::default(); capacity],
129 small: SmallQueue::new(small_capacity),
130 main: MainRing::new(capacity - small_capacity),
131 ghost: GhostQueue::new(capacity / 2),
132 }
133 }
134
135 #[inline]
137 pub const fn capacity(&self) -> usize {
138 self.capacity
139 }
140
141 #[inline]
143 pub fn len(&self) -> usize {
144 self.index.len()
145 }
146
147 #[inline]
149 pub fn is_empty(&self) -> bool {
150 self.index.is_empty()
151 }
152
153 #[inline]
155 pub fn contains(&self, key: &K) -> bool {
156 self.find_slot(key).is_some()
157 }
158
159 #[inline]
163 pub fn peek(&self, key: &K) -> Option<&V> {
164 let slot = self.find_slot(key)?;
165 Some(&self.slots[slot].value)
166 }
167
168 #[inline]
173 pub fn get(&self, key: &K) -> Option<&V> {
174 let index = self.find_slot(key)?;
175 let slot = &self.slots[index];
176 slot.state.record_hit();
177 Some(&slot.value)
178 }
179
180 #[inline]
191 pub fn get_at(&self, slot: usize, key: &K) -> Option<&V> {
192 let resident = self.slots.get(slot)?;
193 if !resident.live || resident.key != *key {
194 return None;
195 }
196
197 resident.state.record_hit();
198 Some(&resident.value)
199 }
200
201 #[inline]
203 pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
204 let index = self.find_slot(key)?;
205 let slot = &mut self.slots[index];
206 slot.state.record_hit_mut();
207 Some(&mut slot.value)
208 }
209
210 pub fn put(&mut self, key: K, value: V) -> Option<V> {
216 let hash = self.hasher.hash_one(&key);
217 if let Some(index) = self.find_slot_hashed(&key, hash) {
218 let slot = &mut self.slots[index];
219 slot.state.record_hit_mut();
220 return Some(core::mem::replace(&mut slot.value, value));
221 }
222 self.insert(key, hash, Some(value), || unreachable!());
223 None
224 }
225
226 pub fn get_or_insert_with<F: FnOnce() -> V>(&mut self, key: K, f: F) -> &V {
233 let hash = self.hasher.hash_one(&key);
234 let slot = match self.find_slot_hashed(&key, hash) {
235 Some(slot) => {
236 self.slots[slot].state.record_hit_mut();
237 slot
238 }
239 None => self.insert(key, hash, Some(f()), || unreachable!()),
240 };
241 &self.slots[slot].value
242 }
243
244 pub fn try_get_or_insert_with<F: FnOnce() -> Result<V, E>, E>(
251 &mut self,
252 key: K,
253 f: F,
254 ) -> Result<&V, E> {
255 let hash = self.hasher.hash_one(&key);
256 let slot = match self.find_slot_hashed(&key, hash) {
257 Some(slot) => {
258 self.slots[slot].state.record_hit_mut();
259 slot
260 }
261 None => self.insert(key, hash, Some(f()?), || unreachable!()),
262 };
263 Ok(&self.slots[slot].value)
264 }
265
266 pub fn get_or_insert_mut<F: FnOnce() -> V>(&mut self, key: K, make: F) -> (usize, &mut V) {
280 let hash = self.hasher.hash_one(&key);
281 let slot = match self.find_slot_hashed(&key, hash) {
282 Some(slot) => {
283 self.slots[slot].state.record_hit_mut();
284 slot
285 }
286 None => self.insert(key, hash, None, make),
287 };
288 (slot, &mut self.slots[slot].value)
289 }
290
291 pub fn remove(&mut self, key: &K) -> bool {
296 let hash = self.hasher.hash_one(key);
297 match self
298 .index
299 .find_entry(hash, |&slot| self.slots[slot].key == *key)
300 {
301 Ok(entry) => {
302 let slot = entry.remove().0;
303 self.unlink_resident(slot);
304 self.slots[slot].live = false;
305 self.free.push(slot);
306 true
307 }
308 Err(_) => {
309 self.ghost.discard(key, hash);
310 false
311 }
312 }
313 }
314
315 pub fn retain<F: FnMut(&K, &V) -> bool>(&mut self, mut keep: F) {
319 let Self {
320 index,
321 slots,
322 free,
323 topology,
324 small,
325 main,
326 ..
327 } = self;
328 index.retain(|&mut slot| {
329 let resident = &mut slots[slot];
330 let keep = keep(&resident.key, &resident.value);
331 if !keep {
332 match topology[slot].location() {
333 Location::Small => small.unlink(topology, slot),
334 Location::Main => main.unlink(topology, slot),
335 Location::Free => unreachable!("resident slot cannot be free"),
336 }
337 resident.live = false;
338 free.push(slot);
339 }
340 keep
341 });
342 }
343
344 pub fn clear(&mut self) {
347 self.index.clear();
348 self.free.clear();
349 self.topology.fill(ResidentSlot::default());
350 self.small.clear();
351 self.main.clear();
352 self.slots.clear();
353 self.ghost.clear();
354 }
355
356 #[inline]
357 fn find_slot(&self, key: &K) -> Option<Slot> {
358 self.find_slot_hashed(key, self.hasher.hash_one(key))
359 }
360
361 #[inline]
362 fn find_slot_hashed(&self, key: &K, hash: u64) -> Option<Slot> {
363 self.index
364 .find(hash, |&slot| self.slots[slot].key == *key)
365 .copied()
366 }
367
368 #[inline]
371 fn index_slot(&mut self, slot: Slot, hash: u64) {
372 let slots = &self.slots;
373 let hasher = &self.hasher;
374 let _ = self
375 .index
376 .insert_unique(hash, slot, |&slot| hasher.hash_one(&slots[slot].key));
377 }
378
379 fn insert<F: FnOnce() -> V>(&mut self, key: K, hash: u64, value: Option<V>, make: F) -> Slot {
382 let ghost_hit = self.ghost.discard(&key, hash);
383 let has_vacancy = !self.free.is_empty() || self.slots.len() < self.capacity;
384 let admission = self.admission(ghost_hit, has_vacancy);
385 let plan = self.plan(admission, has_vacancy);
386
387 let (slot, historical_hash) = if let Some(victim) = plan.victim() {
390 let victim_hash = self.hasher.hash_one(&self.slots[victim].key);
391 self.index
392 .find_entry(victim_hash, |&slot| slot == victim)
393 .expect("a live victim must be indexed")
394 .remove();
395 let location = self.unlink_resident(victim);
396 (victim, (location == Location::Small).then_some(victim_hash))
397 } else {
398 (self.free.pop().unwrap_or(self.slots.len()), None)
399 };
400
401 if let InsertionPlan::PromoteThenEvict { promoted, .. } = plan {
402 self.small.unlink(&mut self.topology, promoted);
403 self.slots[promoted].state.reset();
404 self.main.push(&mut self.topology, promoted);
405 }
406
407 let state = SlotState::new(admission);
408 let (displaced_key, displaced_value) = if slot == self.slots.len() {
409 self.slots.push(Entry {
410 key,
411 value: value.unwrap_or_else(make),
412 state,
413 live: true,
414 });
415 (None, None)
416 } else {
417 let entry = &mut self.slots[slot];
418 let displaced_key = core::mem::replace(&mut entry.key, key);
419 let displaced_value = value.map(|value| core::mem::replace(&mut entry.value, value));
420 entry.state = state;
421 entry.live = true;
422 (Some(displaced_key), displaced_value)
423 };
424
425 match admission {
426 Admission::Small => self.small.push(&self.slots, &mut self.topology, slot),
427 Admission::Main => self.main.push(&mut self.topology, slot),
428 }
429 self.index_slot(slot, hash);
430
431 if let Some(victim_hash) = historical_hash {
434 self.ghost.push(
435 displaced_key.expect("victim must own a key"),
436 victim_hash,
437 &self.hasher,
438 );
439 } else {
440 drop(displaced_key);
441 }
442 drop(displaced_value);
443 slot
444 }
445
446 #[inline]
448 const fn admission(&self, ghost_hit: bool, has_vacancy: bool) -> Admission {
449 if ghost_hit
452 || self.small.capacity == 0
453 || (has_vacancy && self.small.len >= self.small.capacity)
454 {
455 Admission::Main
456 } else {
457 Admission::Small
458 }
459 }
460
461 #[inline]
463 fn plan(&mut self, admission: Admission, has_vacancy: bool) -> InsertionPlan {
464 match admission {
465 Admission::Small if has_vacancy => InsertionPlan::Vacant,
466 Admission::Small => {
467 let tail = self.small.tail.expect("full cache must have a Small tail");
468 if !self.slots[tail].state.earned_promotion() {
469 return InsertionPlan::Evict { victim: tail };
470 }
471
472 assert_eq!(self.main.len, self.main.capacity);
476 let victim = self.main.select(&self.slots, &self.topology);
477 InsertionPlan::PromoteThenEvict {
478 promoted: tail,
479 victim,
480 }
481 }
482 Admission::Main if self.main.len == self.main.capacity => InsertionPlan::Evict {
483 victim: self.main.select(&self.slots, &self.topology),
484 },
485 Admission::Main => {
486 assert!(has_vacancy);
488 InsertionPlan::Vacant
489 }
490 }
491 }
492
493 #[inline]
495 fn unlink_resident(&mut self, slot: Slot) -> Location {
496 let location = self.topology[slot].location();
497 match location {
498 Location::Small => self.small.unlink(&mut self.topology, slot),
499 Location::Main => self.main.unlink(&mut self.topology, slot),
500 Location::Free => unreachable!("resident slot cannot be free"),
501 }
502 location
503 }
504}
505
506impl<K: Hash + Eq + Default, V> Cache<K, V> {
507 pub fn prefill<F: FnMut() -> V>(&mut self, mut make: F) {
517 let start = self.free.len();
518 while self.slots.len() < self.capacity {
519 let slot = self.slots.len();
520 self.slots.push(Entry {
521 key: K::default(),
522 value: make(),
523 state: SlotState::default(),
524 live: false,
525 });
526 self.free.push(slot);
527 }
528
529 self.free[start..].reverse();
532 }
533}
534
535impl<K, V> core::fmt::Debug for Cache<K, V> {
536 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
537 f.debug_struct("Cache")
538 .field("len", &self.index.len())
539 .field("capacity", &self.capacity)
540 .field("small", &self.small.len)
541 .field("small_capacity", &self.small.capacity)
542 .field("main", &self.main.len)
543 .field("main_capacity", &self.main.capacity)
544 .field("ghost", &self.ghost.index.len())
545 .field("ghost_capacity", &self.ghost.capacity)
546 .finish()
547 }
548}
549
550const UNLINKED: usize = usize::MAX;
552const ADMISSION_GENERATION_MASK: usize = usize::MAX >> 2;
554const LOCATION_SHIFT: u32 = usize::BITS - 2;
556const REFERENCED: u8 = 1 << 0;
558const CORRELATED: u8 = 1 << 1;
560
561#[inline]
563const fn linked(slot: usize) -> Option<usize> {
564 if slot == UNLINKED { None } else { Some(slot) }
565}
566
567#[derive(Clone, Copy, Debug, Eq, PartialEq)]
569enum Location {
570 Free,
572 Small,
574 Main,
576}
577
578impl Location {
579 const fn pack(self, admission_generation: usize) -> usize {
580 let tag = match self {
581 Self::Free => 0,
582 Self::Small => 1,
583 Self::Main => 2,
584 };
585 (tag << LOCATION_SHIFT) | (admission_generation & ADMISSION_GENERATION_MASK)
586 }
587}
588
589#[derive(Clone, Copy)]
591struct ResidentSlot {
592 prev: usize,
594 next: usize,
596 location_and_admission: usize,
598}
599
600impl ResidentSlot {
601 #[inline]
602 const fn has_location(&self, location: Location) -> bool {
603 self.location_and_admission & !ADMISSION_GENERATION_MASK == location.pack(0)
604 }
605
606 #[inline]
607 fn location(&self) -> Location {
608 match self.location_and_admission >> LOCATION_SHIFT {
609 0 => Location::Free,
610 1 => Location::Small,
611 2 => Location::Main,
612 _ => unreachable!("invalid resident location tag"),
613 }
614 }
615
616 const fn admitted_at(&self) -> usize {
617 self.location_and_admission & ADMISSION_GENERATION_MASK
618 }
619}
620
621impl Default for ResidentSlot {
622 fn default() -> Self {
623 Self {
624 prev: UNLINKED,
625 next: UNLINKED,
626 location_and_admission: Location::Free.pack(0),
627 }
628 }
629}
630
631#[derive(Clone, Copy, Debug, Eq, PartialEq)]
633enum Admission {
634 Small,
636 Main,
638}
639
640#[derive(Clone, Copy)]
642enum InsertionPlan {
643 Vacant,
645 Evict {
647 victim: Slot,
649 },
650 PromoteThenEvict {
652 promoted: Slot,
654 victim: Slot,
656 },
657}
658
659impl InsertionPlan {
660 const fn victim(self) -> Option<Slot> {
662 match self {
663 Self::Vacant => None,
664 Self::Evict { victim } | Self::PromoteThenEvict { victim, .. } => Some(victim),
665 }
666 }
667}
668
669#[repr(transparent)]
676#[derive(Default)]
677struct SlotState(AtomicU8);
678
679impl SlotState {
680 #[inline]
682 const fn new(admission: Admission) -> Self {
683 let state = match admission {
684 Admission::Small => CORRELATED,
685 Admission::Main => 0,
686 };
687 Self(AtomicU8::new(state))
688 }
689
690 #[inline]
695 fn record_hit(&self) {
696 let current = self.0.load(Ordering::Relaxed);
697 if current & (CORRELATED | REFERENCED) == 0 {
698 self.0.store(REFERENCED, Ordering::Relaxed);
699 }
700 }
701
702 #[inline]
704 fn record_hit_mut(&mut self) {
705 let current = self.0.get_mut();
706 if *current & (CORRELATED | REFERENCED) == 0 {
707 *current |= REFERENCED;
708 }
709 }
710
711 #[inline]
713 fn earned_promotion(&self) -> bool {
714 self.0.load(Ordering::Relaxed) & (CORRELATED | REFERENCED) == REFERENCED
715 }
716
717 #[inline]
719 fn take_reference(&self) -> bool {
720 if self.0.load(Ordering::Relaxed) & REFERENCED == 0 {
721 return false;
722 }
723 self.0.store(0, Ordering::Relaxed);
724 true
725 }
726
727 #[inline]
729 fn reset(&self) {
730 self.0.store(0, Ordering::Relaxed);
731 }
732}
733
734struct SmallQueue {
736 capacity: usize,
738 correlation_window: usize,
740 head: Option<Slot>,
742 tail: Option<Slot>,
744 young_tail: Option<Slot>,
746 admissions: usize,
748 len: usize,
750}
751
752impl SmallQueue {
753 const fn new(capacity: usize) -> Self {
755 Self {
756 capacity,
757 correlation_window: capacity.div_ceil(2),
758 head: None,
759 tail: None,
760 young_tail: None,
761 admissions: 0,
762 len: 0,
763 }
764 }
765
766 #[inline]
768 fn push<K, V>(&mut self, entries: &[Entry<K, V>], slots: &mut [ResidentSlot], slot: Slot) {
769 self.admissions = self.admissions.wrapping_add(1) & ADMISSION_GENERATION_MASK;
773 let old_head = self.head;
774 {
775 let entry = &mut slots[slot];
776 assert!(entry.has_location(Location::Free));
777 entry.prev = UNLINKED;
778 entry.next = old_head.unwrap_or(UNLINKED);
779 entry.location_and_admission = Location::Small.pack(self.admissions);
780 }
781 if let Some(head) = old_head {
782 slots[head].prev = slot;
783 } else {
784 self.tail = Some(slot);
785 }
786 self.head = Some(slot);
787 self.len += 1;
788
789 let demoted = *self.young_tail.get_or_insert(slot);
792 let age =
793 self.admissions.wrapping_sub(slots[demoted].admitted_at()) & ADMISSION_GENERATION_MASK;
794 if age < self.correlation_window {
795 return;
796 }
797
798 let new_boundary = slots[demoted].prev;
799 assert_ne!(new_boundary, UNLINKED);
800
801 entries[demoted].state.reset();
804 slots[demoted].location_and_admission = Location::Small.pack(0);
805 self.young_tail = Some(new_boundary);
806 }
807
808 #[inline]
810 fn unlink(&mut self, slots: &mut [ResidentSlot], slot: Slot) {
811 assert!(slots[slot].has_location(Location::Small));
812 let prev = slots[slot].prev;
813 let next = slots[slot].next;
814 if prev != UNLINKED {
815 slots[prev].next = next;
816 } else {
817 self.head = linked(next);
818 }
819 if next != UNLINKED {
820 slots[next].prev = prev;
821 } else {
822 self.tail = linked(prev);
823 }
824
825 self.len -= 1;
826
827 if self.young_tail == Some(slot) {
830 self.young_tail = linked(prev);
831 }
832 slots[slot] = ResidentSlot::default();
833 }
834
835 const fn clear(&mut self) {
837 self.head = None;
838 self.tail = None;
839 self.young_tail = None;
840 self.admissions = 0;
841 self.len = 0;
842 }
843}
844
845struct MainRing {
850 capacity: usize,
852 hand: Option<Slot>,
854 len: usize,
856}
857
858impl MainRing {
859 const fn new(capacity: usize) -> Self {
861 Self {
862 capacity,
863 hand: None,
864 len: 0,
865 }
866 }
867
868 #[inline]
870 fn select<K, V>(&mut self, entries: &[Entry<K, V>], slots: &[ResidentSlot]) -> Slot {
871 loop {
872 let slot = self.hand.expect("nonempty Main must have a hand");
873 if entries[slot].state.take_reference() {
874 self.hand = Some(slots[slot].next);
877 continue;
878 }
879 return slot;
880 }
881 }
882
883 #[inline]
885 fn push(&mut self, slots: &mut [ResidentSlot], slot: Slot) {
886 assert!(self.len < self.capacity);
887 let Some(hand) = self.hand else {
888 let entry = &mut slots[slot];
891 assert!(entry.has_location(Location::Free));
892 entry.prev = slot;
893 entry.next = slot;
894 entry.location_and_admission = Location::Main.pack(0);
895 self.hand = Some(slot);
896 self.len = 1;
897 return;
898 };
899
900 let prev = slots[hand].prev;
903 assert_ne!(prev, UNLINKED);
904 {
905 let entry = &mut slots[slot];
906 assert!(entry.has_location(Location::Free));
907 entry.prev = prev;
908 entry.next = hand;
909 entry.location_and_admission = Location::Main.pack(0);
910 }
911 slots[prev].next = slot;
912 slots[hand].prev = slot;
913 self.len += 1;
914 }
915
916 #[inline]
918 fn unlink(&mut self, slots: &mut [ResidentSlot], slot: Slot) {
919 assert!(slots[slot].has_location(Location::Main));
920 if self.len == 1 {
921 assert_eq!(self.hand, Some(slot));
922 self.hand = None;
923 self.len = 0;
924 } else {
925 let prev = slots[slot].prev;
926 let next = slots[slot].next;
927 assert_ne!(prev, UNLINKED);
928 assert_ne!(next, UNLINKED);
929 slots[prev].next = next;
930 slots[next].prev = prev;
931 if self.hand == Some(slot) {
932 self.hand = Some(next);
933 }
934 self.len -= 1;
935 }
936 slots[slot] = ResidentSlot::default();
937 }
938
939 const fn clear(&mut self) {
941 self.hand = None;
942 self.len = 0;
943 }
944}
945
946struct GhostSlot<K> {
948 key: Option<K>,
950 prev: usize,
952 next: usize,
954}
955
956struct GhostQueue<K> {
964 index: HashTable<usize>,
970 slots: Vec<GhostSlot<K>>,
972 free_head: Option<usize>,
974 head: Option<usize>,
976 tail: Option<usize>,
978 capacity: usize,
980}
981
982impl<K: Hash + Eq> GhostQueue<K> {
983 fn new(capacity: usize) -> Self {
985 Self {
986 index: HashTable::with_capacity(capacity.checked_mul(2).expect("capacity overflow")),
987 slots: Vec::with_capacity(capacity),
988 free_head: None,
989 head: None,
990 tail: None,
991 capacity,
992 }
993 }
994
995 #[inline]
997 fn push(&mut self, key: K, hash: u64, hasher: &Hasher) {
998 if self.capacity == 0 {
999 return;
1000 }
1001
1002 let mut displaced = None;
1005 let slot = if let Some(slot) = self.free_head {
1006 self.free_head = linked(self.slots[slot].next);
1007 slot
1008 } else if self.slots.len() < self.capacity {
1009 let slot = self.slots.len();
1010 self.slots.push(GhostSlot {
1011 key: None,
1012 prev: UNLINKED,
1013 next: UNLINKED,
1014 });
1015 slot
1016 } else {
1017 let slot = self.tail.expect("full Ghost must have a tail");
1018
1019 let historical_hash = hasher.hash_one(
1022 self.slots[slot]
1023 .key
1024 .as_ref()
1025 .expect("linked Ghost entry must have a key"),
1026 );
1027 self.index
1028 .find_entry(historical_hash, |candidate| *candidate == slot)
1029 .expect("linked Ghost entry must be indexed")
1030 .remove();
1031 displaced = Some(self.unlink(slot));
1032 slot
1033 };
1034
1035 let old_head = self.head;
1036 {
1037 let entry = &mut self.slots[slot];
1038 assert!(entry.key.is_none());
1039 entry.key = Some(key);
1040 entry.prev = UNLINKED;
1041 entry.next = old_head.unwrap_or(UNLINKED);
1042 }
1043 if let Some(head) = old_head {
1044 self.slots[head].prev = slot;
1045 } else {
1046 self.tail = Some(slot);
1047 }
1048 self.head = Some(slot);
1049
1050 let slots = &self.slots;
1053 let _ = self.index.insert_unique(hash, slot, |slot| {
1054 hasher.hash_one(
1055 slots[*slot]
1056 .key
1057 .as_ref()
1058 .expect("indexed Ghost entry must have a key"),
1059 )
1060 });
1061 drop(displaced);
1062 }
1063
1064 #[inline]
1066 fn discard(&mut self, key: &K, hash: u64) -> bool {
1067 let slot = {
1068 let slots = &self.slots;
1069 let Ok(entry) = self.index.find_entry(hash, |slot| {
1070 slots[*slot]
1071 .key
1072 .as_ref()
1073 .expect("indexed Ghost entry must have a key")
1074 == key
1075 }) else {
1076 return false;
1077 };
1078 entry.remove().0
1079 };
1080 let historical = self.unlink(slot);
1083 self.slots[slot].next = self.free_head.unwrap_or(UNLINKED);
1084 self.free_head = Some(slot);
1085 drop(historical);
1086 true
1087 }
1088
1089 #[inline]
1091 fn unlink(&mut self, slot: usize) -> K {
1092 let prev = self.slots[slot].prev;
1093 let next = self.slots[slot].next;
1094 if prev != UNLINKED {
1095 self.slots[prev].next = next;
1096 } else {
1097 self.head = linked(next);
1098 }
1099 if next != UNLINKED {
1100 self.slots[next].prev = prev;
1101 } else {
1102 self.tail = linked(prev);
1103 }
1104
1105 let key = self.slots[slot]
1106 .key
1107 .take()
1108 .expect("linked Ghost entry must have a key");
1109 self.slots[slot].prev = UNLINKED;
1110 self.slots[slot].next = UNLINKED;
1111 key
1112 }
1113
1114 fn clear(&mut self) {
1116 self.index.clear();
1117 self.free_head = None;
1118 self.head = None;
1119 self.tail = None;
1120 self.slots.clear();
1121 }
1122}
1123
1124#[cfg(test)]
1125mod tests {
1126 use super::*;
1127 use crate::NZUsize;
1128 use core::cell::{Cell, RefCell};
1129 use proptest::{prelude::*, test_runner::TestCaseResult};
1130 use std::{
1131 collections::{HashMap, HashSet, VecDeque},
1132 rc::Rc,
1133 sync::Barrier,
1134 thread,
1135 };
1136
1137 #[test]
1138 fn test_basic_put_get_peek() {
1139 let mut cache = Cache::new(NZUsize!(2));
1140 assert!(cache.is_empty());
1141 assert_eq!(cache.capacity(), 2);
1142
1143 assert_eq!(cache.put(1u64, 10u64), None);
1144 assert_eq!(cache.put(2, 20), None);
1145 assert_eq!(cache.len(), 2);
1146 assert_eq!(cache.get(&1).copied(), Some(10));
1147 assert_eq!(cache.peek(&2).copied(), Some(20));
1148 assert!(cache.contains(&1));
1149 assert!(!cache.contains(&3));
1150 assert_eq!(cache.get(&3), None);
1151 cache.check_invariants();
1152 }
1153
1154 #[test]
1155 fn test_put_replaces_existing() {
1156 let mut cache = Cache::new(NZUsize!(2));
1157 assert_eq!(cache.put(1u64, 10u64), None);
1158 assert_eq!(cache.put(1, 11), Some(10));
1159 assert_eq!(cache.get(&1).copied(), Some(11));
1160 assert_eq!(cache.len(), 1);
1161 cache.check_invariants();
1162 }
1163
1164 #[test]
1165 fn test_capacity_one_reuses_its_only_slot() {
1166 let mut cache = Cache::new(NZUsize!(1));
1167 cache.put(1u64, 10u64);
1168 cache.put(2, 20);
1169 assert!(!cache.contains(&1));
1170 assert_eq!(cache.get(&2).copied(), Some(20));
1171 assert_eq!(cache.len(), 1);
1172 cache.check_invariants();
1173 }
1174
1175 #[test]
1176 fn test_get_or_insert_with_calls_factory_only_on_miss() {
1177 let mut cache = Cache::new(NZUsize!(2));
1178 let calls = Cell::new(0);
1179 let compute = |key: u64| {
1180 calls.set(calls.get() + 1);
1181 key * 100
1182 };
1183
1184 assert_eq!(*cache.get_or_insert_with(1, || compute(1)), 100);
1185 assert_eq!(calls.get(), 1);
1186 assert_eq!(*cache.get_or_insert_with(1, || compute(1)), 100);
1188 assert_eq!(calls.get(), 1);
1189 cache.check_invariants();
1190 }
1191
1192 #[test]
1193 fn test_try_get_or_insert_with_does_not_cache_errors() {
1194 let mut cache = Cache::new(NZUsize!(2));
1195
1196 let error: Result<&u64, &str> = cache.try_get_or_insert_with(1u64, || Err("bad"));
1197 assert_eq!(error, Err("bad"));
1198 assert!(!cache.contains(&1));
1199
1200 let value: Result<&u64, &str> = cache.try_get_or_insert_with(1, || Ok(10));
1201 assert_eq!(value, Ok(&10));
1202 assert!(cache.contains(&1));
1203 cache.check_invariants();
1204 }
1205
1206 #[test]
1207 fn test_remove_keeps_slot_for_reuse() {
1208 let makes = Cell::new(0);
1211 let mut cache = Cache::new(NZUsize!(2));
1212 cache.get_or_insert_mut(1, || {
1213 makes.set(makes.get() + 1);
1214 10
1215 });
1216 cache.get_or_insert_mut(2, || {
1217 makes.set(makes.get() + 1);
1218 20
1219 });
1220 assert_eq!(makes.get(), 2);
1221 assert_eq!(cache.slots.len(), 2);
1222
1223 assert!(cache.remove(&1));
1224 assert!(!cache.contains(&1));
1225 assert_eq!(cache.len(), 1);
1226
1227 *cache
1229 .get_or_insert_mut(3, || {
1230 makes.set(makes.get() + 1);
1231 30
1232 })
1233 .1 = 30;
1234
1235 assert_eq!(makes.get(), 2, "freed slot should not call the factory");
1236 assert_eq!(cache.slots.len(), 2);
1237 assert_eq!(cache.get(&3).copied(), Some(30));
1238 assert!(!cache.remove(&999));
1239 cache.check_invariants();
1240 }
1241
1242 #[test]
1243 fn test_retain_frees_slots_for_reuse() {
1244 let mut cache = Cache::new(NZUsize!(4));
1245 for key in 0..4u64 {
1246 cache.put(key, key * 10);
1247 }
1248 cache.retain(|key, _| key % 2 == 0);
1250 assert_eq!(cache.len(), 2);
1251 assert!(cache.contains(&0));
1252 assert!(cache.contains(&2));
1253 assert!(!cache.contains(&1));
1254 assert!(!cache.contains(&3));
1255
1256 cache.put(10, 100);
1258 cache.put(12, 120);
1259 assert_eq!(cache.slots.len(), 4);
1260 assert_eq!(cache.len(), 4);
1261 cache.check_invariants();
1262 }
1263
1264 #[test]
1265 fn test_get_or_insert_mut_reuses_allocations() {
1266 let makes = Cell::new(0);
1269 let mut cache = Cache::new(NZUsize!(3));
1270 for key in 0..100u64 {
1271 let (_, value) = cache.get_or_insert_mut(key, || {
1272 makes.set(makes.get() + 1);
1273 0
1274 });
1275 *value = key; }
1277 assert_eq!(makes.get(), 3, "factory should run only during growth");
1278 assert_eq!(cache.slots.len(), 3);
1279 assert_eq!(cache.len(), 3);
1280 cache.check_invariants();
1281 }
1282
1283 #[test]
1284 fn test_prefill_allocates_once_and_reuses() {
1285 let makes = Cell::new(0);
1288 let mut cache = Cache::new(NZUsize!(3));
1289 cache.prefill(|| {
1290 makes.set(makes.get() + 1);
1291 0
1292 });
1293 assert_eq!(makes.get(), 3);
1294 assert_eq!(cache.slots.len(), 3);
1295 assert!(cache.is_empty());
1296 cache.check_invariants();
1297
1298 for key in 0..100u64 {
1300 *cache
1301 .get_or_insert_mut(key, || {
1302 makes.set(makes.get() + 1);
1303 0
1304 })
1305 .1 = key;
1306 }
1307 assert_eq!(makes.get(), 3, "prefilled slots must be reused");
1308 assert_eq!(cache.slots.len(), 3);
1309 assert_eq!(cache.len(), 3);
1310 cache.check_invariants();
1311 }
1312
1313 #[test]
1314 fn test_prefill_and_detached_slots_preserve_reuse_order() {
1315 for interrupted in [false, true] {
1316 let mut cache = Cache::<u64, u64>::new(NZUsize!(6));
1317 for key in 0..3 {
1318 assert_eq!(cache.get_or_insert_mut(key, || key + 10).0, key as usize);
1319 }
1320 assert!(cache.remove(&1));
1321 assert!(cache.remove(&2));
1322
1323 let mut next = 3;
1324 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1325 cache.prefill(|| {
1326 assert!(!interrupted || next != 5, "prefill factory panic");
1327 let value = next + 10;
1328 next += 1;
1329 value
1330 });
1331 }));
1332 assert_eq!(result.is_err(), interrupted);
1333 assert_eq!(cache.get_at(0, &0), Some(&10));
1334 cache.check_invariants();
1335
1336 let expected: &[usize] = if interrupted {
1339 &[4, 3, 2, 1]
1340 } else {
1341 &[3, 4, 5, 2, 1]
1342 };
1343 for &expected_slot in expected {
1344 let (slot, value) =
1345 cache.get_or_insert_mut(expected_slot as u64, || unreachable!());
1346 assert_eq!(slot, expected_slot);
1347 assert_eq!(*value, expected_slot as u64 + 10);
1348 cache.check_invariants();
1349 }
1350 if interrupted {
1351 assert_eq!(cache.get_or_insert_mut(5, || 15).0, 5);
1352 }
1353
1354 assert!(cache.remove(&1));
1355 assert!(cache.remove(&2));
1356 let mut detached = vec![1, 2];
1357 cache.retain(|key, _| {
1358 if *key == 0 || *key == 3 {
1359 detached.push(*key as usize);
1360 false
1361 } else {
1362 true
1363 }
1364 });
1365 cache.check_invariants();
1366 for expected_slot in detached.into_iter().rev() {
1367 let (slot, value) =
1368 cache.get_or_insert_mut(expected_slot as u64 + 20, || unreachable!());
1369 assert_eq!(slot, expected_slot);
1370 assert_eq!(*value, expected_slot as u64 + 10);
1371 cache.check_invariants();
1372 }
1373 }
1374 }
1375
1376 #[test]
1377 fn test_clear_drops_entries_and_allows_reuse() {
1378 let mut cache = Cache::new(NZUsize!(4));
1379 for key in 0..4u64 {
1380 cache.put(key, key);
1381 }
1382 cache.clear();
1383 assert!(cache.is_empty());
1384 assert_eq!(cache.len(), 0);
1385 cache.put(9, 9);
1386 assert_eq!(cache.get(&9).copied(), Some(9));
1387 cache.check_invariants();
1388 }
1389
1390 #[derive(Clone)]
1391 struct Tracked(Rc<Cell<usize>>);
1392
1393 impl Drop for Tracked {
1394 fn drop(&mut self) {
1395 self.0.set(self.0.get() + 1);
1396 }
1397 }
1398
1399 #[test]
1400 fn test_values_are_dropped_on_replacement_and_clear() {
1401 let drops = Rc::new(Cell::new(0));
1402 let mut cache = Cache::new(NZUsize!(2));
1403 for key in 0..2u64 {
1404 cache.put(key, Tracked(drops.clone()));
1405 }
1406 assert_eq!(drops.get(), 0);
1407
1408 cache.put(2, Tracked(drops.clone()));
1410 assert_eq!(drops.get(), 1);
1411
1412 cache.put(2, Tracked(drops.clone()));
1414 assert_eq!(drops.get(), 2);
1415
1416 cache.clear();
1418 assert_eq!(drops.get(), 4);
1419 }
1420
1421 #[test]
1422 fn test_remove_retains_value_until_reuse() {
1423 let drops = Rc::new(Cell::new(0));
1426 let mut cache = Cache::new(NZUsize!(2));
1427 cache.put(1, Tracked(drops.clone()));
1428 assert!(cache.remove(&1));
1429 assert_eq!(drops.get(), 0, "remove must not drop the value");
1430
1431 cache.put(2, Tracked(drops.clone()));
1433 assert_eq!(drops.get(), 1);
1434 }
1435
1436 #[test]
1437 fn test_get_at_validates_key() {
1438 let mut cache = Cache::new(NZUsize!(2));
1439 let (first, value) = cache.get_or_insert_mut(1u64, || 0u64);
1440 *value = 10;
1441 let (second, value) = cache.get_or_insert_mut(2u64, || 0u64);
1442 *value = 20;
1443
1444 assert_eq!(cache.get_at(first, &1).copied(), Some(10));
1446 assert_eq!(cache.get_at(second, &2).copied(), Some(20));
1447
1448 assert_eq!(cache.get_at(first, &2), None);
1450 assert_eq!(cache.get_at(cache.capacity(), &1), None);
1451 cache.check_invariants();
1452 }
1453
1454 #[test]
1455 fn test_get_at_rejects_stale_evicted_slot() {
1456 let mut cache = Cache::new(NZUsize!(1));
1459 let (slot, value) = cache.get_or_insert_mut(1u64, || 0u64);
1460 *value = 10;
1461 let (reused, value) = cache.get_or_insert_mut(3u64, || 0u64);
1462 *value = 30;
1463
1464 assert_eq!(slot, reused);
1465 assert_eq!(cache.get_at(slot, &1), None);
1466 assert_eq!(cache.get_at(slot, &3).copied(), Some(30));
1467 cache.check_invariants();
1468 }
1469
1470 #[test]
1471 fn test_get_at_rejects_freed_slot() {
1472 let mut cache = Cache::new(NZUsize!(2));
1475 let (slot, value) = cache.get_or_insert_mut(1u64, || 0u64);
1476 *value = 10;
1477 assert!(cache.remove(&1));
1478 assert_eq!(cache.get_at(slot, &1), None);
1479
1480 let (reused, value) = cache.get_or_insert_mut(2u64, || 0u64);
1482 *value = 20;
1483 assert_eq!(reused, slot);
1484 assert_eq!(cache.get_at(slot, &1), None);
1485 assert_eq!(cache.get_at(slot, &2).copied(), Some(20));
1486 cache.check_invariants();
1487 }
1488
1489 #[test]
1490 fn test_get_or_insert_mut_slot_is_stable_on_hit() {
1491 let mut cache = Cache::new(NZUsize!(2));
1492 let (slot, value) = cache.get_or_insert_mut(1u64, || 0u64);
1493 *value = 10;
1494 let (hit_slot, value) = cache.get_or_insert_mut(1u64, || unreachable!());
1495 assert_eq!(slot, hit_slot);
1496 assert_eq!(*value, 10);
1497 cache.check_invariants();
1498 }
1499
1500 #[test]
1501 fn test_get_mut_updates_resident_value() {
1502 let mut cache = Cache::new(NZUsize!(2));
1503 cache.put(1u64, 10u64);
1504 assert_eq!(cache.get_mut(&2), None);
1505 *cache.get_mut(&1).unwrap() = 11;
1506 assert_eq!(cache.get(&1).copied(), Some(11));
1507 cache.check_invariants();
1508 }
1509
1510 #[derive(Clone, Debug)]
1511 enum Op {
1512 Get(u8),
1513 Peek(u8),
1514 Put(u8, u16),
1515 GetOrInsert(u8, u16),
1516 GetOrInsertMut(u8, u16),
1517 GetMut(u8, u16),
1518 Remove(u8),
1519 Retain(u8),
1520 Clear,
1521 }
1522
1523 fn op_strategy(keys: u8) -> impl Strategy<Value = Op> {
1524 prop_oneof![
1525 8 => (0..keys).prop_map(Op::Get),
1526 8 => (0..keys).prop_map(Op::Peek),
1527 8 => (0..keys, any::<u16>()).prop_map(|(k, v)| Op::Put(k, v)),
1528 8 => (0..keys, any::<u16>()).prop_map(|(k, v)| Op::GetOrInsert(k, v)),
1529 8 => (0..keys, any::<u16>()).prop_map(|(k, v)| Op::GetOrInsertMut(k, v)),
1530 8 => (0..keys, any::<u16>()).prop_map(|(k, v)| Op::GetMut(k, v)),
1531 8 => (0..keys).prop_map(Op::Remove),
1532 8 => (0..keys).prop_map(Op::Retain),
1533 1 => Just(Op::Clear),
1534 ]
1535 }
1536
1537 fn exercise_cache(capacity: usize, keys: u8, prefill: bool, ops: Vec<Op>) -> TestCaseResult {
1538 let mut cache = Cache::<u8, u16>::new(NonZeroUsize::new(capacity).unwrap());
1539 if prefill {
1540 cache.prefill(|| 0u16);
1541 }
1542 let mut model = HashMap::new();
1546 for op in ops {
1547 match op {
1548 Op::Get(key) => {
1549 let value = cache.get(&key).copied();
1550 prop_assert_eq!(value, cache.peek(&key).copied());
1551 }
1552 Op::Peek(key) => {
1553 let _ = cache.peek(&key);
1554 }
1555 Op::Put(key, value) => {
1556 cache.put(key, value);
1557 model.insert(key, value);
1558 prop_assert_eq!(cache.peek(&key).copied(), Some(value));
1559 }
1560 Op::GetOrInsert(key, value) => {
1561 let stored = *cache.get_or_insert_with(key, || value);
1562 model.insert(key, stored);
1563 prop_assert_eq!(cache.peek(&key).copied(), Some(stored));
1564 }
1565 Op::GetOrInsertMut(key, value) => {
1566 *cache.get_or_insert_mut(key, || value).1 = value;
1567 model.insert(key, value);
1568 prop_assert_eq!(cache.peek(&key).copied(), Some(value));
1569 }
1570 Op::GetMut(key, value) => {
1571 if let Some(stored) = cache.get_mut(&key) {
1572 *stored = value;
1573 model.insert(key, value);
1574 }
1575 }
1576 Op::Remove(key) => {
1577 let present = cache.contains(&key);
1578 prop_assert_eq!(cache.remove(&key), present);
1579 model.remove(&key);
1580 prop_assert!(!cache.contains(&key));
1581 }
1582 Op::Retain(key) => {
1583 cache.retain(|resident, _| *resident < key);
1584 model.retain(|resident, _| *resident < key);
1585 prop_assert!(cache.len() <= usize::from(key).min(capacity));
1586 }
1587 Op::Clear => {
1588 cache.clear();
1589 model.clear();
1590 prop_assert!(cache.is_empty());
1591 }
1592 }
1593
1594 prop_assert!(cache.len() <= capacity);
1595 prop_assert!(cache.slots.len() <= capacity);
1597 for key in 0..keys {
1600 let present = cache.contains(&key);
1601 prop_assert_eq!(present, cache.peek(&key).is_some());
1602 if present {
1603 prop_assert_eq!(cache.peek(&key).copied(), model.get(&key).copied());
1604 }
1605 }
1606 cache.check_invariants();
1607 }
1608
1609 Ok(())
1610 }
1611
1612 proptest! {
1613 #[test]
1614 fn cache_invariants_hold(
1615 capacity in 1usize..8,
1616 prefill in any::<bool>(),
1617 ops in proptest::collection::vec(op_strategy(16), 0..256),
1618 ) {
1619 exercise_cache(capacity, 16, prefill, ops)?;
1620 }
1621
1622 #[test]
1625 fn cache_invariants_hold_with_wide_small(
1626 capacity in 20usize..48,
1627 prefill in any::<bool>(),
1628 ops in proptest::collection::vec(op_strategy(64), 0..512),
1629 ) {
1630 exercise_cache(capacity, 64, prefill, ops)?;
1631 }
1632 }
1633 impl<K> GhostQueue<K> {
1634 fn check_free_slots(&self) -> HashSet<usize> {
1635 let mut free = HashSet::new();
1636 let mut current = self.free_head;
1637 while let Some(slot) = current {
1638 assert!(free.insert(slot), "duplicate Ghost free slot {slot}");
1639 let entry = &self.slots[slot];
1640 assert!(entry.key.is_none());
1641 assert_eq!(entry.prev, UNLINKED);
1642 current = linked(entry.next);
1643 }
1644 assert_eq!(self.index.len() + free.len(), self.slots.len());
1645 free
1646 }
1647 }
1648
1649 impl<K: Hash + Eq, V> Cache<K, V> {
1650 fn bits(&self, slot: Slot) -> u8 {
1652 self.slots[slot].state.0.load(Ordering::Relaxed)
1653 }
1654
1655 fn small_order(&self) -> Vec<usize> {
1657 let mut order = Vec::with_capacity(self.small.len);
1658 let mut current = self.small.head;
1659 while let Some(slot) = current {
1660 order.push(slot);
1661 current = linked(self.topology[slot].next);
1662 }
1663 order
1664 }
1665
1666 fn main_order(&self) -> Vec<usize> {
1668 let mut order = Vec::with_capacity(self.main.len);
1669 let Some(hand) = self.main.hand else {
1670 return order;
1671 };
1672 let mut current = hand;
1673 loop {
1674 order.push(current);
1675 current = self.topology[current].next;
1676 assert_ne!(current, UNLINKED);
1677 if current == hand {
1678 return order;
1679 }
1680 }
1681 }
1682
1683 fn ghost_order(&self) -> Vec<usize> {
1685 let mut order = Vec::with_capacity(self.ghost.index.len());
1686 let mut current = self.ghost.head;
1687 while let Some(slot) = current {
1688 order.push(slot);
1689 current = linked(self.ghost.slots[slot].next);
1690 }
1691 order
1692 }
1693
1694 fn small_keys(&self) -> Vec<K>
1696 where
1697 K: Clone,
1698 {
1699 self.small_order()
1700 .into_iter()
1701 .map(|slot| self.slots[slot].key.clone())
1702 .collect()
1703 }
1704
1705 fn main_keys(&self) -> Vec<K>
1707 where
1708 K: Clone,
1709 {
1710 self.main_order()
1711 .into_iter()
1712 .map(|slot| self.slots[slot].key.clone())
1713 .collect()
1714 }
1715
1716 fn ghost_keys(&self) -> Vec<K>
1718 where
1719 K: Clone,
1720 {
1721 self.ghost_order()
1722 .into_iter()
1723 .map(|slot| {
1724 self.ghost.slots[slot]
1725 .key
1726 .as_ref()
1727 .expect("live Ghost entry must have a key")
1728 .clone()
1729 })
1730 .collect()
1731 }
1732
1733 fn check_invariants(&self) {
1735 assert!(self.slots.len() <= self.capacity);
1736 assert_eq!(self.topology.len(), self.capacity);
1737 assert!(self.small.len <= self.small.capacity);
1738 assert!(self.main.len <= self.main.capacity);
1739 assert_eq!(self.index.len(), (self.small.len + self.main.len));
1740 assert_eq!(self.index.len() + self.free.len(), self.slots.len());
1741
1742 let free: HashSet<_> = self.free.iter().copied().collect();
1743 assert_eq!(free.len(), self.free.len(), "duplicate resident free slot");
1744 let small = self.small_order();
1745 let main = self.main_order();
1746 assert_eq!(small.len(), self.small.len);
1747 assert_eq!(main.len(), self.main.len);
1748 let young_len = self
1749 .small
1750 .young_tail
1751 .map(|tail| {
1752 small
1753 .iter()
1754 .position(|slot| *slot == tail)
1755 .expect("correlation boundary must belong to Small")
1756 + 1
1757 })
1758 .unwrap_or(0);
1759 assert!(self.small.admissions <= ADMISSION_GENERATION_MASK);
1760 assert!(young_len <= self.small.correlation_window);
1761 assert!(young_len <= self.small.len);
1762
1763 let mut resident = HashSet::new();
1764 for (rank, &slot) in small.iter().enumerate() {
1765 assert!(resident.insert(slot), "duplicate Small resident {slot}");
1766 assert!(self.slots[slot].live);
1767 assert_eq!(self.topology[slot].location(), Location::Small);
1768 let state = self.bits(slot);
1769 let young = rank < young_len;
1770 assert_eq!(state & CORRELATED != 0, young);
1771 if young {
1772 assert_eq!(state, CORRELATED);
1773 let age = self
1774 .small
1775 .admissions
1776 .wrapping_sub(self.topology[slot].admitted_at())
1777 & ADMISSION_GENERATION_MASK;
1778 assert!(age < self.small.correlation_window);
1779 } else {
1780 assert_eq!(self.topology[slot].admitted_at(), 0);
1781 }
1782 let expected_prev = rank.checked_sub(1).map(|rank| small[rank]);
1783 let expected_next = small.get(rank + 1).copied();
1784 assert_eq!(linked(self.topology[slot].prev), expected_prev);
1785 assert_eq!(linked(self.topology[slot].next), expected_next);
1786 }
1787 assert_eq!(self.small.head, small.first().copied());
1788 assert_eq!(self.small.tail, small.last().copied());
1789 let expected_young_tail = young_len
1790 .checked_sub(1)
1791 .and_then(|rank| small.get(rank))
1792 .copied();
1793 assert_eq!(self.small.young_tail, expected_young_tail);
1794
1795 for (rank, &slot) in main.iter().enumerate() {
1796 assert!(resident.insert(slot), "resident {slot} in two queues");
1797 assert!(self.slots[slot].live);
1798 assert_eq!(self.topology[slot].location(), Location::Main);
1799 assert_eq!(self.topology[slot].admitted_at(), 0);
1800 assert_eq!(self.bits(slot) & CORRELATED, 0);
1801 assert_eq!(
1802 self.topology[slot].prev,
1803 main[(rank + main.len() - 1) % main.len()]
1804 );
1805 assert_eq!(self.topology[slot].next, main[(rank + 1) % main.len()]);
1806 }
1807
1808 let indexed: HashSet<_> = self.index.iter().copied().collect();
1809 assert_eq!(indexed.len(), self.index.len(), "duplicate indexed slot");
1810 assert_eq!(indexed, resident);
1811 for &slot in &self.index {
1812 assert!(slot < self.slots.len());
1813 assert!(resident.contains(&slot));
1814 assert_eq!(self.find_slot(&self.slots[slot].key), Some(slot));
1815 }
1816 for slot in 0..self.slots.len() {
1817 if resident.contains(&slot) {
1818 assert!(!free.contains(&slot));
1819 } else {
1820 assert!(free.contains(&slot));
1821 assert!(!self.slots[slot].live);
1822 assert_eq!(self.topology[slot].location(), Location::Free);
1823 assert_eq!(self.topology[slot].admitted_at(), 0);
1824 }
1825 }
1826 for slot in self.slots.len()..self.topology.len() {
1827 assert_eq!(self.topology[slot].location(), Location::Free);
1828 assert_eq!(self.topology[slot].admitted_at(), 0);
1829 }
1830
1831 assert!(self.ghost.index.len() <= self.ghost.capacity);
1832 assert!(self.ghost.slots.len() <= self.ghost.capacity);
1833 let ghost = self.ghost_order();
1834 assert_eq!(ghost.len(), self.ghost.index.len());
1835 let ghost_free = self.ghost.check_free_slots();
1836 let mut seen_ghost = HashSet::new();
1837 for (rank, &slot) in ghost.iter().enumerate() {
1838 assert!(seen_ghost.insert(slot));
1839 assert!(!ghost_free.contains(&slot));
1840 let key = self.ghost.slots[slot]
1841 .key
1842 .as_ref()
1843 .expect("linked Ghost entry must have a key");
1844 assert_eq!(
1845 self.ghost
1846 .index
1847 .find(self.hasher.hash_one(key), |candidate| {
1848 self.ghost.slots[*candidate].key.as_ref() == Some(key)
1849 }),
1850 Some(&slot),
1851 );
1852 assert!(!self.contains(key));
1853 let expected_prev = rank.checked_sub(1).map(|rank| ghost[rank]);
1854 let expected_next = ghost.get(rank + 1).copied();
1855 assert_eq!(linked(self.ghost.slots[slot].prev), expected_prev);
1856 assert_eq!(linked(self.ghost.slots[slot].next), expected_next);
1857 }
1858 assert_eq!(self.ghost.head, ghost.first().copied());
1859 assert_eq!(self.ghost.tail, ghost.last().copied());
1860 for (slot, entry) in self.ghost.slots.iter().enumerate() {
1861 if seen_ghost.contains(&slot) {
1862 assert!(entry.key.is_some());
1863 } else {
1864 assert!(ghost_free.contains(&slot));
1865 assert!(entry.key.is_none());
1866 }
1867 }
1868 }
1869 }
1870
1871 #[test]
1872 fn test_resident_metadata_fits_three_words_and_preserves_links() {
1873 assert_eq!(
1874 core::mem::size_of::<ResidentSlot>(),
1875 3 * core::mem::size_of::<usize>()
1876 );
1877
1878 let mut slot = ResidentSlot {
1879 prev: usize::MAX - 1,
1880 next: usize::MAX - 2,
1881 location_and_admission: Location::Small.pack(ADMISSION_GENERATION_MASK),
1882 };
1883 assert_eq!(slot.location(), Location::Small);
1884 assert_eq!(slot.admitted_at(), ADMISSION_GENERATION_MASK);
1885 assert_eq!(slot.prev, usize::MAX - 1);
1886 assert_eq!(slot.next, usize::MAX - 2);
1887
1888 slot.location_and_admission = Location::Main.pack(0);
1889 assert_eq!(slot.location(), Location::Main);
1890 assert_eq!(slot.admitted_at(), 0);
1891 assert_eq!(slot.prev, usize::MAX - 1);
1892 assert_eq!(slot.next, usize::MAX - 2);
1893
1894 slot.location_and_admission = Location::Free.pack(0);
1895 assert_eq!(slot.location(), Location::Free);
1896 assert_eq!(slot.admitted_at(), 0);
1897 }
1898
1899 #[test]
1900 fn test_partitions_round_for_tiny_capacities() {
1901 let expected = [
1905 (1, 0, 1, 0, 0),
1906 (2, 1, 1, 1, 1),
1907 (10, 1, 9, 1, 5),
1908 (11, 1, 10, 1, 5),
1909 (20, 2, 18, 1, 10),
1910 (40, 4, 36, 2, 20),
1911 ];
1912 for (capacity, small, main, window, ghost) in expected {
1913 let cache = Cache::<u64, u64>::new(NonZeroUsize::new(capacity).unwrap());
1914 assert_eq!(cache.small.capacity, small);
1915 assert_eq!(cache.main.capacity, main);
1916 assert_eq!(cache.small.correlation_window, window);
1917 assert_eq!(cache.ghost.capacity, ghost);
1918 cache.check_invariants();
1919 }
1920
1921 for capacity in 1..=20 {
1924 let mut cache = Cache::new(NonZeroUsize::new(capacity).unwrap());
1925 for key in 0..200u64 {
1926 cache.put(key, key);
1927 cache.check_invariants();
1928 }
1929 }
1930 }
1931
1932 #[test]
1933 fn test_capacity_one_behaves_as_clock() {
1934 let mut cache = Cache::new(NZUsize!(1));
1937 cache.put(1u64, 10u64);
1938 assert_eq!(cache.get(&1), Some(&10));
1939 cache.put(2, 20);
1940 assert_eq!(cache.get(&1), None);
1941 assert_eq!(cache.get(&2), Some(&20));
1942 assert!(cache.ghost_keys().is_empty());
1943 cache.check_invariants();
1944 }
1945
1946 #[test]
1947 fn test_warmup_fills_small_then_main_with_cold_entries() {
1948 let mut cache = Cache::new(NZUsize!(20));
1951 for key in 0..20u64 {
1952 cache.put(key, key);
1953 }
1954
1955 assert_eq!(cache.small_keys(), vec![1, 0]);
1956 assert_eq!(cache.main_keys(), (2..20).collect::<Vec<_>>());
1957 for key in 2..20u64 {
1958 let slot = cache.find_slot(&key).unwrap();
1959 assert_eq!(cache.bits(slot) & REFERENCED, 0);
1960 }
1961 cache.check_invariants();
1962 }
1963
1964 #[test]
1965 fn test_empty_residents_preserve_precise_ghost_history() {
1966 let mut cache = Cache::new(NZUsize!(2));
1969 for key in 1..=3u64 {
1970 cache.put(key, key);
1971 }
1972 assert_eq!(cache.ghost_keys(), vec![1]);
1973
1974 cache.retain(|key, _| *key == 1);
1975 assert_eq!(cache.len(), 0);
1976 assert_eq!(cache.ghost_keys(), vec![1]);
1977 assert!(!cache.ghost.index.is_empty());
1978
1979 cache.retain(|_, _| false);
1980 assert_eq!(cache.small.len + cache.main.len, 0);
1981 assert_eq!(cache.ghost_keys(), vec![1]);
1982 cache.check_invariants();
1983 }
1984
1985 #[test]
1986 fn test_correlation_window_ignores_only_young_hits() {
1987 let mut correlated = Cache::new(NZUsize!(40));
1990 for key in 1..=40u64 {
1991 correlated.put(key, key);
1992 }
1993 assert_eq!(correlated.small_keys(), vec![4, 3, 2, 1]);
1994 assert_eq!(correlated.get(&4), Some(&4));
1995 let slot = correlated.find_slot(&4).unwrap();
1996 assert_eq!(correlated.bits(slot) & REFERENCED, 0);
1997 for key in 41..=44u64 {
1998 correlated.put(key, key);
1999 }
2000 assert!(!correlated.contains(&4));
2001 assert!(!correlated.main_keys().contains(&4));
2002 assert!(correlated.ghost_keys().contains(&4));
2003 correlated.check_invariants();
2004
2005 let mut reused = Cache::new(NZUsize!(40));
2008 for key in 1..=40u64 {
2009 reused.put(key, key);
2010 }
2011 assert_eq!(reused.get(&1), Some(&1));
2012 reused.put(41, 41);
2013 assert!(reused.small_keys().contains(&41));
2014 assert!(reused.main_keys().contains(&1));
2015 let slot = reused.find_slot(&1).unwrap();
2016 assert_eq!(reused.bits(slot) & REFERENCED, 0);
2017 assert!(reused.ghost_keys().is_empty());
2018 reused.check_invariants();
2019 }
2020
2021 #[test]
2022 fn test_removing_younger_entry_does_not_rewind_correlation_age() {
2023 let mut cache = Cache::new(NZUsize!(20));
2024 for key in 0..20u64 {
2025 cache.put(key, key);
2026 }
2027
2028 assert_eq!(cache.get(&0), Some(&0));
2031
2032 assert!(cache.remove(&1));
2035 cache.put(20, 20);
2036 cache.put(21, 21);
2037
2038 assert!(cache.main_keys().contains(&0));
2039 assert!(!cache.ghost_keys().contains(&0));
2040 cache.check_invariants();
2041 }
2042
2043 #[test]
2044 fn test_removing_younger_entry_does_not_delay_correlation_age() {
2045 let mut cache = Cache::new(NZUsize!(40));
2046 for key in 0..40u64 {
2047 cache.put(key, key);
2048 }
2049 assert_eq!(cache.small_keys(), vec![3, 2, 1, 0]);
2050
2051 assert!(cache.remove(&3));
2054 cache.put(40, 40);
2055 assert_eq!(cache.get(&2), Some(&2));
2056
2057 for key in 41..=43u64 {
2060 cache.put(key, key);
2061 }
2062 assert!(cache.main_keys().contains(&2));
2063 assert!(!cache.ghost_keys().contains(&2));
2064 cache.check_invariants();
2065 }
2066
2067 #[test]
2068 fn test_ghost_history_is_bounded_and_promotes_reuse() {
2069 let mut cache = Cache::new(NZUsize!(20));
2071 for key in 0..=30u64 {
2072 cache.put(key, key);
2073 }
2074 assert_eq!(cache.small_keys(), vec![30, 29]);
2075 assert_eq!(cache.main_keys(), (2..20).collect::<Vec<_>>());
2076 assert_eq!(
2077 cache.ghost_keys(),
2078 (20..=28)
2079 .rev()
2080 .chain(core::iter::once(1))
2081 .collect::<Vec<_>>()
2082 );
2083
2084 let (_, value) = cache.get_or_insert_mut(1, || unreachable!());
2087 *value = 100;
2088 assert_eq!(cache.peek(&1), Some(&100));
2089 assert!(cache.main_keys().contains(&1));
2090 assert!(!cache.ghost_keys().contains(&1));
2091 assert_eq!(cache.small_keys(), vec![30, 29]);
2092 assert_eq!(cache.ghost_keys(), (20..=28).rev().collect::<Vec<_>>());
2093
2094 cache.put(0, 200);
2097 assert_eq!(cache.small_keys()[0], 0);
2098 assert!(!cache.main_keys().contains(&0));
2099 assert_eq!(cache.ghost_keys()[0], 29);
2100 cache.check_invariants();
2101 }
2102
2103 #[test]
2104 fn test_main_evicts_despite_an_offered_vacancy() {
2105 let mut cache = Cache::new(NZUsize!(2));
2108 for key in 1..=3u64 {
2109 cache.put(key, key);
2110 }
2111 assert_eq!(cache.main_keys(), vec![2]);
2112 assert!(cache.remove(&3));
2113 assert_eq!(cache.len(), 1);
2114 assert_eq!(cache.ghost_keys(), vec![1]);
2115
2116 cache.put(1, 10);
2119 assert_eq!(cache.len(), 1);
2120 assert_eq!(cache.main_keys(), vec![1]);
2121 assert!(!cache.contains(&2));
2122 cache.check_invariants();
2123 }
2124
2125 #[test]
2126 fn test_promoting_small_replaces_from_full_main() {
2127 let mut cache = Cache::new(NZUsize!(20));
2130 for key in 0..20u64 {
2131 cache.put(key, key);
2132 }
2133 for key in 0..18u64 {
2134 assert_eq!(cache.get(&key), Some(&key));
2135 }
2136 cache.put(20, 20);
2137 assert_eq!(cache.main.len, cache.main.capacity);
2138 assert_eq!(cache.small.len, 2);
2139 let slot = cache.find_slot(&0).unwrap();
2140 assert_eq!(cache.bits(slot) & REFERENCED, 0);
2141
2142 assert_eq!(
2145 cache.main_keys(),
2146 core::iter::once(19)
2147 .chain(2..=17)
2148 .chain(core::iter::once(0))
2149 .collect::<Vec<_>>()
2150 );
2151
2152 let promoted = *cache.small_keys().last().unwrap();
2155 assert_eq!(promoted, 1);
2156 assert_eq!(cache.get(&promoted), Some(&promoted));
2157 cache.put(21, 21);
2158
2159 assert_eq!(
2162 cache.main_keys(),
2163 (2..=17).chain([0, 1]).collect::<Vec<_>>()
2164 );
2165 let slot = cache.find_slot(&promoted).unwrap();
2166 assert_eq!(cache.bits(slot) & REFERENCED, 0);
2167 assert!(cache.small_keys().contains(&21));
2168 assert_eq!(cache.main.len, cache.main.capacity);
2169 assert_eq!(cache.small.len, 2);
2170 cache.check_invariants();
2171 }
2172
2173 #[test]
2174 fn test_slot_stays_stable_on_promotion_and_goes_stale_on_reuse() {
2175 let mut cache = Cache::new(NZUsize!(40));
2178 let (slot1, value) = cache.get_or_insert_mut(1u64, || 0u64);
2179 *value = 10;
2180 let (slot2, value) = cache.get_or_insert_mut(2, || 0);
2181 *value = 20;
2182 for key in 3..=40u64 {
2183 cache.put(key, key * 10);
2184 }
2185 assert_eq!(cache.get_at(slot1, &1), Some(&10));
2186 cache.put(41, 410);
2187 assert_eq!(cache.get_at(slot1, &1), Some(&10));
2188 assert!(cache.main_keys().contains(&1));
2189
2190 assert!(cache.slots[slot2].live);
2193 assert_eq!(cache.slots[slot2].key, 2);
2194 cache.put(42, 420);
2195 assert_eq!(cache.get_at(slot2, &2), None);
2196 let slot42 = cache.find_slot(&42).unwrap();
2197 assert_eq!(slot42, slot2);
2198 assert_eq!(cache.get_at(slot2, &2), None);
2199 assert_eq!(cache.get_at(slot42, &42), Some(&420));
2200 cache.check_invariants();
2201 }
2202
2203 #[test]
2204 fn test_scan_does_not_displace_main() {
2205 let mut cache = Cache::new(NZUsize!(10));
2208 for key in 0..10u64 {
2209 cache.put(key, key);
2210 }
2211 let mut protected = 1..10u64;
2212 assert_eq!(cache.main.len, 9);
2213
2214 for key in 100..110u64 {
2215 cache.put(key, key);
2216 }
2217 assert!(protected.all(|key| cache.peek(&key).is_some()));
2218 cache.check_invariants();
2219 }
2220
2221 #[test]
2222 fn test_prefill_reuses_values_through_churn() {
2223 let mut makes = 0usize;
2226 let mut cache = Cache::<u64, Vec<u8>>::new(NZUsize!(20));
2227 cache.prefill(|| {
2228 makes += 1;
2229 vec![0; 32]
2230 });
2231 assert_eq!(makes, 20);
2232
2233 for key in 0..2_000u64 {
2234 let (_, value) = cache.get_or_insert_mut(key, || unreachable!());
2235 value[0] = key as u8;
2236 cache.check_invariants();
2237 }
2238 assert_eq!(cache.slots.len(), 20);
2239 assert_eq!(makes, 20);
2240 }
2241
2242 #[test]
2243 fn test_remove_retain_and_clear_repair_resident_state() {
2244 let mut cache = Cache::new(NZUsize!(20));
2247 for key in 0..=30u64 {
2248 cache.put(key, key);
2249 }
2250 cache.put(1, 1);
2251 assert!(cache.remove(&1));
2252 assert!(!cache.remove(&1));
2253
2254 let ghost = cache.ghost_keys()[0];
2255 assert!(!cache.remove(&ghost));
2256 assert!(!cache.ghost_keys().contains(&ghost));
2257
2258 cache.put(100, 100);
2261 cache.put(ghost, ghost);
2262 assert!(cache.small_keys().contains(&ghost));
2263 assert!(!cache.main_keys().contains(&ghost));
2264
2265 cache.retain(|key, _| key % 2 == 0);
2268 assert!(!cache.ghost_keys().is_empty());
2269 assert!(
2270 cache
2271 .index
2272 .iter()
2273 .all(|&slot| cache.slots[slot].key % 2 == 0)
2274 );
2275 cache.check_invariants();
2276
2277 cache.clear();
2278 assert!(cache.is_empty());
2279 assert_eq!(cache.small.len + cache.main.len, 0);
2280 assert!(cache.ghost.index.is_empty());
2281 assert!(cache.ghost_keys().is_empty());
2282 cache.check_invariants();
2283 }
2284
2285 #[test]
2286 fn test_shared_hits_are_concurrent() {
2287 let mut cache = Cache::new(NZUsize!(40));
2290 for key in 1..=40u64 {
2291 cache.put(key, key);
2292 }
2293 let slot = cache.find_slot(&1).unwrap();
2294 let barrier = Barrier::new(5);
2295 thread::scope(|s| {
2296 for _ in 0..4 {
2297 s.spawn(|| {
2298 barrier.wait();
2299 for _ in 0..1_000 {
2300 assert_eq!(cache.get_at(slot, &1), Some(&1));
2301 }
2302 });
2303 }
2304 barrier.wait();
2305 });
2306
2307 cache.put(41, 41);
2308 assert!(cache.main_keys().contains(&1));
2309 cache.check_invariants();
2310 }
2311
2312 #[test]
2313 fn test_fallible_factory_error_leaves_cache_unchanged() {
2314 let mut cache = Cache::new(NZUsize!(2));
2317 for key in 1..=3u64 {
2318 cache.put(key, key);
2319 }
2320 let small = cache.small_keys();
2321 let main = cache.main_keys();
2322 let ghost = cache.ghost_keys();
2323 let result = cache.try_get_or_insert_with(1, || Err::<u64, _>("failure"));
2324 assert_eq!(result, Err("failure"));
2325 assert_eq!(cache.small_keys(), small);
2326 assert_eq!(cache.main_keys(), main);
2327 assert_eq!(cache.ghost_keys(), ghost);
2328 cache.check_invariants();
2329 }
2330
2331 #[test]
2332 fn test_correlation_age_survives_admission_counter_wrap() {
2333 let mut wrapped = Cache::new(NZUsize!(40));
2336 wrapped.small.admissions = ADMISSION_GENERATION_MASK - 1;
2337 let mut plain = Cache::new(NZUsize!(40));
2338 for key in 1..=40u64 {
2339 wrapped.put(key, key);
2340 plain.put(key, key);
2341 }
2342 assert_eq!(wrapped.small_keys(), plain.small_keys());
2343 for key in wrapped.small_keys() {
2344 assert_eq!(
2345 wrapped.bits(wrapped.find_slot(&key).unwrap()),
2346 plain.bits(plain.find_slot(&key).unwrap())
2347 );
2348 }
2349
2350 assert_eq!(wrapped.get(&1), Some(&1));
2355 wrapped.retain(|key, _| *key != 4);
2356 assert!(wrapped.remove(&3));
2357 assert_eq!(wrapped.small.young_tail, None);
2358
2359 for cycle in 0..8u64 {
2360 let first = 41 + 2 * cycle;
2361 let second = first + 1;
2362 wrapped.small.admissions = ADMISSION_GENERATION_MASK - 1;
2363 wrapped.put(first, first);
2364 let expected_young_tail = wrapped.find_slot(&first);
2365 wrapped.put(second, second);
2366 assert_eq!(wrapped.small.young_tail, expected_young_tail);
2367 wrapped.check_invariants();
2368
2369 if cycle < 7 {
2370 wrapped.retain(|key, _| *key != second);
2371 assert!(wrapped.remove(&first));
2372 assert_eq!(wrapped.small.young_tail, None);
2373 }
2374 }
2375
2376 wrapped.put(57, 57);
2377 assert!(wrapped.main_keys().contains(&1));
2378 wrapped.check_invariants();
2379
2380 wrapped.clear();
2381 assert_eq!(wrapped.small.admissions, 0);
2382 wrapped.check_invariants();
2383 }
2384
2385 #[derive(Debug, PartialEq, Eq)]
2386 struct GhostKey {
2387 value: u64,
2388 owner: Rc<()>,
2389 }
2390
2391 impl Hash for GhostKey {
2392 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
2393 state.write_u8(0);
2394 }
2395 }
2396
2397 fn exercise_colliding_ghost(capacity: usize, reserve: bool) {
2398 let owner = Rc::new(());
2399 let hasher = Hasher::with_seeds(1, 2, 3, 4);
2400 let key = |value| GhostKey {
2401 value,
2402 owner: Rc::clone(&owner),
2403 };
2404 let mut ghost = GhostQueue::new(capacity);
2405 if !reserve {
2406 ghost.index = HashTable::new();
2408 }
2409 let mut expected = VecDeque::new();
2410 for step in 0..2048u64 {
2411 let value = (step * 17 + step / 5) % 127;
2412 {
2413 let position = expected.iter().position(|entry| *entry == value);
2414 let requested = key(value);
2415 let requested_hash = hasher.hash_one(&requested);
2416 assert_eq!(
2417 ghost.discard(&requested, requested_hash),
2418 position.is_some()
2419 );
2420 if let Some(position) = position {
2421 expected.remove(position);
2422 }
2423 if step % 5 != 0 {
2424 let historical = key(value);
2425 let historical_hash = hasher.hash_one(&historical);
2426 ghost.push(historical, historical_hash, &hasher);
2427 if capacity != 0 {
2428 expected.push_front(value);
2429 expected.truncate(capacity);
2430 }
2431 }
2432 }
2433 if step == 777 || step == 1333 {
2434 ghost.clear();
2435 expected.clear();
2436 }
2437
2438 let mut actual = Vec::new();
2439 let mut linked_slots = HashSet::new();
2440 let mut previous = None;
2441 let mut current = ghost.head;
2442 while let Some(slot) = current {
2443 assert!(linked_slots.insert(slot));
2444 let entry = &ghost.slots[slot];
2445 assert_eq!(linked(entry.prev), previous);
2446 actual.push(entry.key.as_ref().unwrap().value);
2447 previous = current;
2448 current = linked(entry.next);
2449 }
2450 assert_eq!(ghost.tail, previous);
2451 assert_eq!(actual, expected.iter().copied().collect::<Vec<_>>());
2452 assert_eq!(
2453 ghost.index.iter().copied().collect::<HashSet<_>>(),
2454 linked_slots
2455 );
2456 assert_eq!(ghost.index.len(), expected.len());
2457 assert!(ghost.slots.len() <= capacity);
2458 let free = ghost.check_free_slots();
2459 for (slot, entry) in ghost.slots.iter().enumerate() {
2460 assert_eq!(entry.key.is_some(), linked_slots.contains(&slot));
2461 assert_eq!(free.contains(&slot), entry.key.is_none());
2462 }
2463
2464 assert_eq!(Rc::strong_count(&owner), expected.len() + 1);
2466 }
2467 drop(ghost);
2468 assert_eq!(Rc::strong_count(&owner), 1);
2469 }
2470
2471 #[test]
2472 fn test_ghost_collision_churn_and_key_ownership() {
2473 for capacity in [0, 1, 2, 31] {
2474 exercise_colliding_ghost(capacity, true);
2475 }
2476 }
2477
2478 #[test]
2479 fn test_ghost_rehash_reads_live_keys() {
2480 exercise_colliding_ghost(31, false);
2481 }
2482
2483 #[derive(Clone, Debug, Eq, PartialEq)]
2484 struct CollidingKey(u64);
2485
2486 impl Hash for CollidingKey {
2487 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
2488 state.write_u8(0);
2489 }
2490 }
2491
2492 #[test]
2493 fn test_shared_hasher_drives_resident_and_ghost_rehashes() {
2494 let mut cache = Cache::new(NZUsize!(20));
2495
2496 cache.index = HashTable::new();
2499 cache.ghost.index = HashTable::new();
2500 cache.hasher = Hasher::with_seeds(1, 2, 3, 4);
2501
2502 let mut resident_grew = false;
2503 let mut ghost_grew = false;
2504 for key in 0..256 {
2505 let resident_capacity = cache.index.capacity();
2506 let ghost_capacity = cache.ghost.index.capacity();
2507 cache.put(CollidingKey(key), key);
2508 resident_grew |= cache.index.capacity() > resident_capacity;
2509 ghost_grew |= cache.ghost.index.capacity() > ghost_capacity;
2510 cache.check_invariants();
2511 }
2512 assert!(resident_grew, "resident index must rehash");
2513 assert!(ghost_grew, "Ghost index must rehash");
2514
2515 let ghost = cache.ghost_keys();
2516 assert_eq!(ghost.len(), cache.ghost.capacity);
2517 assert_eq!(ghost.iter().collect::<HashSet<_>>().len(), ghost.len());
2518 assert!(ghost.iter().all(|key| !cache.contains(key)));
2519
2520 let requested = ghost[ghost.len() / 2].clone();
2522 cache.put(requested.clone(), u64::MAX);
2523 assert_eq!(cache.get(&requested), Some(&u64::MAX));
2524 assert!(!cache.ghost_keys().contains(&requested));
2525 cache.check_invariants();
2526 }
2527
2528 #[test]
2529 fn test_borrowed_keys_keep_independent_ghost_history() {
2530 let names = [
2531 String::from("first"),
2532 String::from("second"),
2533 String::from("third"),
2534 ];
2535 let mut cache = Cache::new(NZUsize!(2));
2536 for (value, name) in names.iter().enumerate() {
2537 cache.put(vec![name.as_str()], value);
2538 }
2539 assert_eq!(cache.ghost_keys(), vec![vec![names[0].as_str()]]);
2540 assert!(!cache.remove(&vec![names[0].as_str()]));
2541 assert!(cache.ghost_keys().is_empty());
2542 assert_eq!(cache.get(&vec![names[2].as_str()]), Some(&2));
2543 cache.check_invariants();
2544 }
2545
2546 #[test]
2547 fn test_main_sweep_reaches_unreferenced_resident_beyond_128() {
2548 let mut cache = Cache::new(NZUsize!(200));
2549 for key in 0..200u64 {
2550 cache.put(key, key);
2551 }
2552 for key in 20..149 {
2553 assert_eq!(cache.get(&key), Some(&key));
2554 }
2555 cache.put(200, 200);
2556 assert_eq!(cache.ghost_keys(), vec![0]);
2557 cache.put(0, 0);
2558 assert_eq!(cache.peek(&149), None);
2559 for key in 20..149 {
2560 assert_eq!(cache.peek(&key), Some(&key));
2561 assert_eq!(cache.bits(cache.find_slot(&key).unwrap()), 0);
2562 }
2563 cache.check_invariants();
2564 }
2565
2566 #[derive(Clone)]
2567 struct CountingKey {
2568 id: usize,
2569 hashes: Rc<Cell<usize>>,
2570 }
2571
2572 impl CountingKey {
2573 fn new(id: usize) -> Self {
2574 Self {
2575 id,
2576 hashes: Rc::new(Cell::new(0)),
2577 }
2578 }
2579
2580 fn hashes(&self) -> usize {
2581 self.hashes.get()
2582 }
2583 }
2584
2585 impl PartialEq for CountingKey {
2586 fn eq(&self, other: &Self) -> bool {
2587 self.id == other.id
2588 }
2589 }
2590
2591 impl Eq for CountingKey {}
2592
2593 impl Hash for CountingKey {
2594 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
2595 self.hashes.set(self.hashes.get() + 1);
2596 self.id.hash(state);
2597 }
2598 }
2599
2600 #[test]
2601 fn shared_hashes_without_rehash_steps() {
2602 let one = CountingKey::new(1);
2603 let two = CountingKey::new(2);
2604 let three = CountingKey::new(3);
2605 let mut cache = Cache::<CountingKey, usize>::new(NonZeroUsize::new(2).unwrap());
2606
2607 cache.put(one.clone(), 1);
2610 cache.put(two.clone(), 2);
2611 assert_eq!(one.hashes(), 1);
2612 assert_eq!(two.hashes(), 1);
2613
2614 cache.put(three.clone(), 3);
2617 assert_eq!(one.hashes(), 2);
2618 assert_eq!(two.hashes(), 1);
2619 assert_eq!(three.hashes(), 1);
2620
2621 assert!(!cache.remove(&one));
2623 assert_eq!(one.hashes(), 3);
2624
2625 cache.retain(|key, _| key.id != 3);
2628 assert_eq!(three.hashes(), 1);
2629 cache.clear();
2630 assert_eq!(two.hashes(), 1);
2631 assert_eq!(three.hashes(), 1);
2632 }
2633
2634 #[derive(Clone, Default, Eq, PartialEq)]
2635 struct ProbeKey<const COLLIDE: bool>(String);
2636 impl<const COLLIDE: bool> Hash for ProbeKey<COLLIDE> {
2637 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
2638 if COLLIDE {
2639 0u8.hash(state);
2640 } else {
2641 self.0.hash(state);
2642 }
2643 }
2644 }
2645
2646 fn exercise_rehash<const COLLIDE: bool>() {
2647 for capacity in [1, 2, 31, 64] {
2648 for prefill in [false, true] {
2649 let mut cache =
2650 Cache::<ProbeKey<COLLIDE>, Vec<u8>>::new(NonZeroUsize::new(capacity).unwrap());
2651
2652 cache.index = HashTable::new();
2655 cache.hasher = super::Hasher::with_seeds(1, 2, 3, 4);
2656 if prefill {
2657 cache.prefill(Vec::new);
2658 }
2659 let mut hints = Vec::new();
2660 let mut live = HashMap::new();
2661 let mut grew = false;
2662 for step in 0..1024usize {
2663 let key = ProbeKey::<COLLIDE>(format!("key-{step}"));
2664 let before = cache.index.capacity();
2665 let (slot, value) = cache.get_or_insert_mut(key.clone(), Vec::new);
2666 value.clear();
2667 value.extend_from_slice(&step.to_le_bytes());
2668 let value_address = value as *const Vec<u8>;
2669 grew |= cache.index.capacity() > before;
2670 live.retain(|old_key, old: &mut (usize, *const Vec<u8>)| {
2671 if old.0 == slot {
2672 assert!(cache.get_at(old.0, old_key).is_none());
2673 false
2674 } else {
2675 true
2676 }
2677 });
2678 live.insert(key.clone(), (slot, value_address));
2679 hints.push((slot, key));
2680 if step % 11 == 5 {
2681 let removed =
2682 ProbeKey::<COLLIDE>(format!("key-{}", step.saturating_sub(1)));
2683 assert_eq!(cache.remove(&removed), live.remove(&removed).is_some());
2684 }
2685 if step % 37 == 9 {
2686 cache.retain(|key, _| key.0.as_bytes().last().unwrap() % 2 == 0);
2687 live.retain(|key, _| key.0.as_bytes().last().unwrap() % 2 == 0);
2688 }
2689 if step % 127 == 100 {
2690 cache.clear();
2691 live.clear();
2692 if prefill {
2693 cache.prefill(Vec::new);
2694 }
2695 }
2696 assert_eq!(cache.len(), live.len());
2697 cache.check_invariants();
2698 for (key, &(slot, address)) in &live {
2699 let value = cache.peek(key).unwrap();
2700 let expected = key.0[4..].parse::<usize>().unwrap();
2701 assert_eq!(value.as_slice(), expected.to_le_bytes());
2702 assert_eq!(value as *const Vec<u8>, address);
2703 assert_eq!(cache.get_at(slot, key), Some(value));
2704 }
2705 for (slot, key) in &hints {
2706 let expected = live.get(key).filter(|entry| entry.0 == *slot);
2707 assert_eq!(cache.get_at(*slot, key).is_some(), expected.is_some());
2708 }
2709 assert!(
2710 cache
2711 .get_at(usize::MAX, &ProbeKey::<COLLIDE>::default())
2712 .is_none()
2713 );
2714 }
2715 assert!(grew, "the test must exercise index growth");
2716 }
2717 }
2718 }
2719
2720 #[test]
2721 fn collisions_rehash_and_stable_values() {
2722 exercise_rehash::<true>();
2723 }
2724
2725 #[derive(Default)]
2726 struct Counts {
2727 owners: RefCell<HashMap<usize, usize>>,
2728 clones: RefCell<Vec<usize>>,
2729 values: Cell<usize>,
2730 }
2731 struct OwnedKey {
2732 id: usize,
2733 counts: Rc<Counts>,
2734 }
2735 impl OwnedKey {
2736 fn new(id: usize, counts: &Rc<Counts>) -> Self {
2737 *counts.owners.borrow_mut().entry(id).or_default() += 1;
2738 Self {
2739 id,
2740 counts: Rc::clone(counts),
2741 }
2742 }
2743 }
2744 impl Clone for OwnedKey {
2745 fn clone(&self) -> Self {
2746 self.counts.clones.borrow_mut().push(self.id);
2747 Self::new(self.id, &self.counts)
2748 }
2749 }
2750 impl PartialEq for OwnedKey {
2751 fn eq(&self, other: &Self) -> bool {
2752 self.id == other.id
2753 }
2754 }
2755 impl Eq for OwnedKey {}
2756 impl Hash for OwnedKey {
2757 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
2758 0u8.hash(state);
2759 }
2760 }
2761 impl Drop for OwnedKey {
2762 fn drop(&mut self) {
2763 *self.counts.owners.borrow_mut().get_mut(&self.id).unwrap() -= 1;
2764 }
2765 }
2766 struct OwnedValue(Rc<Counts>);
2767 impl Drop for OwnedValue {
2768 fn drop(&mut self) {
2769 self.0.values.set(self.0.values.get() + 1);
2770 }
2771 }
2772
2773 #[test]
2774 fn eviction_does_not_clone_keys() {
2775 let counts = Rc::new(Counts::default());
2776 let mut cache = Cache::<OwnedKey, OwnedValue>::new(NonZeroUsize::new(2).unwrap());
2777 for id in 0..8 {
2778 cache.put(OwnedKey::new(id, &counts), OwnedValue(Rc::clone(&counts)));
2779 }
2780 assert!(counts.clones.borrow().is_empty());
2781 assert_eq!(counts.values.get(), 6);
2782 drop(cache);
2783 assert!(counts.owners.borrow().values().all(|&n| n == 0));
2784 assert_eq!(counts.values.get(), 8);
2785 }
2786
2787 #[test]
2788 fn canonical_key_move_and_ghost_drop_ownership() {
2789 let counts = Rc::new(Counts::default());
2790 let mut cache = Cache::<OwnedKey, OwnedValue>::new(NonZeroUsize::new(2).unwrap());
2791 for id in 0..2 {
2792 cache.put(OwnedKey::new(id, &counts), OwnedValue(Rc::clone(&counts)));
2793 }
2794 assert!(
2795 counts.clones.borrow().is_empty(),
2796 "vacant insertion owns the incoming key directly"
2797 );
2798 assert!(counts.owners.borrow().values().all(|&n| n == 1));
2799 cache.get_or_insert_mut(OwnedKey::new(2, &counts), || {
2800 panic!("eviction must reuse V")
2801 });
2802 assert!(counts.clones.borrow().is_empty());
2803 assert_eq!(
2804 *counts.owners.borrow(),
2805 HashMap::from([(0, 1), (1, 1), (2, 1)])
2806 );
2807
2808 cache.get_or_insert_mut(OwnedKey::new(0, &counts), || {
2810 panic!("Ghost hit must reuse V")
2811 });
2812 assert!(counts.clones.borrow().is_empty());
2813 assert_eq!(
2814 *counts.owners.borrow(),
2815 HashMap::from([(0, 1), (1, 0), (2, 1)])
2816 );
2817 assert!(cache.remove(&OwnedKey::new(2, &counts)));
2818 assert_eq!(
2819 counts.owners.borrow()[&2],
2820 1,
2821 "free slots retain their stale key"
2822 );
2823 cache.get_or_insert_mut(OwnedKey::new(3, &counts), || {
2824 panic!("free slot must reuse V")
2825 });
2826 assert!(
2827 counts.clones.borrow().is_empty(),
2828 "free reuse must not clone keys"
2829 );
2830 assert_eq!(counts.owners.borrow()[&2], 0);
2831 assert_eq!(counts.values.get(), 0);
2832 cache.retain(|_, _| false);
2833 assert!(cache.is_empty());
2834 assert_eq!(counts.values.get(), 0);
2835 cache.clear();
2836 assert!(counts.owners.borrow().values().all(|&n| n == 0));
2837 assert_eq!(counts.values.get(), 2);
2838 cache.put(OwnedKey::new(4, &counts), OwnedValue(Rc::clone(&counts)));
2839 drop(cache);
2840 assert!(counts.owners.borrow().values().all(|&n| n == 0));
2841 assert_eq!(counts.values.get(), 3);
2842 }
2843
2844 #[test]
2845 fn distinct_hashes_rehash_and_stable_values() {
2846 exercise_rehash::<false>();
2847 }
2848
2849 #[derive(Debug)]
2851 struct NonCloneKey {
2852 id: usize,
2853 metadata: usize,
2854 }
2855 impl PartialEq for NonCloneKey {
2856 fn eq(&self, other: &Self) -> bool {
2857 self.id == other.id
2858 }
2859 }
2860 impl Eq for NonCloneKey {}
2861 impl Hash for NonCloneKey {
2862 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
2863 0u8.hash(state);
2864 }
2865 }
2866
2867 #[test]
2868 fn non_clone_keys_keep_incoming_metadata_on_ghost_hits() {
2869 let mut cache = Cache::new(NonZeroUsize::new(2).unwrap());
2870 let key = |id, metadata| NonCloneKey { id, metadata };
2871 let (original_slot, _) = cache.get_or_insert_mut(key(0, 10), || 0);
2872 cache.put(key(1, 11), 1);
2873 cache.put(key(2, 12), 2);
2874 assert_eq!(cache.get_at(original_slot, &key(0, 0)), None);
2875 cache.check_invariants();
2876
2877 let (slot, value) = cache.get_or_insert_mut(key(0, 20), || unreachable!());
2879 *value = 20;
2880 assert_eq!(cache.get_at(slot, &key(0, 0)), Some(&20));
2881 cache.retain(|key, value| {
2882 if key.id == 0 {
2883 assert_eq!(key.metadata, 20);
2884 assert_eq!(*value, 20);
2885 }
2886 true
2887 });
2888 cache.check_invariants();
2889
2890 assert!(cache.remove(&key(0, 0)));
2891 assert_eq!(cache.get_at(slot, &key(0, 0)), None);
2892 let (reused, value) = cache.get_or_insert_mut(key(3, 30), || unreachable!());
2893 assert_eq!(reused, slot);
2894 assert_eq!(*value, 20);
2895 cache.check_invariants();
2896 cache.clear();
2897 cache.check_invariants();
2898 }
2899
2900 struct PanickingKey {
2901 id: usize,
2902 panic_on: Rc<Cell<Option<usize>>>,
2903 }
2904 impl PartialEq for PanickingKey {
2905 fn eq(&self, other: &Self) -> bool {
2906 self.id == other.id
2907 }
2908 }
2909 impl Eq for PanickingKey {}
2910 impl Hash for PanickingKey {
2911 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
2912 self.id.hash(state);
2913 }
2914 }
2915 impl Drop for PanickingKey {
2916 fn drop(&mut self) {
2917 if self.panic_on.get() == Some(self.id) {
2918 self.panic_on.set(None);
2919 panic!("key destructor panic");
2920 }
2921 }
2922 }
2923
2924 #[test]
2925 fn displaced_key_destructors_follow_replacement_bookkeeping() {
2926 let panic_on = Rc::new(Cell::new(None));
2927 let key = |id| PanickingKey {
2928 id,
2929 panic_on: Rc::clone(&panic_on),
2930 };
2931 let mut cache = Cache::new(NonZeroUsize::new(2).unwrap());
2932 for id in 0..3 {
2933 cache.put(key(id), id);
2934 }
2935
2936 panic_on.set(Some(0));
2938 let result =
2939 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| cache.put(key(3), 3)));
2940 assert!(result.is_err());
2941 assert_eq!(cache.peek(&key(3)), Some(&3));
2942 cache.check_invariants();
2943
2944 panic_on.set(Some(2));
2946 let result =
2947 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| cache.remove(&key(2))));
2948 assert!(result.is_err());
2949 cache.check_invariants();
2950 cache.put(key(4), 4);
2951 cache.check_invariants();
2952
2953 panic_on.set(Some(1));
2955 let result =
2956 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| cache.put(key(3), 30)));
2957 assert!(result.is_err());
2958 assert_eq!(cache.peek(&key(3)), Some(&30));
2959 cache.check_invariants();
2960
2961 assert!(cache.remove(&key(4)));
2963 panic_on.set(Some(4));
2964 let result =
2965 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| cache.put(key(5), 5)));
2966 assert!(result.is_err());
2967 assert_eq!(cache.peek(&key(5)), Some(&5));
2968 cache.check_invariants();
2969 }
2970}