Coverage Report

Created: 2026-03-31 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/quick_cache-0.6.18/src/unsync.rs
Line
Count
Source
1
use crate::{
2
    linked_slab::Token,
3
    options::*,
4
    shard::{self, CacheShard, InsertStrategy},
5
    DefaultHashBuilder, Equivalent, Lifecycle, MemoryUsed, UnitWeighter, Weighter,
6
};
7
use std::hash::{BuildHasher, Hash};
8
9
/// A non-concurrent cache.
10
#[derive(Clone)]
11
pub struct Cache<
12
    Key,
13
    Val,
14
    We = UnitWeighter,
15
    B = DefaultHashBuilder,
16
    L = DefaultLifecycle<Key, Val>,
17
> {
18
    shard: CacheShard<Key, Val, We, B, L, SharedPlaceholder>,
19
}
20
21
impl<Key: Eq + Hash, Val> Cache<Key, Val> {
22
    /// Creates a new cache with holds up to `items_capacity` items (approximately).
23
0
    pub fn new(items_capacity: usize) -> Self {
24
0
        Self::with(
25
0
            items_capacity,
26
0
            items_capacity as u64,
27
0
            Default::default(),
28
0
            Default::default(),
29
0
            Default::default(),
30
        )
31
0
    }
32
}
33
34
impl<Key: Eq + Hash, Val, We: Weighter<Key, Val>> Cache<Key, Val, We> {
35
0
    pub fn with_weighter(
36
0
        estimated_items_capacity: usize,
37
0
        weight_capacity: u64,
38
0
        weighter: We,
39
0
    ) -> Self {
40
0
        Self::with(
41
0
            estimated_items_capacity,
42
0
            weight_capacity,
43
0
            weighter,
44
0
            Default::default(),
45
0
            Default::default(),
46
        )
47
0
    }
48
}
49
50
impl<Key: Eq + Hash, Val, We: Weighter<Key, Val>, B: BuildHasher, L: Lifecycle<Key, Val>>
51
    Cache<Key, Val, We, B, L>
