Skip to main content

commonware_utils/
cache.rs

1//! A fixed-capacity key-value cache using [Clock2Q+](https://arxiv.org/abs/2511.21958) replacement.
2//!
3//! Clock2Q+ separates residents into a Small probation queue and a Main CLOCK
4//! ring. New entries enter Small, where hits outside a correlation window earn
5//! promotion to Main. Main gives referenced entries a second chance before
6//! eviction. A bounded Ghost queue records keys evicted from Small. A later
7//! request for one of these keys consumes its history and enters Main directly.
8//! During warm-up, cold entries fill Small first and then Main.
9//!
10//! For capacity `C`, Small is `max(C / 10, 1)` when `C > 1`, Main gets the
11//! remainder, and Ghost is `C / 2`. The newest half of Small, rounded up, forms
12//! the correlation window. Hits inside this window are ignored. Admission age
13//! advances even when entries are removed, so older survivors never become
14//! young again. At capacities 2 through 19, Small holds one always-correlated
15//! slot, so admission to a full Main requires a Ghost hit. Capacity one uses
16//! Main alone.
17//!
18//! # Stable Slots and Hints
19//!
20//! A resident keeps its numeric slot until removal or eviction, including when
21//! it moves from Small to Main. [Cache::get_at] validates both liveness and the
22//! full key. Stale or out-of-bounds hints therefore return a miss, and callers
23//! can fall back to [Cache::get].
24//!
25//! # Value Reuse
26//!
27//! Slots grow lazily to capacity and are then reused in place. [Cache::remove]
28//! and [Cache::retain] keep detached values available for later insertion.
29//! [Cache::get_or_insert_mut] returns the selected slot and its existing value
30//! so buffer values can be overwritten without reallocating. [Cache::prefill]
31//! can allocate every value during construction. Value-returning methods such
32//! as [Cache::put] replace and drop displaced values as usual.
33//!
34//! Resident and Ghost indexes store only slot IDs and read canonical keys from
35//! their respective slots. Each index reserves twice its independent entry
36//! limit, keeping its backing allocation fixed during insertion and eviction.
37//! Tombstone cleanup can still rehash the indexes in place. Keys, values, and
38//! other metadata can allocate separately.
39//!
40//! # Concurrency
41//!
42//! [Cache] performs no internal locking. Shared lookups record hits in an inline
43//! atomic byte without accessing queue topology. Mutating operations require
44//! `&mut self`, so a reader-writer lock can serve hits concurrently:
45//!
46//! ```
47//! use commonware_utils::cache::Cache;
48//! use core::num::NonZeroUsize;
49//! use std::sync::RwLock;
50//!
51//! let cache = RwLock::new(Cache::<u64, u64>::new(NonZeroUsize::new(4).unwrap()));
52//! if cache.read().unwrap().get(&7).is_none() {
53//!     cache.write().unwrap().get_or_insert_with(7, || 7 * 7);
54//! }
55//! assert_eq!(cache.read().unwrap().get(&7).copied(), Some(49));
56//! ```
57
58#[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
67/// Shared hashing state for resident and Ghost indexes.
68type Hasher = ahash::RandomState;
69
70/// Stable identifier for cache storage.
71type Slot = usize;
72
73/// A single cache slot.
74///
75/// A slot is live when its key is present in the index, and free otherwise.
76/// Free slots keep their (now stale) `key` and `value` until the slot is reused,
77/// with `live` cleared so [Cache::get_at] cannot resolve them.
78struct Entry<K, V> {
79    key: K,
80    value: V,
81    state: SlotState,
82    live: bool,
83}
84
85/// A fixed-capacity key-value cache using Clock2Q+ replacement.
86///
87/// Keys must keep the same [Hash] and [Eq] identity throughout each cache
88/// operation, including value factories and other callbacks, and while resident
89/// or retained in Ghost history. Violating this requirement is a logic error
90/// and may cause incorrect lookups or admission decisions.
91pub struct Cache<K, V> {
92    /// Resident membership. Each indexed slot owns its canonical key.
93    /// Twice the resident limit allows tombstone cleanup without growing.
94    index: HashTable<Slot>,
95    /// Shared hashing state for resident and Ghost indexes.
96    hasher: Hasher,
97    /// Stable keys, reusable values, and inline shared-hit state.
98    slots: Vec<Entry<K, V>>,
99    /// Slots detached by remove/retain or prepared by prefill.
100    free: Vec<Slot>,
101    /// Maximum number of residents.
102    capacity: usize,
103    /// Cold queue links and admission ages indexed by stable slot identity.
104    topology: Vec<ResidentSlot>,
105    /// Admission queue and correlation-window state.
106    small: SmallQueue,
107    /// Eviction ring and CLOCK hand.
108    main: MainRing,
109    /// Exact bounded history of keys evicted from Small.
110    ghost: GhostQueue<K>,
111}
112
113impl<K: Hash + Eq, V> Cache<K, V> {
114    /// Creates an empty cache with room for `capacity` residents.
115    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    /// Returns the maximum number of entries the cache can hold.
136    #[inline]
137    pub const fn capacity(&self) -> usize {
138        self.capacity
139    }
140
141    /// Returns the number of entries currently in the cache.
142    #[inline]
143    pub fn len(&self) -> usize {
144        self.index.len()
145    }
146
147    /// Returns `true` if the cache holds no entries.
148    #[inline]
149    pub fn is_empty(&self) -> bool {
150        self.index.is_empty()
151    }
152
153    /// Returns `true` if `key` is in the cache without recording use.
154    #[inline]
155    pub fn contains(&self, key: &K) -> bool {
156        self.find_slot(key).is_some()
157    }
158
159    /// Returns a reference to the value for `key` without recording use.
160    ///
161    /// Unlike [Self::get], this does not affect replacement decisions.
162    #[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    /// Returns a reference to the value for `key`, recording use.
169    ///
170    /// This takes `&self` so it can be called concurrently behind a shared lock.
171    /// Hits update only relaxed atomic replacement metadata.
172    #[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    /// Returns a reference to the value in `slot` if that slot currently holds
181    /// `key` as a live entry, recording use.
182    ///
183    /// This is the read half of an external slot index: callers that recorded a
184    /// key's slot (via [Self::get_or_insert_mut]) can resolve it with a
185    /// key compare instead of a hash lookup. Any stale index entry reads as a
186    /// miss rather than a wrong value: a slot reused for another key fails the
187    /// key compare, a slot freed by [Self::remove] or [Self::retain] is not
188    /// live, and an out-of-range slot does not exist. External indexes
189    /// therefore need no maintenance beyond tolerating misses.
190    #[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    /// Returns a mutable reference to the value for `key`, recording use.
202    #[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    /// Inserts `value` for `key`.
211    ///
212    /// If `key` was already present, replaces and returns the previous value,
213    /// recording use. Otherwise claims vacant capacity or selects a
214    /// resident to evict.
215    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    /// Returns the value for `key`, computing and inserting it with `f` on a
227    /// miss.
228    ///
229    /// On a hit, `f` is not called. On a miss, `f` is called, its result is
230    /// inserted (evicting a resident if necessary), and a reference
231    /// to the stored value is returned.
232    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    /// Returns the value for `key`, computing and inserting it with a fallible
245    /// `f` on a miss.
246    ///
247    /// On a hit, `f` is not called. On a miss, `f` is called; if it returns an
248    /// error the error is propagated and nothing is inserted, so failures are
249    /// not cached.
250    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    /// Returns the slot index and a mutable reference to the slot for `key`,
267    /// reusing an existing allocation where possible.
268    ///
269    /// On a hit, records use and returns the current value. On a miss into a
270    /// reused slot (a freed slot or an eviction victim), returns the reused
271    /// slot's stale value for the caller to overwrite. Only when the cache
272    /// grows is `make` called to produce a fresh value. This lets callers whose
273    /// values are buffers overwrite them in place without allocating on every
274    /// insert.
275    ///
276    /// The slot index identifies the entry until it is evicted or removed, so
277    /// callers can record it in an external index and resolve later reads with
278    /// [Self::get_at] instead of a hash lookup.
279    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    /// Removes `key`, returning whether it was present.
292    ///
293    /// The slot and its allocation are retained for reuse, so the value is not
294    /// returned.
295    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    /// Retains only the entries for which `keep` returns `true`.
316    ///
317    /// Dropped entries' slots and allocations are retained for reuse.
318    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    /// Removes all entries, dropping their values and retaining the allocated
345    /// capacity of the indexes and slot vectors.
346    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    /// Publishes a slot after its canonical key is installed. All indexed slots
369    /// retain their keys while insertion may rehash the table.
370    #[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    /// Installs an absent key, replacing a supplied value or reusing storage.
380    /// `make` supplies a value only when a reusable value is unavailable.
381    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        // Remove the resident index while its canonical victim key is still
388        // installed. The same hash accompanies a Small key into Ghost.
389        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        // Bookkeeping is complete before destructors can run. Small victims
432        // transfer their owned key. Main and free-slot keys have no history.
433        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    /// Chooses the resident partition for an incoming key.
447    #[inline]
448    const fn admission(&self, ghost_hit: bool, has_vacancy: bool) -> Admission {
449        // A Ghost hit bypasses Small. During warm-up, Small fills to its target
450        // before additional vacant cache slots are assigned to Main.
451        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    /// Plans the resident transition for one confirmed-miss insertion.
462    #[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                // A full cache and bounded partitions imply Main is also full.
473                // Promote the referenced Small tail while the incoming entry
474                // reuses the Main victim selected here.
475                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                // Main can grow only while the cache has unused capacity.
487                assert!(has_vacancy);
488                InsertionPlan::Vacant
489            }
490        }
491    }
492
493    /// Detaches a resident and returns its previous partition.
494    #[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    /// Pre-allocates all slots up to capacity, each holding a value from `make`,
508    /// and leaves them free for reuse.
509    ///
510    /// After this call, the first `capacity` inserts reuse a pre-allocated slot
511    /// instead of growing, so `make` (and any allocation it performs) runs only
512    /// here. Use this to front-load value allocation at construction so
513    /// steady-state inserts never allocate values. Free slots are seeded with the
514    /// default key as a throwaway placeholder that is overwritten when the slot
515    /// is first filled.
516    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        // The free list pops from the back, so reverse the new entries to hand
530        // them out in ascending slot order (matching how growth assigns slots).
531        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
550/// Sentinel used when a cache-owned slot has no neighbor.
551const UNLINKED: usize = usize::MAX;
552/// Low bits available for the modular Small admission generation.
553const ADMISSION_GENERATION_MASK: usize = usize::MAX >> 2;
554/// Bit offset of the resident location tag.
555const LOCATION_SHIFT: u32 = usize::BITS - 2;
556/// Slot-state bit set after an eligible resident hit.
557const REFERENCED: u8 = 1 << 0;
558/// Slot-state bit set while a Small resident is in the correlation window.
559const CORRELATED: u8 = 1 << 1;
560
561/// Converts the internal link sentinel into an optional slot.
562#[inline]
563const fn linked(slot: usize) -> Option<usize> {
564    if slot == UNLINKED { None } else { Some(slot) }
565}
566
567/// Where a resident slot participates in the replacement policy.
568#[derive(Clone, Copy, Debug, Eq, PartialEq)]
569enum Location {
570    /// The cache slot is not attached to either resident partition.
571    Free,
572    /// An entry in the Small queue.
573    Small,
574    /// An entry in the Main CLOCK ring.
575    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/// Cold topology for a stable cache slot.
590#[derive(Clone, Copy)]
591struct ResidentSlot {
592    /// Previous entry toward the head of Small, or in Main's circular ring.
593    prev: usize,
594    /// Next entry toward the tail of Small, or in Main's circular ring.
595    next: usize,
596    /// Location tag in the high bits and Small admission generation in the low bits.
597    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/// Resident partition selected for an incoming key.
632#[derive(Clone, Copy, Debug, Eq, PartialEq)]
633enum Admission {
634    /// Admit at the head of the Small queue.
635    Small,
636    /// Admit immediately before the hand in the Main CLOCK ring.
637    Main,
638}
639
640/// Storage action selected for one confirmed-miss insertion.
641#[derive(Clone, Copy)]
642enum InsertionPlan {
643    /// Use unused cache capacity.
644    Vacant,
645    /// Replace one resident.
646    Evict {
647        /// Resident selected for eviction.
648        victim: Slot,
649    },
650    /// Promote a Small resident and replace a separate Main resident.
651    PromoteThenEvict {
652        /// Small tail retained and moved to Main.
653        promoted: Slot,
654        /// Main resident selected for eviction.
655        victim: Slot,
656    },
657}
658
659impl InsertionPlan {
660    /// Returns the resident selected for replacement, if any.
661    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/// Replacement state stored inline with each cache slot.
670///
671/// The correlation marker and reference bit share one atomic byte, so a shared
672/// hit needs no access to cache-owned queue topology. Relaxed ordering is
673/// sufficient because these bits affect only replacement decisions and do not
674/// publish resident keys or values.
675#[repr(transparent)]
676#[derive(Default)]
677struct SlotState(AtomicU8);
678
679impl SlotState {
680    /// Returns the initial state for a resident entering `admission`.
681    #[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    /// Records a hit through a shared cache reference.
691    ///
692    /// Hits inside the correlation window are ignored. The conditional store
693    /// also avoids dirtying the cache line again after the bit is already set.
694    #[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    /// Records a hit while the cache is already exclusively borrowed.
703    #[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    /// Returns whether a Small resident has earned promotion.
712    #[inline]
713    fn earned_promotion(&self) -> bool {
714        self.0.load(Ordering::Relaxed) & (CORRELATED | REFERENCED) == REFERENCED
715    }
716
717    /// Consumes one Main reference bit.
718    #[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    /// Clears correlation and reference state during an exclusive transition.
728    #[inline]
729    fn reset(&self) {
730        self.0.store(0, Ordering::Relaxed);
731    }
732}
733
734/// State and endpoints for the Small queue.
735struct SmallQueue {
736    /// Maximum number of Small residents.
737    capacity: usize,
738    /// Admission age at which a resident leaves the correlation window.
739    correlation_window: usize,
740    /// Newest Small resident.
741    head: Option<Slot>,
742    /// Oldest Small resident.
743    tail: Option<Slot>,
744    /// Oldest resident still inside the correlation window.
745    young_tail: Option<Slot>,
746    /// Modular count of Small admissions, including removed entries.
747    admissions: usize,
748    /// Current number of Small residents.
749    len: usize,
750}
751
752impl SmallQueue {
753    /// Constructs an empty Small queue with its derived correlation window.
754    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    /// Attaches a free stable slot at the queue head.
767    #[inline]
768    fn push<K, V>(&mut self, entries: &[Entry<K, V>], slots: &mut [ResidentSlot], slot: Slot) {
769        // Admission count, rather than live queue length, preserves correlation
770        // age across explicit removal and retention. A correlated resident leaves
771        // within one bounded window, long before the generation counter wraps.
772        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        // Only the oldest correlated survivor can leave the window after one
790        // new admission because Small preserves admission order.
791        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        // Discard references made inside the correlation window. Only a hit
802        // after the entry becomes old should promote it to Main.
803        entries[demoted].state.reset();
804        slots[demoted].location_and_admission = Location::Small.pack(0);
805        self.young_tail = Some(new_boundary);
806    }
807
808    /// Detaches a resident and repairs the queue and correlation boundary.
809    #[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        // Removing the correlation boundary shrinks the live window without
828        // making any older resident young again.
829        if self.young_tail == Some(slot) {
830            self.young_tail = linked(prev);
831        }
832        slots[slot] = ResidentSlot::default();
833    }
834
835    /// Resets all Small endpoints and counters.
836    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
845/// Circular resident topology and replacement hand for Main.
846///
847/// Unlike the Small and Ghost queues, Main has no head or tail. Its entries
848/// form a ring, and the CLOCK hand sweeps that ring for an unreferenced victim.
849struct MainRing {
850    /// Maximum number of Main residents.
851    capacity: usize,
852    /// Next resident considered for eviction.
853    hand: Option<Slot>,
854    /// Current number of Main residents.
855    len: usize,
856}
857
858impl MainRing {
859    /// Constructs an empty Main ring.
860    const fn new(capacity: usize) -> Self {
861        Self {
862            capacity,
863            hand: None,
864            len: 0,
865        }
866    }
867
868    /// Sweeps the CLOCK hand to the first unreferenced resident.
869    #[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                // A set bit grants one second chance. Continue from the next
875                // resident after consuming it.
876                self.hand = Some(slots[slot].next);
877                continue;
878            }
879            return slot;
880        }
881    }
882
883    /// Attaches a free slot immediately before the hand.
884    #[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            // A one-entry ring links the resident to itself and points the hand
889            // at that sole eviction candidate.
890            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        // New residents start behind the current hand, so they are considered
901        // only after the existing CLOCK sweep reaches them.
902        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    /// Detaches a resident and advances the hand when necessary.
917    #[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    /// Resets the Main hand and resident count.
940    const fn clear(&mut self) {
941        self.hand = None;
942        self.len = 0;
943    }
944}
945
946/// A key-only entry in the bounded Ghost queue.
947struct GhostSlot<K> {
948    /// Historical key, or `None` when this slot is available for reuse.
949    key: Option<K>,
950    /// Previous entry toward the Ghost head, or `UNLINKED` when free.
951    prev: usize,
952    /// Next entry toward the Ghost tail, or the next free slot when `key` is `None`.
953    next: usize,
954}
955
956/// Exact bounded history of keys recently evicted from Small.
957///
958/// Ghost stores keys without values, so it records recent eviction history
959/// without consuming resident cache capacity. A later request for a recorded
960/// key demonstrates reuse and admits the key directly to Main. The hash index
961/// provides exact membership checks, while the linked queue discards the oldest
962/// history when it reaches its bound.
963struct GhostQueue<K> {
964    /// Exact key membership represented by queue positions.
965    ///
966    /// Twice the history bound keeps live entries within half the usable
967    /// capacity, where hashbrown reclaims tombstones in place without growing.
968    /// Both indexes use the cache's single randomized hashing state.
969    index: HashTable<usize>,
970    /// Storage for linked historical entries.
971    slots: Vec<GhostSlot<K>>,
972    /// First detached position, linked through [GhostSlot::next].
973    free_head: Option<usize>,
974    /// Newest historical entry.
975    head: Option<usize>,
976    /// Oldest historical entry.
977    tail: Option<usize>,
978    /// Maximum number of historical entries.
979    capacity: usize,
980}
981
982impl<K: Hash + Eq> GhostQueue<K> {
983    /// Constructs empty exact history bounded by `capacity`.
984    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    /// Adds an evicted Small key at the queue head.
996    #[inline]
997    fn push(&mut self, key: K, hash: u64, hasher: &Hasher) {
998        if self.capacity == 0 {
999            return;
1000        }
1001
1002        // Prefer recycled storage, then grow to the Ghost bound, then reuse
1003        // the oldest live slot after removing its previous key.
1004        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            // The table's slot index is valid only while this canonical key is
1020            // live. Remove the index entry before unlinking and dropping it.
1021            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        // Resident and Ghost keys are disjoint. Every indexed slot owns a live
1051        // key, including while insertion rehashes the table.
1052        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    /// Removes exact history for `key` and reports whether it was present.
1065    #[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        // Recycle the position before dropping the key, so a panicking
1081        // destructor cannot strand the detached slot outside the free list.
1082        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    /// Detaches one historical entry and returns its owned key.
1090    #[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    /// Removes all historical entries and reusable positions.
1115    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        // A hit does not call the factory.
1187        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        // A removed entry frees its slot for reuse without growing the slot
1209        // vector or calling the factory again.
1210        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        // Reusing the freed slot does not call the factory or grow.
1228        *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        // Keep even keys.
1249        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        // Freed slots are reused for new inserts.
1257        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        // The factory runs at most `capacity` times no matter how many distinct
1267        // keys churn through the cache, proving evicted slots are reused.
1268        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; // Overwrite the possibly stale reused value.
1276        }
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        // Prefill runs the factory exactly `capacity` times. Subsequent inserts
1286        // reuse pre-allocated slots without growing or calling the factory.
1287        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        // Churn many keys without further factory calls or slot growth.
1299        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            // Successful prefill yields new slots in creation order. An interrupted
1337            // prefill leaves completed slots on top of the existing free stack.
1338            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        // Inserting a third entry evicts one and drops its value.
1409        cache.put(2, Tracked(drops.clone()));
1410        assert_eq!(drops.get(), 1);
1411
1412        // Replacing an existing key drops the old value.
1413        cache.put(2, Tracked(drops.clone()));
1414        assert_eq!(drops.get(), 2);
1415
1416        // Clearing drops the remaining two values.
1417        cache.clear();
1418        assert_eq!(drops.get(), 4);
1419    }
1420
1421    #[test]
1422    fn test_remove_retains_value_until_reuse() {
1423        // remove does not drop the value. The freed slot keeps it until a
1424        // value-inserting method reuses the slot.
1425        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        // Reusing the freed slot through a value insert drops the retained value.
1432        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        // A recorded slot resolves with a key comparison and no hash lookup.
1445        assert_eq!(cache.get_at(first, &1).copied(), Some(10));
1446        assert_eq!(cache.get_at(second, &2).copied(), Some(20));
1447
1448        // The wrong key for a slot and an out-of-range slot are both misses.
1449        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        // Evicting key 1 reuses its slot for key 3. The stale hint fails the key
1457        // comparison while the new key resolves at the same slot.
1458        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        // remove keeps the slot's stale key for allocation reuse, but get_at
1473        // must reject a freed slot without requiring external index cleanup.
1474        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        // Reusing the slot for another key resolves the new key only.
1481        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        // Oracle: last value written for each live key. A key the cache
1543        // reports as present must hold its last-written value (no stale or
1544        // conjured values). An evicted key is simply absent.
1545        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            // The slot vector never exceeds capacity, proving reuse.
1596            prop_assert!(cache.slots.len() <= capacity);
1597            // Every present key holds its last-written value and was logically
1598            // inserted. Absent keys are an allowed (evicted) state.
1599            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        // Small needs at least two slots to exercise demotion, promotion, and
1623        // correlation-boundary repair.
1624        #[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        /// Returns the inline replacement state for a resident slot.
1651        fn bits(&self, slot: Slot) -> u8 {
1652            self.slots[slot].state.0.load(Ordering::Relaxed)
1653        }
1654
1655        /// Returns Small slot order from newest to oldest.
1656        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        /// Returns Main slot order starting at the CLOCK hand.
1667        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        /// Returns Ghost slot order from newest to oldest.
1684        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        /// Returns Small keys from newest to oldest.
1695        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        /// Returns Main keys starting at the CLOCK hand.
1706        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        /// Returns Ghost keys from newest to oldest.
1717        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        /// Asserts resident ownership, queue topology, and exact history agree.
1734        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        // Ratio-derived partitions round down and the correlation window rounds
1902        // up. Capacities above one still keep at least one Small slot, while
1903        // capacity one uses Main alone.
1904        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        // Exercise every tiny partition through repeated replacement, where
1922        // off-by-one errors in the queue bounds are easiest to expose.
1923        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        // With no Small or Ghost capacity, the only Main slot is replaced on
1935        // each cold insertion.
1936        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        // Warm-up fills the 10% Small partition first, then uses the remaining
1949        // vacant slots for unreferenced Main residents.
1950        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        // Resident filtering does not erase unrelated Ghost evidence. The
1967        // cache may therefore retain useful history with no live residents.
1968        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        // Key 4 is at the Small head, inside the two-entry correlation window.
1988        // Its hit must not set the bit, so a scan ages and evicts it to Ghost.
1989        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        // Key 1 is already outside the window. Its hit marks it for promotion
2006        // to Main when the next insertion examines the Small tail.
2007        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        // Key 0 has left the correlation window, so this hit makes it eligible
2029        // for promotion when it reaches the Small tail.
2030        assert_eq!(cache.get(&0), Some(&0));
2031
2032        // Removing the younger key must not move key 0 back into the
2033        // correlation window or discard its eligible reference.
2034        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        // Removing key 3 must not erase its admission from key 2's age. Key 40
2052        // is the second later Small admission, so key 2 leaves the window.
2053        assert!(cache.remove(&3));
2054        cache.put(40, 40);
2055        assert_eq!(cache.get(&2), Some(&2));
2056
2057        // Advance key 2 to the Small tail. Its eligible hit must promote it
2058        // into Main instead of letting the final insertion evict it to Ghost.
2059        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        // Cold churn fills the ten-entry Ghost queue and drops its oldest key.
2070        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        // Key 1 is still in Ghost, so reuse consumes its history and admits it
2085        // directly to Main without disturbing Small.
2086        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        // Key 0 has already aged out of Ghost and therefore enters Small as a
2095        // cold miss, evicting Small's tail into Ghost.
2096        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        // Removing the Small resident leaves a globally vacant slot while Main
2106        // remains full and key 1 remains in Ghost.
2107        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        // Ghost reuse is constrained to Main, so it replaces Main key 2 and
2117        // deliberately leaves the Small vacancy unused.
2118        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        // Reference the original Small tail so the next cold miss promotes it
2128        // while keeping both fixed-size resident partitions full.
2129        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        // The sweep evicted key 18 and left the hand on key 19. The promoted
2143        // key links immediately before the hand, so it is swept last.
2144        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        // Repeat the transition with a full Main CLOCK. The promoted key keeps
2153        // its slot and starts in Main with a clear reference bit.
2154        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        // Key 19 was unreferenced at the hand, so it is evicted and the hand
2160        // moves to key 2. Key 1 links before the hand, after key 0.
2161        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        // Promotion changes only resident topology, so key 1 keeps its original
2176        // slot and the existing lookup hint remains valid.
2177        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        // An actual eviction reuses key 2's slot for key 42. The old hint then
2191        // fails full-key validation while the new key resolves in that slot.
2192        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        // Warm-up places keys 1 through 9 in Main. A one-cache-capacity scan is
2206        // absorbed by Small and leaves that Main working set intact.
2207        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        // Prefill allocates every value once. Replacement churn must keep reusing
2224        // those same stable slots without invoking the factory again.
2225        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        // Exercise removal from Main and Ghost, then prove a removed Ghost key
2245        // returns as cold instead of receiving stale Main admission.
2246        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        // Replace the vacancy left by the resident removal. With a full cache,
2259        // the forgotten Ghost key must now enter Small as a cold admission.
2260        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        // Retain detaches rejected residents but preserves unrelated Ghost
2266        // history. Clear resets both resident topology and Ghost history.
2267        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        // Shared lookups race only on the relaxed per-slot reference bit. The
2288        // accumulated hit must still protect key 1 on the next insertion.
2289        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        // Key 1 is in Ghost. A failed factory must return before cache
2315        // insertion consumes that evidence or changes resident topology.
2316        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        // Correlation ages are modular differences, so the packed generation can
2334        // wrap inside the window and leave the same Small state as a fresh run.
2335        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        // Key 1 left the window across the first wrap. Repeatedly remove both
2351        // young residents and cross the wrap with replacements. The older
2352        // survivor remains eligible because young_tail, not generation
2353        // equality, owns youth.
2354        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            // Exercise the insertion callback through multiple table growths.
2407            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            // GhostKey is not Clone, and each live historical key owns one Rc.
2465            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        // Removing both reservations forces both indexes to call their rehash
2497        // callbacks while the public Cache API drives all replacement transitions.
2498        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        // Constant hashes still consume only the exact requested history key.
2521        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        // The reserved resident and Ghost tables cannot rehash during this short
2608        // sequence, so every count belongs to the key's explicit operation path.
2609        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        // Key 3 replaces Small's key 1. Each incoming key and the transferred
2615        // victim is hashed once across its resident and Ghost operations.
2616        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        // A nonresident removal shares the resident-miss hash with Ghost discard.
2622        assert!(!cache.remove(&one));
2623        assert_eq!(one.hashes(), 3);
2624
2625        // Resident retain and clear operate from canonical table/slot ownership
2626        // and do not need a new key hash.
2627        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                // Zero reserve forces actual rehash callbacks during lazy growth and
2653                // publication into prefilled storage, even with constant hashes.
2654                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        // Key 0 is historical: its admission must consume Ghost and evict Main's 1.
2809        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    // Equality ignores metadata so an incoming key can differ from Ghost's owner.
2850    #[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        // The historical key is consumed while the caller's key becomes canonical.
2878        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        // A full Ghost replaces its oldest history before dropping that old key.
2937        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        // Discard returns the historical slot to its free list before dropping.
2945        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        // A Ghost hit replaces Main's canonical key with the caller's owned key.
2954        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        // A freed resident retains its stale key until the next published admission.
2962        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}