Skip to content

Commit 5a0774c

Browse files
committed
Added Cache::remove_if
This method removes the entry from cache for a given key only if a provided predicate returns true for the entry's value.
1 parent bce276c commit 5a0774c

3 files changed

Lines changed: 88 additions & 0 deletions

File tree

src/shard.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,19 @@ impl<
604604
self.remove_internal(hash, idx)
605605
}
606606

607+
pub fn remove_if<Q, F>(&mut self, hash: u64, key: &Q, f: F) -> Option<(Key, Val)>
608+
where
609+
Q: Hash + Equivalent<Key> + ?Sized,
610+
F: FnOnce(&Val) -> bool,
611+
{
612+
let (idx, resident) = self.search_resident(hash, key)?;
613+
if f(&resident.value) {
614+
self.remove_internal(hash, idx)
615+
} else {
616+
None
617+
}
618+
}
619+
607620
pub fn remove_token(&mut self, token: Token) -> Option<(Key, Val)> {
608621
let Some((Entry::Resident(resident), _)) = self.entries.get(token) else {
609622
return None;

src/sync.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,19 @@ impl<
274274
shard.write().remove(hash, key)
275275
}
276276

277+
/// Remove an item from the cache whose key is `key` if `f(&value)` returns `true` for that entry.
278+
/// Compared to peek and remove, this method guarantees that no new value was inserted in-between.
279+
///
280+
/// Returns the removed entry, if any.
281+
pub fn remove_if<Q, F>(&self, key: &Q, f: F) -> Option<(Key, Val)>
282+
where
283+
Q: Hash + Equivalent<Key> + ?Sized,
284+
F: FnOnce(&Val) -> bool,
285+
{
286+
let (shard, hash) = self.shard_for(key).unwrap();
287+
shard.write().remove_if(hash, key, f)
288+
}
289+
277290
/// Inserts an item in the cache, but _only_ if an entry with key `key` already exists.
278291
/// If `soft` is set, the replace operation won't affect the "hotness" of the entry,
279292
/// even if the value is replaced.
@@ -756,4 +769,28 @@ mod tests {
756769
assert!(cache.len() <= 180);
757770
assert!(cache.weight() <= 200);
758771
}
772+
773+
#[test]
774+
fn test_remove_if() {
775+
let cache = Cache::new(100);
776+
777+
// Insert test data
778+
cache.insert(1, 10);
779+
cache.insert(2, 20);
780+
cache.insert(3, 30);
781+
782+
// Test removing with predicate that returns true
783+
let removed = cache.remove_if(&2, |v| *v == 20);
784+
assert_eq!(removed, Some((2, 20)));
785+
assert_eq!(cache.get(&2), None);
786+
787+
// Test removing with predicate that returns false
788+
let not_removed = cache.remove_if(&3, |v| *v == 999);
789+
assert_eq!(not_removed, None);
790+
assert_eq!(cache.get(&3), Some(30));
791+
792+
// Test removing non-existent key
793+
let not_found = cache.remove_if(&999, |_| true);
794+
assert_eq!(not_found, None);
795+
}
759796
}

src/unsync.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,18 @@ impl<Key: Eq + Hash, Val, We: Weighter<Key, Val>, B: BuildHasher, L: Lifecycle<K
194194
self.shard.remove(self.shard.hash(key), key)
195195
}
196196

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+
pub fn remove_if<Q, F>(&mut self, key: &Q, f: F) -> Option<(Key, Val)>
202+
where
203+
Q: Hash + Equivalent<Key> + ?Sized,
204+
F: FnOnce(&Val) -> bool,
205+
{
206+
self.shard.remove_if(self.shard.hash(key), key, f)
207+
}
208+
197209
/// Replaces an item in the cache, but only if it already exists.
198210
/// If `soft` is set, the replace operation won't affect the "hotness" of the key,
199211
/// even if the value is replaced.
@@ -635,4 +647,30 @@ mod tests {
635647
}
636648
cache.validate(false);
637649
}
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+
}
638676
}

0 commit comments

Comments
 (0)