52
{
53
    /// Creates a new cache that can hold up to `weight_capacity` in weight.
54
    /// `estimated_items_capacity` is the estimated number of items the cache is expected to hold,
55
    /// roughly equivalent to `weight_capacity / average item weight`.
56
0
    pub fn with(
57
0
        estimated_items_capacity: usize,
58
0
        weight_capacity: u64,
59
0
        weighter: We,
60
0
        hash_builder: B,
61
0
        lifecycle: L,
62
0
    ) -> Self {
63
0
        Self::with_options(
64
0
            OptionsBuilder::new()
65
0
                .estimated_items_capacity(estimated_items_capacity)
66
0
                .weight_capacity(weight_capacity)
67
0
                .build()
68
0
                .unwrap(),
69
0
            weighter,
70
0
            hash_builder,
71
0
            lifecycle,
72
        )
73
0
    }
74
75
    /// Constructs a cache based on [OptionsBuilder].
76
    ///
77
    /// # Example
78
    ///
79
    /// ```rust
80
    /// use quick_cache::{unsync::{Cache, DefaultLifecycle}, OptionsBuilder, UnitWeighter, DefaultHashBuilder};
81
    ///
82
    /// Cache::<(String, u64), String>::with_options(
83
    ///   OptionsBuilder::new()
84
    ///     .estimated_items_capacity(10000)
85
    ///     .weight_capacity(10000)
86
    ///     .build()
87
    ///     .unwrap(),
88
    ///     UnitWeighter,
89
    ///     DefaultHashBuilder::default(),
90
    ///     DefaultLifecycle::default(),
91
    /// );
92
    /// ```
93
0
    pub fn with_options(options: Options, weighter: We, hash_builder: B, lifecycle: L) -> Self {
94
0
        let shard = CacheShard::new(
95
0
            options.hot_allocation,
96
0
            options.ghost_allocation,
97
0
            options.estimated_items_capacity,
98
0
            options.weight_capacity,
99
0
            weighter,
100
0
            hash_builder,
101
0
            lifecycle,
102
        );
103
0
        Self { shard }
104
0
    }
105
106
    /// Returns whether the cache is empty.
107
0
    pub fn is_empty(&self) -> bool {
108
0
        self.shard.len() == 0
109
0
    }
110
111
    /// Returns the number of cached items
112
0
    pub fn len(&self) -> usize {
113
0
        self.shard.len()
114
0
    }
115
116
    /// Returns the total weight of cached items
117
0
    pub fn weight(&self) -> u64 {
118
0
        self.shard.weight()
119
0
    }
120
121
    /// Returns the maximum weight of cached items
122
0
    pub fn capacity(&self) -> u64 {
123
0
        self.shard.capacity()
124
0
    }
125
126
    /// Returns the number of misses
127
    #[cfg(feature = "stats")]
128
    pub fn misses(&self) -> u64 {
129
        self.shard.misses()
130
    }
131
132
    /// Returns the number of hits
133
    #[cfg(feature = "stats")]
134
    pub fn hits(&self) -> u64 {
135
        self.shard.hits()
136
    }
137
138
    /// Reserver additional space for `additional` entries.
139
    /// Note that this is counted in entries, and is not weighted.
140
0
    pub fn reserve(&mut self, additional: usize) {
141
0
        self.shard.reserve(additional);
142
0
    }
143
144
    /// Check if a key exist in the cache.
145
0
    pub fn contains_key<Q>(&self, key: &Q) -> bool
146
0
    where
147
0
        Q: Hash + Equivalent<Key> + ?Sized,
148
    {
149
0
        self.shard.contains(self.shard.hash(key), key)
150
0
    }
151
152
    /// Fetches an item from the cache.
153
0
    pub fn get<Q>(&self, key: &Q) -> Option<&Val>
154
0
    where
155
0
        Q: Hash + Equivalent<Key> + ?Sized,
156
    {
157
0
        self.shard.get(self.shard.hash(key), key)
158
0
    }
159
160
    /// Fetches an item from the cache.
161
    ///
162
    /// Note: Leaking the returned RefMut might cause cache weight tracking to be inaccurate.
163
0
    pub fn get_mut<Q>(&mut self, key: &Q) -> Option<RefMut<'_, Key, Val, We, B, L>>
164
0
    where
165
0
        Q: Hash + Equivalent<Key> + ?Sized,
166
    {
167
0
        self.shard.get_mut(self.shard.hash(key), key).map(RefMut)
168
0
    }
169
170
    /// Peeks an item from the cache. Contrary to gets, peeks don't alter the key "hotness".
171
0
    pub fn peek<Q>(&self, key: &Q) -> Option<&Val>
172
0
    where
173
0
        Q: Hash + Equivalent<Key> + ?Sized,
174
    {
175
0
        self.shard.peek(self.shard.hash(key), key)
176
0
    }
177
178
    /// Peeks an item from the cache. Contrary to gets, peeks don't alter the key "hotness".
179
    ///
180
    /// Note: Leaking the returned RefMut might cause cache weight tracking to be inaccurate.
181
0
    pub fn peek_mut<Q>(&mut self, key: &Q) -> Option<RefMut<'_, Key, Val, We, B, L>>
182
0
    where
183
0
        Q: Hash + Equivalent<Key> + ?Sized,
184
    {
185
0
        self.shard.peek_mut(self.shard.hash(key), key).map(RefMut)
186
0
    }
187
188
    /// Remove an item from the cache whose key is `key`.
189
    /// Returns the removed entry, if any.
190
0
    pub fn remove<Q>(&mut self, key: &Q) -> Option<(Key, Val)>
191
0
    where
192
0
        Q: Hash + Equivalent<Key> + ?Sized,
193
    {
194
0
        self.shard.remove(self.shard.hash(key), key)
195
0
    }
196
197
    /// Remove an item from the cache whose key is `key` if `f(&value)` returns `true` for that entry.
198
    /// Compared to peek and remove, this method is more efficient as it requires only 1 lookup.
199
    ///
200
    /// Returns the removed entry, if any.
201
0
    pub fn remove_if<Q, F>(&mut self, key: &Q, f: F) -> Option<(Key, Val)>
202
0
    where
203
0
        Q: Hash + Equivalent<Key> + ?Sized,
204
0
        F: FnOnce(&Val) -> bool,
205
    {
206
0
        self.shard.remove_if(self.shard.hash(key), key, f)
207
0
    }
208
209
    /// Replaces an item in the cache, but only if it already exists.
210
    /// If `soft` is set, the replace operation won't affect the "hotness" of the key,
211
    /// even if the value is replaced.
212
    ///
213
    /// Returns `Ok` if the entry was admitted and `Err(_)` if it wasn't.
214
0
    pub fn replace(&mut self, key: Key, value: Val, soft: bool) -> Result<(), (Key, Val)> {
215
0
        let lcs = self.replace_with_lifecycle(key, value, soft)?;
216
0
        self.shard.lifecycle.end_request(lcs);
217
0
        Ok(())
218
0
    }
219
220
    /// Replaces an item in the cache, but only if it already exists.
221
    /// If `soft` is set, the replace operation won't affect the "hotness" of the key,
222
    /// even if the value is replaced.
223
    ///
224
    /// Returns `Ok` if the entry was admitted and `Err(_)` if it wasn't.
225
0
    pub fn replace_with_lifecycle(
226
0
        &mut self,
227
0
        key: Key,
228
0
        value: Val,
229
0
        soft: bool,
230
0
    ) -> Result<L::RequestState, (Key, Val)> {
231
0
        let mut lcs = self.shard.lifecycle.begin_request();
232
0
        self.shard.insert(
233
0
            &mut lcs,
234
0
            self.shard.hash(&key),
235
0
            key,
236
0
            value,
237
0
            InsertStrategy::Replace { soft },
238
0
        )?;
239
0
        Ok(lcs)
240
0
    }
241
242
    /// Retains only the items specified by the predicate.
243
    /// In other words, remove all items for which `f(&key, &value)` returns `false`. The
244
    /// elements are visited in arbitrary order.
245
0
    pub fn retain<F>(&mut self, f: F)
246
0
    where
247
0
        F: Fn(&Key, &Val) -> bool,
248
    {
249
0
        self.shard.retain(f);
250
0
    }
251
252
    /// Gets or inserts an item in the cache with key `key`.
253
    /// Returns a reference to the inserted `value` if it was admitted to the cache.
254
    ///
255
    /// See also `get_ref_or_guard`.
256
0
    pub fn get_or_insert_with<Q, E>(
257
0
        &mut self,
258
0
        key: &Q,
259
0
        with: impl FnOnce() -> Result<Val, E>,
260
0
    ) -> Result<Option<&Val>, E>
261
0
    where
262
0
        Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
263
    {
264
0
        let idx = match self.shard.upsert_placeholder(self.shard.hash(key), key) {
265
0
            Ok((idx, _)) => idx,
266
0
            Err((plh, _)) => {
267
0
                let v = with()?;
268
0
                let mut lcs = self.shard.lifecycle.begin_request();
269
0
                let replaced = self.shard.replace_placeholder(&mut lcs, &plh, false, v);
270
0
                self.shard.lifecycle.end_request(lcs);
271
0
                debug_assert!(replaced.is_ok(), "unsync replace_placeholder can't fail");
272
0
                plh.idx
273
            }
274
        };
275
0
        Ok(self.shard.peek_token(idx))
276
0
    }
277
278
    /// Gets or inserts an item in the cache with key `key`.
279
    /// Returns a mutable reference to the inserted `value` if it was admitted to the cache.
280
    ///
281
    /// See also `get_mut_or_guard`.
282
0
    pub fn get_mut_or_insert_with<'a, Q, E>(
283
0
        &'a mut self,
284
0
        key: &Q,
285
0
        with: impl FnOnce() -> Result<Val, E>,
286
0
    ) -> Result<Option<RefMut<'a, Key, Val, We, B, L>>, E>
