1#[cfg(not(feature = "std"))]
68use alloc::vec::Vec;
69use core::{
70 hash::Hash,
71 num::NonZeroUsize,
72 sync::atomic::{AtomicBool, Ordering},
73};
74use hashbrown::HashMap;
75
76type Hasher = ahash::RandomState;
77
78struct Slot<K, V> {
84 key: K,
85 value: V,
86 referenced: AtomicBool,
87 live: bool,
88}
89
90pub struct Clock<K, V> {
96 index: HashMap<K, usize, Hasher>,
100 slots: Vec<Slot<K, V>>,
102 free: Vec<usize>,
106 hand: usize,
108 capacity: usize,
110}
111
112impl<K: Hash + Eq + Clone, V> Clock<K, V> {
113 pub fn new(capacity: NonZeroUsize) -> Self {
115 let capacity = capacity.get();
116 Self {
117 index: HashMap::with_capacity_and_hasher(capacity, Hasher::default()),
118 slots: Vec::with_capacity(capacity),
119 free: Vec::new(),
120 hand: 0,
121 capacity,
122 }
123 }
124
125 #[inline]
127 pub const fn capacity(&self) -> usize {
128 self.capacity
129 }
130
131 #[inline]
133 pub fn len(&self) -> usize {
134 self.index.len()
135 }
136
137 #[inline]
139 pub fn is_empty(&self) -> bool {
140 self.index.is_empty()
141 }
142
143 #[inline]
145 pub fn contains(&self, key: &K) -> bool {
146 self.index.contains_key(key)
147 }
148
149 #[inline]
154 pub fn peek(&self, key: &K) -> Option<&V> {
155 let &slot = self.index.get(key)?;
156 Some(&self.slots[slot].value)
157 }
158
159 #[inline]
165 pub fn get(&self, key: &K) -> Option<&V> {
166 let &index = self.index.get(key)?;
167 let slot = &self.slots[index];
168
169 if !slot.referenced.load(Ordering::Relaxed) {
173 slot.referenced.store(true, Ordering::Relaxed);
174 }
175 Some(&slot.value)
176 }
177
178 #[inline]
189 pub fn get_at(&self, slot: usize, key: &K) -> Option<&V> {
190 let slot = self.slots.get(slot)?;
191 if !slot.live || slot.key != *key {
192 return None;
193 }
194
195 if !slot.referenced.load(Ordering::Relaxed) {
197 slot.referenced.store(true, Ordering::Relaxed);
198 }
199 Some(&slot.value)
200 }
201
202 #[inline]
204 pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
205 let &index = self.index.get(key)?;
206 let slot = &mut self.slots[index];
207 slot.referenced.store(true, Ordering::Relaxed);
208 Some(&mut slot.value)
209 }
210
211 pub fn put(&mut self, key: K, value: V) -> Option<V> {
217 if let Some(&index) = self.index.get(&key) {
218 let slot = &mut self.slots[index];
219 slot.referenced.store(true, Ordering::Relaxed);
220 return Some(core::mem::replace(&mut slot.value, value));
221 }
222 self.insert_value(key, value);
223 None
224 }
225
226 pub fn get_or_insert_with<F: FnOnce() -> V>(&mut self, key: K, f: F) -> &V {
233 let slot = match self.index.get(&key) {
234 Some(&slot) => {
235 self.slots[slot].referenced.store(true, Ordering::Relaxed);
236 slot
237 }
238 None => self.insert_value(key, f()),
239 };
240 &self.slots[slot].value
241 }
242
243 pub fn try_get_or_insert_with<F: FnOnce() -> Result<V, E>, E>(
250 &mut self,
251 key: K,
252 f: F,
253 ) -> Result<&V, E> {
254 let slot = match self.index.get(&key) {
255 Some(&slot) => {
256 self.slots[slot].referenced.store(true, Ordering::Relaxed);
257 slot
258 }
259 None => self.insert_value(key, f()?),
260 };
261 Ok(&self.slots[slot].value)
262 }
263
264 pub fn get_or_insert_mut<F: FnOnce() -> V>(&mut self, key: K, make: F) -> (usize, &mut V) {
278 let slot = match self.index.get(&key) {
279 Some(&slot) => {
280 self.slots[slot].referenced.store(true, Ordering::Relaxed);
281 slot
282 }
283 None => match self.take_slot() {
284 Some(slot) => {
285 self.slots[slot].key = key.clone();
286 self.slots[slot].referenced.store(true, Ordering::Relaxed);
287 self.slots[slot].live = true;
288 self.index.insert(key, slot);
289 slot
290 }
291 None => {
292 let value = make();
293 self.grow(key, value)
294 }
295 },
296 };
297 (slot, &mut self.slots[slot].value)
298 }
299
300 pub fn remove(&mut self, key: &K) -> bool {
305 match self.index.remove(key) {
306 Some(slot) => {
307 self.slots[slot].referenced.store(false, Ordering::Relaxed);
308 self.slots[slot].live = false;
309 self.free.push(slot);
310 true
311 }
312 None => false,
313 }
314 }
315
316 pub fn retain<F: FnMut(&K, &V) -> bool>(&mut self, mut keep: F) {
320 let Self {
321 index, slots, free, ..
322 } = self;
323 index.retain(|key, &mut slot| {
324 let keep = keep(key, &slots[slot].value);
325 if !keep {
326 slots[slot].referenced.store(false, Ordering::Relaxed);
327 slots[slot].live = false;
328 free.push(slot);
329 }
330 keep
331 });
332 }
333
334 pub fn clear(&mut self) {
337 self.index.clear();
338 self.slots.clear();
339 self.free.clear();
340 self.hand = 0;
341 }
342
343 fn grow(&mut self, key: K, value: V) -> usize {
347 let slot = self.slots.len();
348 self.index.insert(key.clone(), slot);
349 self.slots.push(Slot {
350 key,
351 value,
352 referenced: AtomicBool::new(true),
353 live: true,
354 });
355 slot
356 }
357
358 fn insert_value(&mut self, key: K, value: V) -> usize {
360 match self.take_slot() {
361 Some(slot) => {
362 self.slots[slot].key = key.clone();
363 self.slots[slot].value = value;
364 self.slots[slot].referenced.store(true, Ordering::Relaxed);
365 self.slots[slot].live = true;
366 self.index.insert(key, slot);
367 slot
368 }
369 None => self.grow(key, value),
370 }
371 }
372
373 fn take_slot(&mut self) -> Option<usize> {
379 if let Some(slot) = self.free.pop() {
380 return Some(slot);
381 }
382 if self.slots.len() < self.capacity {
383 return None;
384 }
385
386 let len = self.slots.len();
387 while self.slots[self.hand].referenced.load(Ordering::Relaxed) {
388 self.slots[self.hand]
389 .referenced
390 .store(false, Ordering::Relaxed);
391 self.hand = (self.hand + 1) % len;
392 }
393 let slot = self.hand;
394 self.hand = (self.hand + 1) % len;
395 self.index.remove(&self.slots[slot].key);
396 Some(slot)
397 }
398}
399
400impl<K: Hash + Eq + Clone + Default, V> Clock<K, V> {
401 pub fn prefill<F: FnMut() -> V>(&mut self, mut make: F) {
410 let start = self.free.len();
411 while self.slots.len() < self.capacity {
412 let slot = self.slots.len();
413 self.slots.push(Slot {
414 key: K::default(),
415 value: make(),
416 referenced: AtomicBool::new(false),
417 live: false,
418 });
419 self.free.push(slot);
420 }
421 self.free[start..].reverse();
424 }
425}
426
427impl<K, V> core::fmt::Debug for Clock<K, V> {
428 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
429 f.debug_struct("Clock")
430 .field("len", &self.index.len())
431 .field("capacity", &self.capacity)
432 .finish()
433 }
434}
435
436#[cfg(test)]
437mod tests {
438 use super::*;
439 use crate::NZUsize;
440 use core::cell::Cell;
441 use proptest::prelude::*;
442 use std::{collections::HashMap, rc::Rc, thread};
443
444 impl<K: Hash + Eq + Clone, V> Clock<K, V> {
445 fn check_invariants(&self) {
447 assert!(self.slots.len() <= self.capacity);
448 assert_eq!(self.index.len() + self.free.len(), self.slots.len());
449 if self.slots.is_empty() {
450 assert_eq!(self.hand, 0);
451 } else {
452 assert!(self.hand < self.slots.len());
453 }
454 let free: std::collections::HashSet<usize> = self.free.iter().copied().collect();
457 assert_eq!(free.len(), self.free.len(), "duplicate free slot");
458 let mut seen = std::collections::HashSet::new();
459 for (key, &slot) in &self.index {
460 assert!(slot < self.slots.len());
461 assert!(!free.contains(&slot), "slot {slot} both live and free");
462 assert!(seen.insert(slot), "slot {slot} mapped twice");
463 assert!(self.slots[slot].key == *key);
464 assert!(self.slots[slot].live, "indexed slot {slot} not live");
465 }
466 for &slot in &self.free {
467 assert!(!self.slots[slot].live, "free slot {slot} still live");
468 }
469 }
470 }
471
472 #[test]
473 fn test_basic_put_get_peek() {
474 let mut cache = Clock::new(NZUsize!(2));
475 assert!(cache.is_empty());
476 assert_eq!(cache.capacity(), 2);
477
478 assert_eq!(cache.put(1u64, 10u64), None);
479 assert_eq!(cache.put(2, 20), None);
480 assert_eq!(cache.len(), 2);
481
482 assert_eq!(cache.get(&1).copied(), Some(10));
483 assert_eq!(cache.peek(&2).copied(), Some(20));
484 assert!(cache.contains(&1));
485 assert!(!cache.contains(&3));
486 assert_eq!(cache.get(&3), None);
487 cache.check_invariants();
488 }
489
490 #[test]
491 fn test_put_replaces_existing() {
492 let mut cache = Clock::new(NZUsize!(2));
493 assert_eq!(cache.put(1u64, 10u64), None);
494 assert_eq!(cache.put(1, 11), Some(10));
495 assert_eq!(cache.get(&1).copied(), Some(11));
496 assert_eq!(cache.len(), 1);
497 cache.check_invariants();
498 }
499
500 #[test]
501 fn test_capacity_one_eviction() {
502 let mut cache = Clock::new(NZUsize!(1));
503 cache.put(1u64, 10u64);
504 cache.put(2, 20);
505 assert!(!cache.contains(&1));
506 assert_eq!(cache.get(&2).copied(), Some(20));
507 assert_eq!(cache.len(), 1);
508 cache.check_invariants();
509 }
510
511 #[test]
512 fn test_second_chance_protects_referenced_entry() {
513 let mut cache = Clock::new(NZUsize!(3));
517 cache.put(1u64, 10u64); cache.put(2, 20); cache.put(3, 30); cache.put(4, 40);
525 assert!(!cache.contains(&1));
526
527 assert_eq!(cache.get(&2).copied(), Some(20));
529
530 cache.put(5, 50);
533 assert!(cache.contains(&2));
534 assert!(!cache.contains(&3));
535 assert!(cache.contains(&4));
536 assert!(cache.contains(&5));
537 cache.check_invariants();
538 }
539
540 #[test]
541 fn test_all_referenced_evicts_hand_position() {
542 let mut cache = Clock::new(NZUsize!(3));
545 cache.put(1u64, 10u64);
546 cache.put(2, 20);
547 cache.put(3, 30);
548 assert!(cache.get(&1).is_some());
549 assert!(cache.get(&2).is_some());
550 assert!(cache.get(&3).is_some());
551
552 cache.put(4, 40);
554 assert!(!cache.contains(&1));
555 assert!(cache.contains(&2));
556 assert!(cache.contains(&3));
557 assert!(cache.contains(&4));
558 cache.check_invariants();
559 }
560
561 #[test]
562 fn test_get_or_insert_with_calls_f_only_on_miss() {
563 let mut cache = Clock::new(NZUsize!(2));
564 let calls = Cell::new(0);
565 let compute = |k: u64| {
566 calls.set(calls.get() + 1);
567 k * 100
568 };
569
570 assert_eq!(*cache.get_or_insert_with(1, || compute(1)), 100);
571 assert_eq!(calls.get(), 1);
572 assert_eq!(*cache.get_or_insert_with(1, || compute(1)), 100);
574 assert_eq!(calls.get(), 1);
575 cache.check_invariants();
576 }
577
578 #[test]
579 fn test_try_get_or_insert_with_does_not_cache_errors() {
580 let mut cache = Clock::new(NZUsize!(2));
581
582 let err: Result<&u64, &str> = cache.try_get_or_insert_with(1u64, || Err("bad"));
583 assert_eq!(err, Err("bad"));
584 assert!(!cache.contains(&1));
585
586 let ok: Result<&u64, &str> = cache.try_get_or_insert_with(1, || Ok(10));
587 assert_eq!(ok, Ok(&10));
588 assert!(cache.contains(&1));
589 cache.check_invariants();
590 }
591
592 #[test]
593 fn test_remove_keeps_slot_for_reuse() {
594 let makes = Cell::new(0);
597 let mut cache: Clock<u64, u64> = Clock::new(NZUsize!(2));
598 cache.get_or_insert_mut(1, || {
599 makes.set(makes.get() + 1);
600 10
601 });
602 cache.get_or_insert_mut(2, || {
603 makes.set(makes.get() + 1);
604 20
605 });
606 assert_eq!(makes.get(), 2);
607 assert_eq!(cache.slots.len(), 2);
608
609 assert!(cache.remove(&1));
610 assert!(!cache.contains(&1));
611 assert_eq!(cache.len(), 1);
612
613 *cache
615 .get_or_insert_mut(3, || {
616 makes.set(makes.get() + 1);
617 30
618 })
619 .1 = 30;
620 assert_eq!(
621 makes.get(),
622 2,
623 "freed slot should be reused, factory not called"
624 );
625 assert_eq!(cache.slots.len(), 2);
626 assert_eq!(cache.get(&3).copied(), Some(30));
627 assert!(!cache.remove(&999));
628 cache.check_invariants();
629 }
630
631 #[test]
632 fn test_retain() {
633 let mut cache = Clock::new(NZUsize!(4));
634 for i in 0..4u64 {
635 cache.put(i, i * 10);
636 }
637 cache.retain(|k, _| k % 2 == 0);
639 assert_eq!(cache.len(), 2);
640 assert!(cache.contains(&0));
641 assert!(cache.contains(&2));
642 assert!(!cache.contains(&1));
643 assert!(!cache.contains(&3));
644 cache.put(10, 100);
646 cache.put(12, 120);
647 assert_eq!(cache.slots.len(), 4);
648 assert_eq!(cache.len(), 4);
649 cache.check_invariants();
650 }
651
652 #[test]
653 fn test_get_or_insert_mut_reuses_allocations() {
654 let makes = Cell::new(0);
657 let mut cache: Clock<u64, u64> = Clock::new(NZUsize!(3));
658 for k in 0..100u64 {
659 let (_, v) = cache.get_or_insert_mut(k, || {
660 makes.set(makes.get() + 1);
661 0
662 });
663 *v = k; }
665 assert_eq!(makes.get(), 3, "factory should run only during growth");
666 assert_eq!(cache.slots.len(), 3);
667 assert_eq!(cache.len(), 3);
668 cache.check_invariants();
669 }
670
671 #[test]
672 fn test_prefill_allocates_once_and_reuses() {
673 let makes = Cell::new(0);
676 let mut cache: Clock<u64, u64> = Clock::new(NZUsize!(3));
677 cache.prefill(|| {
678 makes.set(makes.get() + 1);
679 0
680 });
681 assert_eq!(makes.get(), 3);
682 assert_eq!(cache.slots.len(), 3);
683 assert!(cache.is_empty());
684 cache.check_invariants();
685
686 for k in 0..100u64 {
688 *cache
689 .get_or_insert_mut(k, || {
690 makes.set(makes.get() + 1);
691 0
692 })
693 .1 = k;
694 }
695 assert_eq!(makes.get(), 3, "prefilled slots must be reused");
696 assert_eq!(cache.slots.len(), 3);
697 assert_eq!(cache.len(), 3);
698 cache.check_invariants();
699 }
700
701 #[test]
702 fn test_clear() {
703 let mut cache = Clock::new(NZUsize!(4));
704 for i in 0..4u64 {
705 cache.put(i, i);
706 }
707 cache.clear();
708 assert!(cache.is_empty());
709 assert_eq!(cache.len(), 0);
710 cache.put(9, 9);
711 assert_eq!(cache.get(&9).copied(), Some(9));
712 cache.check_invariants();
713 }
714
715 #[derive(Clone)]
716 struct Tracked {
717 _counter: Rc<Cell<usize>>,
718 }
719
720 impl Drop for Tracked {
721 fn drop(&mut self) {
722 self._counter.set(self._counter.get() + 1);
723 }
724 }
725
726 #[test]
727 fn test_values_dropped_on_eviction_and_clear() {
728 let drops = Rc::new(Cell::new(0));
729 let mut cache: Clock<u64, Tracked> = Clock::new(NZUsize!(2));
730 for i in 0..2u64 {
731 cache.put(
732 i,
733 Tracked {
734 _counter: drops.clone(),
735 },
736 );
737 }
738 assert_eq!(drops.get(), 0);
739
740 cache.put(
742 2,
743 Tracked {
744 _counter: drops.clone(),
745 },
746 );
747 assert_eq!(drops.get(), 1);
748
749 cache.put(
751 2,
752 Tracked {
753 _counter: drops.clone(),
754 },
755 );
756 assert_eq!(drops.get(), 2);
757
758 cache.clear();
760 assert_eq!(drops.get(), 4);
761 }
762
763 #[test]
764 fn test_remove_retains_value_until_reuse() {
765 let drops = Rc::new(Cell::new(0));
768 let mut cache: Clock<u64, Tracked> = Clock::new(NZUsize!(2));
769 cache.put(
770 1,
771 Tracked {
772 _counter: drops.clone(),
773 },
774 );
775 assert!(cache.remove(&1));
776 assert_eq!(drops.get(), 0, "remove must not drop the value");
777
778 cache.put(
780 2,
781 Tracked {
782 _counter: drops.clone(),
783 },
784 );
785 assert_eq!(drops.get(), 1);
786 }
787
788 #[test]
789 fn test_get_at_validates_key() {
790 let mut cache = Clock::new(NZUsize!(2));
791 let (slot1, v) = cache.get_or_insert_mut(1u64, || 0u64);
792 *v = 10;
793 let (slot2, v) = cache.get_or_insert_mut(2u64, || 0u64);
794 *v = 20;
795
796 assert_eq!(cache.get_at(slot1, &1).copied(), Some(10));
798 assert_eq!(cache.get_at(slot2, &2).copied(), Some(20));
799
800 assert_eq!(cache.get_at(slot1, &2), None);
802 assert_eq!(cache.get_at(cache.capacity(), &1), None);
803 cache.check_invariants();
804 }
805
806 #[test]
807 fn test_get_at_stale_slot_after_eviction() {
808 let mut cache = Clock::new(NZUsize!(1));
811 let (slot, v) = cache.get_or_insert_mut(1u64, || 0u64);
812 *v = 10;
813 let (reused, v) = cache.get_or_insert_mut(3u64, || 0u64);
814 *v = 30;
815 assert_eq!(slot, reused);
816 assert_eq!(cache.get_at(slot, &1), None);
817 assert_eq!(cache.get_at(slot, &3).copied(), Some(30));
818 cache.check_invariants();
819 }
820
821 #[test]
822 fn test_get_at_freed_slot_is_a_miss() {
823 let mut cache = Clock::new(NZUsize!(2));
826 let (slot, v) = cache.get_or_insert_mut(1u64, || 0u64);
827 *v = 10;
828 assert!(cache.remove(&1));
829 assert_eq!(cache.get_at(slot, &1), None);
830
831 let (reused, v) = cache.get_or_insert_mut(2u64, || 0u64);
833 *v = 20;
834 assert_eq!(reused, slot);
835 assert_eq!(cache.get_at(slot, &1), None);
836 assert_eq!(cache.get_at(slot, &2).copied(), Some(20));
837 cache.check_invariants();
838 }
839
840 #[test]
841 fn test_get_at_records_use() {
842 let mut c = Clock::new(NZUsize!(3));
846 c.put(1u64, 10u64);
847 c.put(2, 20);
848 c.put(3, 30);
849 c.put(4, 40); let slot = *c.index.get(&2).unwrap();
852 assert_eq!(c.get_at(slot, &2).copied(), Some(20));
853 c.put(5, 50);
854 assert!(c.contains(&2), "get_at must protect key2 from eviction");
855 assert!(!c.contains(&3));
856 c.check_invariants();
857 }
858
859 #[test]
860 fn test_get_or_insert_mut_slot_stable_on_hit() {
861 let mut cache = Clock::new(NZUsize!(2));
862 let (slot, v) = cache.get_or_insert_mut(1u64, || 0u64);
863 *v = 10;
864 let (hit_slot, v) = cache.get_or_insert_mut(1u64, || unreachable!());
865 assert_eq!(slot, hit_slot);
866 assert_eq!(*v, 10);
867 cache.check_invariants();
868 }
869
870 #[test]
871 fn test_get_mut() {
872 let mut cache = Clock::new(NZUsize!(2));
873 cache.put(1u64, 10u64);
874 assert_eq!(cache.get_mut(&2), None);
875 *cache.get_mut(&1).unwrap() = 11;
876 assert_eq!(cache.get(&1).copied(), Some(11));
877 cache.check_invariants();
878 }
879
880 #[test]
881 fn test_peek_does_not_record_use() {
882 fn setup() -> Clock<u64, u64> {
888 let mut c = Clock::new(NZUsize!(3));
889 c.put(1, 10);
890 c.put(2, 20);
891 c.put(3, 30);
892 c.put(4, 40); c
894 }
895
896 let mut c = setup();
898 assert_eq!(c.peek(&2).copied(), Some(20));
899 c.put(5, 50);
900 assert!(!c.contains(&2), "peek must not protect key2 from eviction");
901 assert!(c.contains(&3));
902
903 let mut c = setup();
905 assert_eq!(c.get(&2).copied(), Some(20));
906 c.put(5, 50);
907 assert!(c.contains(&2), "get must protect key2 from eviction");
908 assert!(!c.contains(&3));
909 }
910
911 #[test]
912 fn test_concurrent_get_is_sound() {
913 let mut cache: Clock<u64, u64> = Clock::new(NZUsize!(64));
916 for i in 0..64u64 {
917 cache.put(i, i * 10);
918 }
919 let cache = &cache;
920 thread::scope(|s| {
921 for _ in 0..4 {
922 s.spawn(move || {
923 for _ in 0..2_000 {
924 for i in 0..64u64 {
925 assert_eq!(cache.get(&i).copied(), Some(i * 10));
926 }
927 }
928 });
929 }
930 });
931 for i in 0..64u64 {
932 assert_eq!(cache.get(&i).copied(), Some(i * 10));
933 }
934 cache.check_invariants();
935 }
936
937 #[derive(Clone, Debug)]
938 enum Op {
939 Get(u8),
940 Peek(u8),
941 Put(u8, u16),
942 GetOrInsert(u8, u16),
943 GetOrInsertMut(u8, u16),
944 GetMut(u8, u16),
945 Remove(u8),
946 Retain(u8),
947 }
948
949 fn op_strategy() -> impl Strategy<Value = Op> {
950 prop_oneof![
951 (0u8..16).prop_map(Op::Get),
952 (0u8..16).prop_map(Op::Peek),
953 (0u8..16, any::<u16>()).prop_map(|(k, v)| Op::Put(k, v)),
954 (0u8..16, any::<u16>()).prop_map(|(k, v)| Op::GetOrInsert(k, v)),
955 (0u8..16, any::<u16>()).prop_map(|(k, v)| Op::GetOrInsertMut(k, v)),
956 (0u8..16, any::<u16>()).prop_map(|(k, v)| Op::GetMut(k, v)),
957 (0u8..16).prop_map(Op::Remove),
958 (0u8..16).prop_map(Op::Retain),
959 ]
960 }
961
962 const KEY_SPACE: u8 = 16;
963
964 proptest! {
965 #[test]
966 fn prop_invariants_hold(
967 cap in 1usize..8,
968 prefill in any::<bool>(),
969 ops in proptest::collection::vec(op_strategy(), 0..256),
970 ) {
971 let mut cache: Clock<u8, u16> = Clock::new(NonZeroUsize::new(cap).unwrap());
972 if prefill {
973 cache.prefill(|| 0u16);
974 }
975 let mut model: HashMap<u8, u16> = HashMap::new();
979 for op in ops {
980 match op {
981 Op::Get(k) => {
982 let got = cache.get(&k).copied();
983 prop_assert_eq!(got, cache.peek(&k).copied());
984 }
985 Op::Peek(k) => {
986 let _ = cache.peek(&k);
987 }
988 Op::Put(k, v) => {
989 cache.put(k, v);
990 model.insert(k, v);
991 prop_assert_eq!(cache.peek(&k).copied(), Some(v));
992 }
993 Op::GetOrInsert(k, v) => {
994 let stored = *cache.get_or_insert_with(k, || v);
995 model.insert(k, stored);
996 prop_assert_eq!(cache.peek(&k).copied(), Some(stored));
997 }
998 Op::GetOrInsertMut(k, v) => {
999 *cache.get_or_insert_mut(k, || v).1 = v;
1000 model.insert(k, v);
1001 prop_assert_eq!(cache.peek(&k).copied(), Some(v));
1002 }
1003 Op::GetMut(k, v) => {
1004 if let Some(slot) = cache.get_mut(&k) {
1005 *slot = v;
1006 model.insert(k, v);
1007 }
1008 }
1009 Op::Remove(k) => {
1010 let had = cache.contains(&k);
1011 prop_assert_eq!(cache.remove(&k), had);
1012 model.remove(&k);
1013 prop_assert!(!cache.contains(&k));
1014 }
1015 Op::Retain(k) => {
1016 cache.retain(|key, _| *key < k);
1017 model.retain(|key, _| *key < k);
1018 prop_assert!(cache.len() <= usize::from(k).min(cap));
1019 }
1020 }
1021 prop_assert!(cache.len() <= cap);
1022 prop_assert!(cache.slots.len() <= cap);
1024 for k in 0..KEY_SPACE {
1027 let present = cache.contains(&k);
1028 prop_assert_eq!(present, cache.peek(&k).is_some());
1029 if present {
1030 prop_assert_eq!(cache.peek(&k).copied(), model.get(&k).copied());
1031 }
1032 }
1033 cache.check_invariants();
1034 }
1035 }
1036 }
1037}