287
0
    where
288
0
        Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
289
    {
290
0
        let idx = match self.shard.upsert_placeholder(self.shard.hash(key), key) {
291
0
            Ok((idx, _)) => idx,
292
0
            Err((plh, _)) => {
293
0
                let v = with()?;
294
0
                let mut lcs = self.shard.lifecycle.begin_request();
295
0
                let replaced = self.shard.replace_placeholder(&mut lcs, &plh, false, v);
296
0
                debug_assert!(replaced.is_ok(), "unsync replace_placeholder can't fail");
297
0
                self.shard.lifecycle.end_request(lcs);
298
0
                plh.idx
299
            }
300
        };
301
0
        Ok(self.shard.peek_token_mut(idx).map(RefMut))
302
0
    }
303
304
    /// Gets an item from the cache with key `key` .
305
    /// If the corresponding value isn't present in the cache, this functions returns a guard
306
    /// that can be used to insert the value once it's computed.
307
0
    pub fn get_ref_or_guard<Q>(&mut self, key: &Q) -> Result<&Val, Guard<'_, Key, Val, We, B, L>>
308
0
    where
309
0
        Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
310
    {
311
        // TODO: this could be using a simpler entry API
312
0
        match self.shard.upsert_placeholder(self.shard.hash(key), key) {
313
0
            Ok((_, v)) => unsafe {
314
                // Rustc gets insanely confused about returning from mut borrows
315
                // Safety: v has the same lifetime as self
316
0
                let v: *const Val = v;
317
0
                Ok(&*v)
318
            },
319
0
            Err((placeholder, _)) => Err(Guard {
320
0
                cache: self,
321
0
                placeholder,
322
0
                inserted: false,
323
0
            }),
324
        }
325
0
    }
326
327
    /// Gets an item from the cache with key `key` .
328
    /// If the corresponding value isn't present in the cache, this functions returns a guard
329
    /// that can be used to insert the value once it's computed.
330
    ///
331
    /// Note: Leaking the returned RefMut might cause cache weight tracking to be inaccurate.
332
0
    pub fn get_mut_or_guard<'a, Q>(
333
0
        &'a mut self,
334
0
        key: &Q,
335
0
    ) -> Result<Option<RefMut<'a, Key, Val, We, B, L>>, Guard<'a, Key, Val, We, B, L>>
336
0
    where
337
0
        Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
338
    {
339
        // TODO: this could be using a simpler entry API
340
0
        match self.shard.upsert_placeholder(self.shard.hash(key), key) {
341
0
            Ok((idx, _)) => Ok(self.shard.peek_token_mut(idx).map(RefMut)),
342
0
            Err((placeholder, _)) => Err(Guard {
343
0
                cache: self,
344
0
                placeholder,
345
0
                inserted: false,
346
0
            }),
347
        }
348
0
    }
349
350
    /// Inserts an item in the cache with key `key`.
351
0
    pub fn insert(&mut self, key: Key, value: Val) {
352
0
        let lcs = self.insert_with_lifecycle(key, value);
353
0
        self.shard.lifecycle.end_request(lcs);
354
0
    }
355
356
    /// Inserts an item in the cache with key `key`.
357
0
    pub fn insert_with_lifecycle(&mut self, key: Key, value: Val) -> L::RequestState {
358
0
        let mut lcs = self.shard.lifecycle.begin_request();
359
0
        let result = self.shard.insert(
360
0
            &mut lcs,
361
0
            self.shard.hash(&key),
362
0
            key,
363
0
            value,
364
0
            InsertStrategy::Insert,
365
        );
366
        // result cannot err with the Insert strategy
367
0
        debug_assert!(result.is_ok());
368
0
        lcs
369
0
    }
370
371
    /// Clear all items from the cache
372
0
    pub fn clear(&mut self) {
373
0
        self.shard.clear();
374
0
    }
375
376
    /// Iterator for the items in the cache
377
0
    pub fn iter(&self) -> impl Iterator<Item = (&'_ Key, &'_ Val)> + '_ {
378
        // TODO: add a concrete type, impl trait in the public api is really bad.
379
0
        self.shard.iter()
380
0
    }
381
382
    /// Drain all items from the cache
383
    ///
384
    /// The cache will be emptied even if the returned iterator isn't fully consumed.
385
0
    pub fn drain(&mut self) -> impl Iterator<Item = (Key, Val)> + '_ {
386
        // TODO: add a concrete type, impl trait in the public api is really bad.
387
0
        self.shard.drain()
388
0
    }
389
390
    /// Sets the cache to a new weight capacity.
391
    ///
392
    /// If the new capacity is smaller than the current weight, items will be evicted
393
    /// to bring the cache within the new limit.
394
0
    pub fn set_capacity(&mut self, new_weight_capacity: u64) {
395
0
        self.shard.set_capacity(new_weight_capacity);
396
0
    }
397
398
    #[cfg(any(fuzzing, test))]
399
0
    pub fn validate(&self, accept_overweight: bool) {
400
0
        self.shard.validate(accept_overweight);
401
0
    }
402
403
    /// Get total memory used by cache data structures
404
    ///
405
    /// It should be noted that if cache key or value is some type like `Vec<T>`,
406
    /// the memory allocated in the heap will not be counted.
407
0
    pub fn memory_used(&self) -> MemoryUsed {
408
0
        self.shard.memory_used()
409
0
    }
410
}
411
412
impl<Key, Val, We, B, L> std::fmt::Debug for Cache<Key, Val, We, B, L> {
413
0
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
414
0
        f.debug_struct("Cache").finish_non_exhaustive()
415
0
    }
416
}
417
418
/// Default `Lifecycle` for the unsync cache.
419
pub struct DefaultLifecycle<Key, Val>(std::marker::PhantomData<(Key, Val)>);
420
421
impl<Key, Val> std::fmt::Debug for DefaultLifecycle<Key, Val> {
422
0
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
423
0
        f.debug_tuple("DefaultLifecycle").finish()
424
0
    }
425
}
426
427
impl<Key, Val> Default for DefaultLifecycle<Key, Val> {
428
    #[inline]
429
0
    fn default() -> Self {
430
0
        Self(Default::default())
431
0
    }
432
}
433
434
impl<Key, Val> Clone for DefaultLifecycle<Key, Val> {
435
    #[inline]
436
0
    fn clone(&self) -> Self {
437
0
        Self(Default::default())
438
0
    }
439
}
440
441
impl<Key, Val> Lifecycle<Key, Val> for DefaultLifecycle<Key, Val> {
442
    type RequestState = ();
443
444
    #[inline]
445
0
    fn begin_request(&self) -> Self::RequestState {}
446
447
    #[inline]
448
0
    fn on_evict(&self, _state: &mut Self::RequestState, _key: Key, _val: Val) {}
449
}
450
451
#[derive(Debug, Clone)]
452
pub(crate) struct SharedPlaceholder {
453
    hash: u64,
454
    idx: Token,
455
}
456
457
pub struct Guard<'a, Key, Val, We, B, L> {
458
    cache: &'a mut Cache<Key, Val, We, B, L>,
459
    placeholder: SharedPlaceholder,
460
    inserted: bool,
461
}
462
463
impl<Key: Eq + Hash, Val, We: Weighter<Key, Val>, B: BuildHasher, L: Lifecycle<Key, Val>>
464
    Guard<'_, Key, Val, We, B, L>
465
{
466
    /// Inserts the value into the placeholder
467
0
    pub fn insert(self, value: Val) {
468
0
        self.insert_internal(value, false);
469
0
    }
470
471
    /// Inserts the value into the placeholder
472
0
    pub fn insert_with_lifecycle(self, value: Val) -> L::RequestState {
473
0
        self.insert_internal(value, true).unwrap()
474
0
    }
475
476
    #[inline]
477
0
    fn insert_internal(mut self, value: Val, return_lcs: bool) -> Option<L::RequestState> {
478
0
        let mut lcs = self.cache.shard.lifecycle.begin_request();
479
0
        let replaced =
480
0
            self.cache
481
0
                .shard
482
0
                .replace_placeholder(&mut lcs, &self.placeholder, false, value);
483
0
        debug_assert!(replaced.is_ok(), "unsync replace_placeholder can't fail");
484
0
        self.inserted = true;
485
0
        if return_lcs {
486
0
            Some(lcs)
487
        } else {
488
0
            self.cache.shard.lifecycle.end_request(lcs);
489
0
            None
490
        }
491
0
    }
492
}
493
494
impl<Key, Val, We, B, L> Drop for Guard<'_, Key, Val, We, B, L> {
495
    #[inline]
496
0
    fn drop(&mut self) {
497
        #[cold]
498
0
        fn drop_slow<Key, Val, We, B, L>(this: &mut Guard<'_, Key, Val, We, B, L>) {
499
0
            this.cache.shard.remove_placeholder(&this.placeholder);
500
0
        }
501
0
        if !self.inserted {
502
0
            drop_slow(self);
503
0
        }
504
0
    }
505
}
506
507
pub struct RefMut<'cache, Key, Val, We: Weighter<Key, Val>, B, L>(
508
    crate::shard::RefMut<'cache, Key, Val, We, B, L, SharedPlaceholder>,
509
);
510
511
impl<Key, Val, We: Weighter<Key, Val>, B, L> std::ops::Deref for RefMut<'_, Key, Val, We, B, L> {
512
    type Target = Val;
513
514
    #[inline]
515
0
    fn deref(&self) -> &Self::Target {
516
0
        self.0.pair().1
517
0
    }
518
}
519
520
impl<Key, Val, We: Weighter<Key, Val>, B, L> std::ops::DerefMut for RefMut<'_, Key, Val, We, B, L> {
521
    #[inline]
522
0
    fn deref_mut(&mut self) -> &mut Self::Target {
523
0
        self.0.value_mut()
524
0
    }
525
}
526
527
impl shard::SharedPlaceholder for SharedPlaceholder {
528
    #[inline]
529
0
    fn new(hash: u64, idx: Token) -> Self {
530
0
        Self { hash, idx }
531
0
    }
532
533
    #[inline]
534
0
    fn same_as(&self, _other: &Self) -> bool {
535
0
        true
536
0
    }
537
538
    #[inline]
539
0
    fn hash(&self) -> u64 {
540
0
        self.hash
541
0
    }
542
543
    #[inline]
544
0
    fn idx(&self) -> Token {
545
0
        self.idx
546
0
    }
547
}
548
549
#[cfg(test)]
550
mod tests {
551
    use super::*;
552
553
    struct Weighter;
554
555
    impl crate::Weighter<u32, u32> for Weighter {
556
        fn weight(&self, _key: &u32, val: &u32) -> u64 {
557
            *val as u64
558
        }
559
    }
560
561
    #[test]
562
    fn test_zero_weights() {
563
        let mut cache = Cache::with_weighter(100, 100, Weighter);
564
        cache.insert(0, 0);
565
        assert_eq!(cache.weight(), 0);
566
        for i in 1..100 {
567
            cache.insert(i, i);
568
            cache.insert(i, i);
569
        }
570
        assert_eq!(cache.get(&0).copied(), Some(0));
571
        assert!(cache.contains_key(&0));
572
        let a = cache.weight();
573
        *cache.get_mut(&0).unwrap() += 1;
574
        assert_eq!(cache.weight(), a + 1);
575
        for i in 1..100 {
576
            cache.insert(i, i);
577
            cache.insert(i, i);
578
        }
579
        assert_eq!(cache.get(&0), None);
580
        assert!(!cache.contains_key(&0));
581
582
        cache.insert(0, 1);
583
        let a = cache.weight();
584
        *cache.get_mut(&0).unwrap() -= 1;
585
        assert_eq!(cache.weight(), a - 1);
586
        for i in 1..100 {
587
            cache.insert(i, i);
588
            cache.insert(i, i);
589
        }
590
        assert_eq!(cache.get(&0).copied(), Some(0));
591
        assert!(cache.contains_key(&0));
592
    }
593
594
    #[test]
595
    fn test_set_capacity() {
596
        let mut cache = Cache::new(100);
597
        for i in 0..80 {
598
            cache.insert(i, i);
599
        }
600
        let initial_len = cache.len();
601
        assert!(initial_len <= 80);
602
603
        // Set to smaller capacity
604
        cache.set_capacity(50);
605
        assert!(cache.len() <= 50);
606
        assert!(cache.weight() <= 50);
607
        cache.validate(false);
608
609
        // Set to larger capacity
610
        cache.set_capacity(200);
611
        assert_eq!(cache.capacity(), 200);
612
        cache.validate(false);
613
614
        // Insert more items
615
        for i in 100..180 {
616
            cache.insert(i, i);
617
        }
618
        assert!(cache.len() <= 180);
619
        assert!(cache.weight() <= 200);
620
        cache.validate(false);
621
    }
622
623
    #[test]
624
    fn test_set_capacity_with_ghosts() {
625
        // Create a cache that will generate ghost entries
626
        let mut cache = Cache::new(50);
627
628
        // Insert items to fill the cache
629
        for i in 0..100 {
630
            cache.insert(i, i);
631
        }
632
        cache.validate(false);
633
634
        // Set to smaller capacity - should trim both resident and ghost entries
635
        cache.set_capacity(25);
636
        assert!(cache.weight() <= 25);
637
        cache.validate(false);
638
639
        // Set back to larger capacity
640
        cache.set_capacity(100);
641
        assert_eq!(cache.capacity(), 100);
642
        cache.validate(false);
643
644
        // Insert more items
645
        for i in 100..150 {
646
            cache.insert(i, i);
647
        }
648
        cache.validate(false);
649
    }
650
651
    #[test]
652
    fn test_remove_if() {
653
        let mut cache = Cache::new(100);
654
655
        // Insert test data
656
        cache.insert(1, 10);
657
        cache.insert(2, 20);
658
        cache.insert(3, 30);
659
660
        // Test removing with predicate that returns true
661
        let removed = cache.remove_if(&2, |v| *v == 20);
662
        assert_eq!(removed, Some((2, 20)));
663
        assert_eq!(cache.get(&2), None);
664
665
        // Test removing with predicate that returns false
666
        let not_removed = cache.remove_if(&3, |v| *v == 999);
667
        assert_eq!(not_removed, None);
668
        assert_eq!(cache.get(&3), Some(&30));
669
670
        // Test removing non-existent key
671
        let not_found = cache.remove_if(&999, |_| true);
672
        assert_eq!(not_found, None);
673
674
        cache.validate(false);
675
    }
676
}