diff --git a/.vscode/settings.json b/.vscode/settings.json index a061a0b0..5e984168 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,6 +19,7 @@ "deqs", "Deque", "Deques", + "docsrs", "Einziger", "else's", "Eytan", diff --git a/CHANGELOG.md b/CHANGELOG.md index 79a297a5..6108c25b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,24 @@ # Moka Cache — Change Log +## Version 0.9.1 + +### Fixed (Changed) + +- Relax the too restrictive requirement `Arc: Borrow` to `K: Borrow` for the + `contains_key`, `get` and `invalidate` methods of the following caches, so that + they will accept `&[u8]` as the key when `K` is `Vec`: ([#167][gh-pull-0167]) + - `sync::Cache` + - `sync::SegmentedCache` + - `future::Cache` + + ## Version 0.9.0 ### Added - Add support for eviction listener to the following caches ([#145][gh-pull-0145]). Eviction listener is a callback function that will be called when an entry is - removed from the cache. + removed from the cache: - `sync::Cache` - `sync::SegmentedCache` - `future::Cache` @@ -412,6 +424,7 @@ The minimum supported Rust version (MSRV) is now 1.51.0 (2021-03-25). [gh-issue-0034]: https://github.com/moka-rs/moka/issues/34/ [gh-issue-0031]: https://github.com/moka-rs/moka/issues/31/ +[gh-pull-0167]: https://github.com/moka-rs/moka/pull/167/ [gh-pull-0159]: https://github.com/moka-rs/moka/pull/159/ [gh-pull-0145]: https://github.com/moka-rs/moka/pull/145/ [gh-pull-0143]: https://github.com/moka-rs/moka/pull/143/ diff --git a/Cargo.toml b/Cargo.toml index ed95765b..c7ef7330 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "moka" -version = "0.9.0" +version = "0.9.1" edition = "2018" rust-version = "1.51" diff --git a/src/cht/map/bucket.rs b/src/cht/map/bucket.rs index e220bcf2..1991f5f9 100644 --- a/src/cht/map/bucket.rs +++ b/src/cht/map/bucket.rs @@ -1,5 +1,4 @@ use std::{ - borrow::Borrow, hash::{BuildHasher, Hash, Hasher}, mem::{self, MaybeUninit}, ptr, @@ -72,16 +71,12 @@ impl Drop for BucketArray { } impl<'g, K: 'g + Eq, V: 'g> BucketArray { - pub(crate) fn get( + pub(crate) fn get( &self, guard: &'g Guard, hash: u64, - key: &Q, - ) -> Result>, RelocatedError> - where - Q: Eq + ?Sized, - K: Borrow, - { + mut eq: impl FnMut(&K) -> bool, + ) -> Result>, RelocatedError> { let loop_result = self.probe_loop(guard, hash, |_, _, this_bucket_ptr| { let this_bucket_ref = if let Some(r) = unsafe { this_bucket_ptr.as_ref() } { r @@ -90,7 +85,7 @@ impl<'g, K: 'g + Eq, V: 'g> BucketArray { return ProbeLoopAction::Return(Shared::null()); }; - if this_bucket_ref.key.borrow() != key { + if !eq(&this_bucket_ref.key) { // Different key. Try next bucket return ProbeLoopAction::Continue; } @@ -111,16 +106,14 @@ impl<'g, K: 'g + Eq, V: 'g> BucketArray { } } - pub(crate) fn remove_if( + pub(crate) fn remove_if( &self, guard: &'g Guard, hash: u64, - key: &Q, + mut eq: impl FnMut(&K) -> bool, mut condition: F, ) -> Result>, F> where - Q: Eq + ?Sized, - K: Borrow, F: FnMut(&K, &V) -> bool, { let loop_result = self.probe_loop(guard, hash, |_, this_bucket, this_bucket_ptr| { @@ -133,7 +126,7 @@ impl<'g, K: 'g + Eq, V: 'g> BucketArray { let this_key = &this_bucket_ref.key; - if this_key.borrow() != key { + if !eq(this_key) { // Different key. Try next bucket. return ProbeLoopAction::Continue; } @@ -189,7 +182,7 @@ impl<'g, K: 'g + Eq, V: 'g> BucketArray { let state = maybe_state.take().unwrap(); if let Some(this_bucket_ref) = unsafe { this_bucket_ptr.as_ref() } { - if this_bucket_ref.key.borrow() != state.key() { + if &this_bucket_ref.key != state.key() { // Different key. Try next bucket. maybe_state = Some(state); return ProbeLoopAction::Continue; @@ -784,52 +777,79 @@ mod tests { let h3 = hash(&build_hasher, k3); let v3 = 15; - assert_eq!(buckets.get(guard, h1, k1), Ok(Shared::null())); - assert_eq!(buckets.get(guard, h2, k2), Ok(Shared::null())); - assert_eq!(buckets.get(guard, h3, k3), Ok(Shared::null())); + assert_eq!(buckets.get(guard, h1, |&k| k == k1), Ok(Shared::null())); + assert_eq!(buckets.get(guard, h2, |&k| k == k2), Ok(Shared::null())); + assert_eq!(buckets.get(guard, h3, |&k| k == k3), Ok(Shared::null())); assert!(matches!( insert(&buckets, guard, k1, h1, || v1), Ok(InsertionResult::Inserted) )); - assert_eq!(into_value(buckets.get(guard, h1, k1)), Ok(Some(v1))); - assert_eq!(buckets.get(guard, h2, k2), Ok(Shared::null())); - assert_eq!(buckets.get(guard, h3, k3), Ok(Shared::null())); + assert_eq!( + into_value(buckets.get(guard, h1, |&k| k == k1)), + Ok(Some(v1)) + ); + assert_eq!(buckets.get(guard, h2, |&k| k == k2), Ok(Shared::null())); + assert_eq!(buckets.get(guard, h3, |&k| k == k3), Ok(Shared::null())); assert!(matches!( insert(&buckets, guard, k2, h2, || v2), Ok(InsertionResult::Inserted) )); - assert_eq!(into_value(buckets.get(guard, h1, k1)), Ok(Some(v1))); - assert_eq!(into_value(buckets.get(guard, h2, k2)), Ok(Some(v2))); - assert_eq!(buckets.get(guard, h3, k3), Ok(Shared::null())); + assert_eq!( + into_value(buckets.get(guard, h1, |&k| k == k1)), + Ok(Some(v1)) + ); + assert_eq!( + into_value(buckets.get(guard, h2, |&k| k == k2)), + Ok(Some(v2)) + ); + assert_eq!(buckets.get(guard, h3, |&k| k == k3), Ok(Shared::null())); assert!(matches!( insert(&buckets, guard, k3, h3, || v3), Ok(InsertionResult::Inserted) )); - assert_eq!(into_value(buckets.get(guard, h1, k1)), Ok(Some(v1))); - assert_eq!(into_value(buckets.get(guard, h2, k2)), Ok(Some(v2))); - assert_eq!(into_value(buckets.get(guard, h3, k3)), Ok(Some(v3))); - - let b1 = buckets.remove_if(guard, h1, k1, |_, _| true).ok().unwrap(); + assert_eq!( + into_value(buckets.get(guard, h1, |&k| k == k1)), + Ok(Some(v1)) + ); + assert_eq!( + into_value(buckets.get(guard, h2, |&k| k == k2)), + Ok(Some(v2)) + ); + assert_eq!( + into_value(buckets.get(guard, h3, |&k| k == k3)), + Ok(Some(v3)) + ); + + let b1 = buckets + .remove_if(guard, h1, |&k| k == k1, |_, _| true) + .ok() + .unwrap(); assert!(is_tombstone(b1)); unsafe { defer_destroy_tombstone(guard, b1) }; - let b2 = buckets.remove_if(guard, h2, k2, |_, _| true).ok().unwrap(); + let b2 = buckets + .remove_if(guard, h2, |&k| k == k2, |_, _| true) + .ok() + .unwrap(); assert!(is_tombstone(b2)); unsafe { defer_destroy_tombstone(guard, b2) }; - let b3 = buckets.remove_if(guard, h3, k3, |_, _| true).ok().unwrap(); + let b3 = buckets + .remove_if(guard, h3, |&k| k == k3, |_, _| true) + .ok() + .unwrap(); assert!(is_tombstone(b3)); unsafe { defer_destroy_tombstone(guard, b3) }; - assert_eq!(buckets.get(guard, h1, k1), Ok(Shared::null())); - assert_eq!(buckets.get(guard, h2, k2), Ok(Shared::null())); - assert_eq!(buckets.get(guard, h3, k3), Ok(Shared::null())); + assert_eq!(buckets.get(guard, h1, |&k| k == k1), Ok(Shared::null())); + assert_eq!(buckets.get(guard, h2, |&k| k == k2), Ok(Shared::null())); + assert_eq!(buckets.get(guard, h3, |&k| k == k3), Ok(Shared::null())); for this_bucket in buckets.buckets.iter() { let this_bucket_ptr = this_bucket.swap(Shared::null(), Ordering::Relaxed, guard); diff --git a/src/cht/map/bucket_array_ref.rs b/src/cht/map/bucket_array_ref.rs index 9937e0d5..26e61b7c 100644 --- a/src/cht/map/bucket_array_ref.rs +++ b/src/cht/map/bucket_array_ref.rs @@ -1,7 +1,6 @@ use super::bucket::{self, Bucket, BucketArray, InsertOrModifyState, RehashOp}; use std::{ - borrow::Borrow, hash::{BuildHasher, Hash}, sync::atomic::{AtomicUsize, Ordering}, }; @@ -19,17 +18,12 @@ where K: Hash + Eq, S: BuildHasher, { - pub(crate) fn get_key_value_and_then( + pub(crate) fn get_key_value_and_then( &self, - key: &Q, hash: u64, - with_entry: F, - ) -> Option - where - Q: Hash + Eq + ?Sized, - K: Borrow, - F: FnOnce(&K, &V) -> Option, - { + mut eq: impl FnMut(&K) -> bool, + with_entry: impl FnOnce(&K, &V) -> Option, + ) -> Option { let guard = &crossbeam_epoch::pin(); let current_ref = self.get(guard); let mut bucket_array_ref = current_ref; @@ -38,7 +32,7 @@ where loop { match bucket_array_ref - .get(guard, hash, key) + .get(guard, hash, &mut eq) .map(|p| unsafe { p.as_ref() }) { Ok(Some(Bucket { @@ -64,19 +58,13 @@ where result } - pub(crate) fn remove_entry_if_and( + pub(crate) fn remove_entry_if_and( &self, - key: &Q, hash: u64, - mut condition: F, - with_previous_entry: G, - ) -> Option - where - Q: Hash + Eq + ?Sized, - K: Borrow, - F: FnMut(&K, &V) -> bool, - G: FnOnce(&K, &V) -> T, - { + mut eq: impl FnMut(&K) -> bool, + mut condition: impl FnMut(&K, &V) -> bool, + with_previous_entry: impl FnOnce(&K, &V) -> T, + ) -> Option { let guard = &crossbeam_epoch::pin(); let current_ref = self.get(guard); let mut bucket_array_ref = current_ref; @@ -96,7 +84,7 @@ where bucket_array_ref = bucket_array_ref.rehash(guard, self.build_hasher, rehash_op); } - match bucket_array_ref.remove_if(guard, hash, key, condition) { + match bucket_array_ref.remove_if(guard, hash, &mut eq, condition) { Ok(previous_bucket_ptr) => { if let Some(previous_bucket_ref) = unsafe { previous_bucket_ptr.as_ref() } { let Bucket { @@ -129,17 +117,13 @@ where result } - pub(crate) fn insert_if_not_present_and( + pub(crate) fn insert_if_not_present_and( &self, key: K, hash: u64, - on_insert: F, - with_existing_entry: G, - ) -> Option - where - F: FnOnce() -> V, - G: FnOnce(&K, &V) -> T, - { + on_insert: impl FnOnce() -> V, + with_existing_entry: impl FnOnce(&K, &V) -> T, + ) -> Option { use bucket::InsertionResult; let guard = &crossbeam_epoch::pin(); @@ -198,19 +182,14 @@ where result } - pub(crate) fn insert_with_or_modify_entry_and( + pub(crate) fn insert_with_or_modify_entry_and( &self, key: K, hash: u64, - on_insert: F, - mut on_modify: G, - with_old_entry: H, - ) -> Option - where - F: FnOnce() -> V, - G: FnMut(&K, &V) -> V, - H: FnOnce(&K, &V) -> T, - { + on_insert: impl FnOnce() -> V, + mut on_modify: impl FnMut(&K, &V) -> V, + with_old_entry: impl FnOnce(&K, &V) -> T, + ) -> Option { let guard = &crossbeam_epoch::pin(); let current_ref = self.get(guard); let mut bucket_array_ref = current_ref; @@ -267,10 +246,7 @@ where result } - pub(crate) fn keys(&self, mut with_key: F) -> Vec - where - F: FnMut(&K) -> T, - { + pub(crate) fn keys(&self, mut with_key: impl FnMut(&K) -> T) -> Vec { let guard = &crossbeam_epoch::pin(); let current_ref = self.get(guard); let mut bucket_array_ref = current_ref; diff --git a/src/cht/segment.rs b/src/cht/segment.rs index 551aec2d..c0a3968a 100644 --- a/src/cht/segment.rs +++ b/src/cht/segment.rs @@ -250,65 +250,37 @@ impl HashMap { impl HashMap { /// Returns a clone of the value corresponding to the key. - /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html - /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html #[inline] - pub(crate) fn get(&self, key: &Q, hash: u64) -> Option + pub(crate) fn get(&self, hash: u64, eq: impl FnMut(&K) -> bool) -> Option where - Q: Hash + Eq + ?Sized, - K: Borrow, V: Clone, { - self.get_key_value_and(key, hash, |_, v| v.clone()) + self.get_key_value_and(hash, eq, |_, v| v.clone()) } /// Returns the result of invoking a function with a reference to the /// key-value pair corresponding to the supplied key. - /// - /// The supplied key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for the key - /// type. - /// - /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html - /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html #[inline] - pub(crate) fn get_key_value_and(&self, key: &Q, hash: u64, with_entry: F) -> Option - where - Q: Hash + Eq + ?Sized, - K: Borrow, - F: FnOnce(&K, &V) -> T, - { - self.get_key_value_and_then(key, hash, |k, v| Some(with_entry(k, v))) + pub(crate) fn get_key_value_and( + &self, + hash: u64, + eq: impl FnMut(&K) -> bool, + with_entry: impl FnOnce(&K, &V) -> T, + ) -> Option { + self.get_key_value_and_then(hash, eq, |k, v| Some(with_entry(k, v))) } /// Returns the result of invoking a function with a reference to the /// key-value pair corresponding to the supplied key. - /// - /// The supplied key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for the key - /// type. - /// - /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html - /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html #[inline] - pub(crate) fn get_key_value_and_then( + pub(crate) fn get_key_value_and_then( &self, - key: &Q, hash: u64, - with_entry: F, - ) -> Option - where - Q: Hash + Eq + ?Sized, - K: Borrow, - F: FnOnce(&K, &V) -> Option, - { + eq: impl FnMut(&K) -> bool, + with_entry: impl FnOnce(&K, &V) -> Option, + ) -> Option { self.bucket_array_ref(hash) - .get_key_value_and_then(key, hash, with_entry) + .get_key_value_and_then(hash, eq, with_entry) } /// Inserts a key-value pair into the map, returning the result of invoking @@ -319,16 +291,15 @@ impl HashMap { /// updated. #[cfg(test)] #[inline] - pub fn insert_entry_and( + pub fn insert_entry_and( &self, key: K, hash: u64, value: V, - with_previous_entry: F, + with_previous_entry: impl FnOnce(&K, &V) -> T, ) -> Option where V: Clone, - F: FnOnce(&K, &V) -> T, { let result = self .bucket_array_ref(hash) @@ -350,40 +321,23 @@ impl HashMap { /// Removes a key from the map, returning a clone of the value previously /// corresponding to the key. - /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html - /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html #[inline] - pub(crate) fn remove(&self, key: &Q, hash: u64) -> Option + pub(crate) fn remove(&self, hash: u64, eq: impl FnMut(&K) -> bool) -> Option where - Q: Hash + Eq + ?Sized, - K: Borrow, V: Clone, { - self.remove_entry_if_and(key, hash, |_, _| true, |_, v| v.clone()) + self.remove_entry_if_and(hash, eq, |_, _| true, |_, v| v.clone()) } /// Removes a key from the map, returning a clone of the key-value pair /// previously corresponding to the key. - /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html - /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html #[inline] - pub(crate) fn remove_entry(&self, key: &Q, hash: u64) -> Option<(K, V)> + pub(crate) fn remove_entry(&self, hash: u64, eq: impl FnMut(&K) -> bool) -> Option<(K, V)> where - Q: Hash + Eq + ?Sized, - K: Borrow + Clone, + K: Clone, V: Clone, { - self.remove_entry_if_and(key, hash, |_, _| true, |k, v| (k.clone(), v.clone())) + self.remove_entry_if_and(hash, eq, |_, _| true, |k, v| (k.clone(), v.clone())) } /// Removes a key from the map if a condition is met, returning a clone of @@ -392,22 +346,18 @@ impl HashMap { /// `condition` will be invoked at least once if [`Some`] is returned. It /// may also be invoked one or more times if [`None`] is returned. /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html - /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - pub(crate) fn remove_if(&self, key: &Q, hash: u64, condition: F) -> Option + pub(crate) fn remove_if( + &self, + hash: u64, + eq: impl FnMut(&K) -> bool, + condition: impl FnMut(&K, &V) -> bool, + ) -> Option where - Q: Hash + Eq + ?Sized, - K: Borrow, V: Clone, - F: FnMut(&K, &V) -> bool, { - self.remove_entry_if_and(key, hash, condition, move |_, v| v.clone()) + self.remove_entry_if_and(hash, eq, condition, move |_, v| v.clone()) } /// Removes a key from the map if a condition is met, returning the result @@ -417,30 +367,18 @@ impl HashMap { /// `condition` will be invoked at least once if [`Some`] is returned. It /// may also be invoked one or more times if [`None`] is returned. /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html - /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None #[inline] - pub(crate) fn remove_entry_if_and( + pub(crate) fn remove_entry_if_and( &self, - key: &Q, hash: u64, - condition: F, - with_previous_entry: G, - ) -> Option - where - Q: Hash + Eq + ?Sized, - K: Borrow, - F: FnMut(&K, &V) -> bool, - G: FnOnce(&K, &V) -> T, - { + eq: impl FnMut(&K) -> bool, + condition: impl FnMut(&K, &V) -> bool, + with_previous_entry: impl FnOnce(&K, &V) -> T, + ) -> Option { self.bucket_array_ref(hash) - .remove_entry_if_and(key, hash, condition, move |k, v| { + .remove_entry_if_and(hash, eq, condition, move |k, v| { self.len.fetch_sub(1, Ordering::Relaxed); with_previous_entry(k, v) @@ -459,17 +397,15 @@ impl HashMap { /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None #[inline] - pub(crate) fn insert_with_or_modify( + pub(crate) fn insert_with_or_modify( &self, key: K, hash: u64, - on_insert: F, - on_modify: G, + on_insert: impl FnOnce() -> V, + on_modify: impl FnMut(&K, &V) -> V, ) -> Option where V: Clone, - F: FnOnce() -> V, - G: FnMut(&K, &V) -> V, { self.insert_with_or_modify_entry_and(key, hash, on_insert, on_modify, |_, v| v.clone()) } @@ -487,19 +423,14 @@ impl HashMap { /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None #[inline] - pub(crate) fn insert_with_or_modify_entry_and( + pub(crate) fn insert_with_or_modify_entry_and( &self, key: K, hash: u64, - on_insert: F, - on_modify: G, - with_old_entry: H, - ) -> Option - where - F: FnOnce() -> V, - G: FnMut(&K, &V) -> V, - H: FnOnce(&K, &V) -> T, - { + on_insert: impl FnOnce() -> V, + on_modify: impl FnMut(&K, &V) -> V, + with_old_entry: impl FnOnce(&K, &V) -> T, + ) -> Option { let result = self.bucket_array_ref(hash).insert_with_or_modify_entry_and( key, hash, @@ -534,10 +465,7 @@ impl HashMap { result } - pub(crate) fn keys(&self, segment: usize, with_key: F) -> Option> - where - F: FnMut(&K) -> T, - { + pub(crate) fn keys(&self, segment: usize, with_key: impl FnMut(&K) -> T) -> Option> { if segment >= self.segments.len() { return None; } @@ -666,12 +594,12 @@ mod tests { let hash = map.hash(key); assert_eq!(map.insert_entry_and(key, hash, 5, |_, v| *v), None); - assert_eq!(map.get(key, hash), Some(5)); + assert_eq!(map.get(hash, |k| k == &key), Some(5)); assert!(!map.is_empty()); assert_eq!(map.len(), 1); - assert_eq!(map.remove(key, hash), Some(5)); + assert_eq!(map.remove(hash, |k| k == &key), Some(5)); assert!(map.is_empty()); assert_eq!(map.len(), 0); @@ -687,17 +615,17 @@ mod tests { let hash = map.hash(key); assert_eq!(map.insert_if_not_present(key, hash, 5), None); - assert_eq!(map.get(key, hash), Some(5)); + assert_eq!(map.get(hash, |k| k == &key), Some(5)); assert_eq!(map.insert_if_not_present(key, hash, 6), Some(5)); - assert_eq!(map.get(key, hash), Some(5)); + assert_eq!(map.get(hash, |k| k == &key), Some(5)); - assert_eq!(map.remove(key, hash), Some(5)); + assert_eq!(map.remove(hash, |k| k == &key), Some(5)); assert_eq!(map.insert_if_not_present(key, hash, 7), None); - assert_eq!(map.get(key, hash), Some(7)); + assert_eq!(map.get(hash, |k| k == &key), Some(7)); - assert_eq!(map.remove(key, hash), Some(7)); + assert_eq!(map.remove(hash, |k| k == &key), Some(7)); assert!(map.is_empty()); assert_eq!(map.len(), 0); @@ -764,7 +692,7 @@ mod tests { // Get all entries from the cht MashMap. for key in 0..MAX_VALUE { let hash = hashmap.hash(&key); - if let Some(thread_id) = hashmap.get(&key, hash) { + if let Some(thread_id) = hashmap.get(hash, |&k| k == key) { let count = results2.get_mut(&thread_id).unwrap(); *count += 1; } @@ -790,12 +718,12 @@ mod tests { for j in 0..=i { let hash = map.hash(&j); - assert_eq!(map.get(&j, hash), Some(j)); + assert_eq!(map.get(hash, |&k| k == j), Some(j)); assert_eq!(map.insert_entry_and(j, hash, j, |_, v| *v), Some(j)); } - for k in i + 1..MAX_VALUE { - assert_eq!(map.get(&k, map.hash(&k)), None); + for l in i + 1..MAX_VALUE { + assert_eq!(map.get(map.hash(&l), |&k| k == l), None); } } @@ -816,12 +744,12 @@ mod tests { for j in 0..=i { let hash = map.hash(&j); - assert_eq!(map.get(&j, hash), Some(j)); + assert_eq!(map.get(hash, |&k| k == j), Some(j)); assert_eq!(map.insert_entry_and(j, hash, j, |_, v| *v), Some(j)); } - for k in i + 1..MAX_VALUE { - assert_eq!(map.get(&k, map.hash(&k)), None); + for l in i + 1..MAX_VALUE { + assert_eq!(map.get(map.hash(&l), |&k| k == l), None); } } @@ -867,7 +795,7 @@ mod tests { assert_eq!(map.len(), MAX_INSERTED_VALUE as usize); for i in 0..MAX_INSERTED_VALUE { - assert_eq!(map.get(&i, map.hash(&i)), Some(i)); + assert_eq!(map.get(map.hash(&i), |&k| k == i), Some(i)); } run_deferred(); @@ -907,7 +835,7 @@ mod tests { assert_eq!(map.len(), MAX_INSERTED_VALUE as usize); for i in 0..MAX_INSERTED_VALUE { - assert_eq!(map.get(&i, map.hash(&i)), Some(i)); + assert_eq!(map.get(map.hash(&i), |&k| k == i), Some(i)); } run_deferred(); @@ -924,14 +852,14 @@ mod tests { } for i in 0..MAX_VALUE { - assert_eq!(map.remove(&i, map.hash(&i)), Some(i)); + assert_eq!(map.remove(map.hash(&i), |&k| k == i), Some(i)); } assert!(map.is_empty()); assert_eq!(map.len(), 0); for i in 0..MAX_VALUE { - assert_eq!(map.get(&i, map.hash(&i)), None); + assert_eq!(map.get(map.hash(&i), |&k| k == i), None); } run_deferred(); @@ -963,7 +891,7 @@ mod tests { barrier.wait(); for j in (0..MAX_VALUE).map(|j| j + (i as i32 * MAX_VALUE)) { - assert_eq!(map.remove(&j, map.hash(&j)), Some(j)); + assert_eq!(map.remove(map.hash(&j), |&k| k == j), Some(j)); } }) }) @@ -976,7 +904,7 @@ mod tests { assert_eq!(map.len(), 0); for i in 0..MAX_INSERTED_VALUE { - assert_eq!(map.get(&i, map.hash(&i)), None); + assert_eq!(map.get(map.hash(&i), |&k| k == i), None); } run_deferred(); @@ -1026,7 +954,7 @@ mod tests { for j in (0..MAX_VALUE).map(|j| INSERTED_MIDPOINT + j + (i as i32 * MAX_VALUE)) { - assert_eq!(map.remove(&j, map.hash(&j)), Some(j)); + assert_eq!(map.remove(map.hash(&j), |&k| k == j), Some(j)); } }) }) @@ -1044,11 +972,11 @@ mod tests { assert_eq!(map.len(), INSERTED_MIDPOINT as usize); for i in 0..INSERTED_MIDPOINT { - assert_eq!(map.get(&i, map.hash(&i)), Some(i)); + assert_eq!(map.get(map.hash(&i), |&k| k == i), Some(i)); } for i in INSERTED_MIDPOINT..MAX_INSERTED_VALUE { - assert_eq!(map.get(&i, map.hash(&i)), None); + assert_eq!(map.get(map.hash(&i), |&k| k == i), None); } run_deferred(); @@ -1098,7 +1026,7 @@ mod tests { for j in (0..MAX_VALUE).map(|j| INSERTED_MIDPOINT + j + (i as i32 * MAX_VALUE)) { - assert_eq!(map.remove(&j, map.hash(&j)), Some(j)); + assert_eq!(map.remove(map.hash(&j), |&k| k == j), Some(j)); } }) }) @@ -1116,11 +1044,11 @@ mod tests { assert_eq!(map.len(), INSERTED_MIDPOINT as usize); for i in 0..INSERTED_MIDPOINT { - assert_eq!(map.get(&i, map.hash(&i)), Some(i)); + assert_eq!(map.get(map.hash(&i), |&k| k == i), Some(i)); } for i in INSERTED_MIDPOINT..MAX_INSERTED_VALUE { - assert_eq!(map.get(&i, map.hash(&i)), None); + assert_eq!(map.get(map.hash(&i), |&k| k == i), None); } run_deferred(); @@ -1137,13 +1065,13 @@ mod tests { map.insert_with_or_modify(key, hash, || 1, |_, x| x + 1), None ); - assert_eq!(map.get(key, hash), Some(1)); + assert_eq!(map.get(hash, |&k| k == key), Some(1)); assert_eq!( map.insert_with_or_modify(key, hash, || 1, |_, x| x + 1), Some(1) ); - assert_eq!(map.get(key, hash), Some(2)); + assert_eq!(map.get(hash, |&k| k == key), Some(2)); run_deferred(); } @@ -1180,7 +1108,7 @@ mod tests { assert_eq!(map.len(), MAX_VALUE as usize); for i in 0..MAX_VALUE { - assert_eq!(map.get(&i, map.hash(&i)), Some(NUM_THREADS as i32)); + assert_eq!(map.get(map.hash(&i), |&k| k == i), Some(NUM_THREADS as i32)); } run_deferred(); @@ -1218,7 +1146,7 @@ mod tests { assert_eq!(map.len(), MAX_VALUE as usize); for i in 0..MAX_VALUE { - assert_eq!(map.get(&i, map.hash(&i)), Some(i)); + assert_eq!(map.get(map.hash(&i), |&k| k == i), Some(i)); } run_deferred(); @@ -1266,7 +1194,7 @@ mod tests { assert_eq!(map.len(), MAX_VALUE as usize); for i in 0..MAX_VALUE { - assert_eq!(map.get(&i, map.hash(&i)), Some(i)); + assert_eq!(map.get(map.hash(&i), |&k| k == i), Some(i)); } run_deferred(); @@ -1297,7 +1225,7 @@ mod tests { barrier.wait(); for j in 0..MAX_VALUE { - let prev_value = map.remove(&j, map.hash(&j)); + let prev_value = map.remove(map.hash(&j), |&k| k == j); if let Some(v) = prev_value { assert_eq!(v, j); @@ -1315,7 +1243,7 @@ mod tests { assert_eq!(map.len(), 0); for i in 0..MAX_VALUE { - assert_eq!(map.get(&i, map.hash(&i)), None); + assert_eq!(map.get(map.hash(&i), |&k| k == i), None); } run_deferred(); @@ -1341,12 +1269,12 @@ mod tests { ); assert!(!map.is_empty()); assert_eq!(map.len(), 1); - map.get_key_value_and(&0, hash, |_k, v| assert_eq!(v, &0)); + map.get_key_value_and(hash, |k| k == &0, |_k, v| assert_eq!(v, &0)); - map.remove_entry_if_and(&0, hash, |_, _| true, |_k, v| assert_eq!(v, &0)); + map.remove_entry_if_and(hash, |k| k == &0, |_, _| true, |_k, v| assert_eq!(v, &0)); assert!(map.is_empty()); assert_eq!(map.len(), 0); - assert_eq!(map.get_key_value_and(&0, hash, |_, _| ()), None); + assert_eq!(map.get_key_value_and(hash, |k| k == &0, |_, _| ()), None); run_deferred(); @@ -1395,10 +1323,14 @@ mod tests { for i in 0..NUM_VALUES { assert_eq!( - map.get_key_value_and(&i, map.hash(&i), |k, v| { - assert_eq!(*k, i); - assert_eq!(*v, i); - }), + map.get_key_value_and( + map.hash(&i), + |k| k == &i, + |k, v| { + assert_eq!(**k, i); + assert_eq!(*v, i); + } + ), Some(()) ); } @@ -1406,11 +1338,11 @@ mod tests { for i in 0..NUM_VALUES { assert_eq!( map.remove_entry_if_and( - &i, map.hash(&i), + |k| k == &i, |_, _| true, |k, v| { - assert_eq!(*k, i); + assert_eq!(**k, i); assert_eq!(*v, i); } ), @@ -1438,7 +1370,10 @@ mod tests { } for i in 0..NUM_VALUES { - assert_eq!(map.get_key_value_and(&i, map.hash(&i), |_, _| ()), None); + assert_eq!( + map.get_key_value_and(map.hash(&i), |k| k == &i, |_, _| ()), + None + ); } } @@ -1534,10 +1469,14 @@ mod tests { for i in (0..NUM_VALUES).map(|i| i as i32) { assert_eq!( - map.get_key_value_and(&i, map.hash(&i), |k, v| { - assert_eq!(*k, i); - assert_eq!(*v, i); - }), + map.get_key_value_and( + map.hash(&i), + |k| k == &i, + |k, v| { + assert_eq!(**k, i); + assert_eq!(*v, i); + } + ), Some(()) ); } @@ -1556,11 +1495,11 @@ mod tests { assert_eq!( map.remove_entry_if_and( - &key_value, map.hash(&key_value), + |k| k == &key_value, |_, _| true, |k, v| { - assert_eq!(*k, key_value); + assert_eq!(**k, key_value); assert_eq!(*v, key_value); } ), @@ -1591,7 +1530,10 @@ mod tests { } for i in (0..NUM_VALUES).map(|i| i as i32) { - assert_eq!(map.get_key_value_and(&i, map.hash(&i), |_, _| ()), None); + assert_eq!( + map.get_key_value_and(map.hash(&i), |k| k == &i, |_, _| ()), + None + ); } } @@ -1620,18 +1562,18 @@ mod tests { for i in 0..NUM_VALUES { if is_even(&i, &i) { - assert_eq!(map.remove_if(&i, map.hash(&i), is_even), Some(i)); + assert_eq!(map.remove_if(map.hash(&i), |&k| k == i, is_even), Some(i)); } else { - assert_eq!(map.remove_if(&i, map.hash(&i), is_even), None); + assert_eq!(map.remove_if(map.hash(&i), |&k| k == i, is_even), None); } } for i in (0..NUM_VALUES).filter(|i| i % 2 == 0) { - assert_eq!(map.get(&i, map.hash(&i)), None); + assert_eq!(map.get(map.hash(&i), |&k| k == i), None); } for i in (0..NUM_VALUES).filter(|i| i % 2 != 0) { - assert_eq!(map.get(&i, map.hash(&i)), Some(i)); + assert_eq!(map.get(map.hash(&i), |&k| k == i), Some(i)); } run_deferred(); @@ -1648,35 +1590,34 @@ mod tests { const NUM_KEYS: usize = 200; for i in 0..NUM_KEYS { - let key = Arc::new(i); - let hash = map.hash(&key); - assert_eq!(map.insert_entry_and(key, hash, i, |_, v| *v), None); + let hash = map.hash(&i); + assert_eq!(map.insert_entry_and(i, hash, i, |_, v| *v), None); } assert!(!map.is_empty()); assert_eq!(map.len(), NUM_KEYS); - let mut keys = map.keys(0, |k| Arc::clone(k)).unwrap(); + let mut keys = map.keys(0, |k| *k).unwrap(); assert_eq!(keys.len(), NUM_KEYS); keys.sort_unstable(); for (i, key) in keys.into_iter().enumerate() { - assert_eq!(i, *key); + assert_eq!(i, key); } for i in (0..NUM_KEYS).step_by(2) { - assert_eq!(map.remove(&i, map.hash(&i)), Some(i)); + assert_eq!(map.remove(map.hash(&i), |&k| k == i), Some(i)); } assert!(!map.is_empty()); assert_eq!(map.len(), NUM_KEYS / 2); - let mut keys = map.keys(0, |k| Arc::clone(k)).unwrap(); + let mut keys = map.keys(0, |k| *k).unwrap(); assert_eq!(keys.len(), NUM_KEYS / 2); keys.sort_unstable(); for (i, key) in keys.into_iter().enumerate() { - assert_eq!(i, *key / 2); + assert_eq!(i, key / 2); } run_deferred(); diff --git a/src/future/cache.rs b/src/future/cache.rs index 3defc6df..93fc2eb9 100644 --- a/src/future/cache.rs +++ b/src/future/cache.rs @@ -719,7 +719,7 @@ where /// on the borrowed form _must_ match those for the key type. pub fn contains_key(&self, key: &Q) -> bool where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { self.base.contains_key_with_hash(key, self.base.hash(key)) @@ -737,7 +737,7 @@ where /// [rustdoc-std-arc]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html pub fn get(&self, key: &Q) -> Option where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { self.base.get_with_hash(key, self.base.hash(key)) @@ -992,7 +992,7 @@ where /// on the borrowed form _must_ match those for the key type. pub async fn invalidate(&self, key: &Q) where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { let hash = self.base.hash(key); @@ -1010,7 +1010,7 @@ where fn do_blocking_invalidate(&self, key: &Q) where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { let hash = self.base.hash(key); @@ -1347,7 +1347,7 @@ where /// on the borrowed form _must_ match those for the key type. pub fn invalidate(&self, key: &Q) where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { self.0.do_blocking_invalidate(key) @@ -2724,7 +2724,7 @@ mod tests { expected.push((Arc::new("alice"), "a0", RemovalCause::Replaced)); cache.sync(); - // Insert an okay value. This will replace the previsous + // Insert an okay value. This will replace the previous // value "panic now!" so the eviction listener will panic. cache.insert("alice", "a2").await; cache.sync(); @@ -2737,6 +2737,31 @@ mod tests { verify_notification_vec(&cache, actual, &expected); } + // This test ensures that the `contains_key`, `get` and `invalidate` can use + // borrowed form `&[u8]` for key with type `Vec`. + // https://github.com/moka-rs/moka/issues/166 + #[tokio::test] + async fn borrowed_forms_of_key() { + let cache: Cache, ()> = Cache::new(1); + + let key = vec![1_u8]; + cache.insert(key.clone(), ()).await; + + // key as &Vec + let key_v: &Vec = &key; + assert!(cache.contains_key(key_v)); + assert_eq!(cache.get(key_v), Some(())); + cache.invalidate(key_v).await; + + cache.insert(key, ()).await; + + // key as &[u8] + let key_s: &[u8] = &[1_u8]; + assert!(cache.contains_key(key_s)); + assert_eq!(cache.get(key_s), Some(())); + cache.invalidate(key_s).await; + } + #[tokio::test] async fn test_debug_format() { let cache = Cache::new(10); diff --git a/src/future/value_initializer.rs b/src/future/value_initializer.rs index e8fe2e84..cb187216 100644 --- a/src/future/value_initializer.rs +++ b/src/future/value_initializer.rs @@ -32,7 +32,7 @@ struct WaiterGuard<'a, K, V, S> // NOTE: We usually do not attach trait bounds to here at the struct definition, but // the Drop trait requires these bounds here. where - Arc: Eq + Hash, + K: Eq + Hash, V: Clone, S: BuildHasher, { @@ -45,7 +45,7 @@ where impl<'a, K, V, S> WaiterGuard<'a, K, V, S> where - Arc: Eq + Hash, + K: Eq + Hash, V: Clone, S: BuildHasher, { @@ -72,7 +72,7 @@ where impl<'a, K, V, S> Drop for WaiterGuard<'a, K, V, S> where - Arc: Eq + Hash, + K: Eq + Hash, V: Clone, S: BuildHasher, { @@ -98,7 +98,7 @@ pub(crate) struct ValueInitializer { impl ValueInitializer where - Arc: Eq + Hash, + K: Eq + Hash, V: Clone, S: BuildHasher, { @@ -241,7 +241,7 @@ where #[inline] pub(crate) fn remove_waiter(&self, key: &Arc, type_id: TypeId) { let (cht_key, hash) = self.cht_key_hash(key, type_id); - self.waiters.remove(&cht_key, hash); + self.waiters.remove(hash, |k| k == &cht_key); } #[inline] diff --git a/src/sync/cache.rs b/src/sync/cache.rs index 13a90adc..8d3e2c4c 100644 --- a/src/sync/cache.rs +++ b/src/sync/cache.rs @@ -873,7 +873,7 @@ where /// on the borrowed form _must_ match those for the key type. pub fn contains_key(&self, key: &Q) -> bool where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { self.base.contains_key_with_hash(key, self.base.hash(key)) @@ -881,7 +881,7 @@ where pub(crate) fn contains_key_with_hash(&self, key: &Q, hash: u64) -> bool where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { self.base.contains_key_with_hash(key, hash) @@ -899,7 +899,7 @@ where /// [rustdoc-std-arc]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html pub fn get(&self, key: &Q) -> Option where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { self.base.get_with_hash(key, self.base.hash(key)) @@ -907,7 +907,7 @@ where pub(crate) fn get_with_hash(&self, key: &Q, hash: u64) -> Option where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { self.base.get_with_hash(key, hash) @@ -1200,7 +1200,7 @@ where /// on the borrowed form _must_ match those for the key type. pub fn invalidate(&self, key: &Q) where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { let hash = self.base.hash(key); @@ -1209,7 +1209,7 @@ where pub(crate) fn invalidate_with_hash(&self, key: &Q, hash: u64) where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { // Lock the key for removal if blocking removal notification is enabled. @@ -2931,7 +2931,7 @@ mod tests { expected.push((Arc::new("alice"), "a0", RemovalCause::Replaced)); cache.sync(); - // Insert an okay value. This will replace the previsous + // Insert an okay value. This will replace the previous // value "panic now!" so the eviction listener will panic. cache.insert("alice", "a2"); cache.sync(); @@ -2945,6 +2945,31 @@ mod tests { } } + // This test ensures that the `contains_key`, `get` and `invalidate` can use + // borrowed form `&[u8]` for key with type `Vec`. + // https://github.com/moka-rs/moka/issues/166 + #[test] + fn borrowed_forms_of_key() { + let cache: Cache, ()> = Cache::new(1); + + let key = vec![1_u8]; + cache.insert(key.clone(), ()); + + // key as &Vec + let key_v: &Vec = &key; + assert!(cache.contains_key(key_v)); + assert_eq!(cache.get(key_v), Some(())); + cache.invalidate(key_v); + + cache.insert(key, ()); + + // key as &[u8] + let key_s: &[u8] = &[1_u8]; + assert!(cache.contains_key(key_s)); + assert_eq!(cache.get(key_s), Some(())); + cache.invalidate(key_s); + } + #[test] fn test_debug_format() { let cache = Cache::new(10); diff --git a/src/sync/segment.rs b/src/sync/segment.rs index 1aa984a8..d953d158 100644 --- a/src/sync/segment.rs +++ b/src/sync/segment.rs @@ -244,7 +244,7 @@ where /// on the borrowed form _must_ match those for the key type. pub fn contains_key(&self, key: &Q) -> bool where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { let hash = self.inner.hash(key); @@ -263,7 +263,7 @@ where /// [rustdoc-std-arc]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html pub fn get(&self, key: &Q) -> Option where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { let hash = self.inner.hash(key); @@ -358,7 +358,7 @@ where /// on the borrowed form _must_ match those for the key type. pub fn invalidate(&self, key: &Q) where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { let hash = self.inner.hash(key); @@ -635,7 +635,7 @@ where #[inline] fn hash(&self, key: &Q) -> u64 where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { let mut hasher = self.build_hasher.build_hasher(); @@ -1580,6 +1580,31 @@ mod tests { } } + // This test ensures that the `contains_key`, `get` and `invalidate` can use + // borrowed form `&[u8]` for key with type `Vec`. + // https://github.com/moka-rs/moka/issues/166 + #[test] + fn borrowed_forms_of_key() { + let cache: SegmentedCache, ()> = SegmentedCache::new(1, 2); + + let key = vec![1_u8]; + cache.insert(key.clone(), ()); + + // key as &Vec + let key_v: &Vec = &key; + assert!(cache.contains_key(key_v)); + assert_eq!(cache.get(key_v), Some(())); + cache.invalidate(key_v); + + cache.insert(key, ()); + + // key as &[u8] + let key_s: &[u8] = &[1_u8]; + assert!(cache.contains_key(key_s)); + assert_eq!(cache.get(key_s), Some(())); + cache.invalidate(key_s); + } + #[test] fn test_debug_format() { let cache = SegmentedCache::new(10, 4); diff --git a/src/sync/value_initializer.rs b/src/sync/value_initializer.rs index 154f4af8..7296ff4e 100644 --- a/src/sync/value_initializer.rs +++ b/src/sync/value_initializer.rs @@ -28,7 +28,7 @@ pub(crate) struct ValueInitializer { impl ValueInitializer where - Arc: Eq + Hash, + K: Eq + Hash, V: Clone, S: BuildHasher, { @@ -152,7 +152,7 @@ where #[inline] pub(crate) fn remove_waiter(&self, key: &Arc, type_id: TypeId) { let (cht_key, hash) = self.cht_key_hash(key, type_id); - self.waiters.remove(&cht_key, hash); + self.waiters.remove(hash, |k| k == &cht_key); } #[inline] diff --git a/src/sync_base/base_cache.rs b/src/sync_base/base_cache.rs index b659bde4..c4b763cd 100644 --- a/src/sync_base/base_cache.rs +++ b/src/sync_base/base_cache.rs @@ -188,7 +188,7 @@ where #[inline] pub(crate) fn hash(&self, key: &Q) -> u64 where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { self.inner.hash(key) @@ -196,7 +196,7 @@ where pub(crate) fn contains_key_with_hash(&self, key: &Q, hash: u64) -> bool where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { self.inner @@ -214,7 +214,7 @@ where pub(crate) fn get_with_hash(&self, key: &Q, hash: u64) -> Option where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { // Define a closure to record a read op. @@ -252,7 +252,7 @@ where #[cfg(feature = "sync")] pub(crate) fn get_key_with_hash(&self, key: &Q, hash: u64) -> Option> where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { self.inner @@ -262,7 +262,7 @@ where #[inline] pub(crate) fn remove_entry(&self, key: &Q, hash: u64) -> Option> where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { self.inner.remove_entry(key, hash) @@ -882,7 +882,7 @@ where #[inline] fn hash(&self, key: &Q) -> u64 where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { let mut hasher = self.build_hasher.build_hasher(); @@ -893,31 +893,33 @@ where #[inline] fn get_key_value_and(&self, key: &Q, hash: u64, with_entry: F) -> Option where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, F: FnOnce(&Arc, &TrioArc>) -> T, { - self.cache.get_key_value_and(key, hash, with_entry) + self.cache + .get_key_value_and(hash, |k| (k as &K).borrow() == key, with_entry) } #[inline] fn get_key_value_and_then(&self, key: &Q, hash: u64, with_entry: F) -> Option where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, F: FnOnce(&Arc, &TrioArc>) -> Option, { - self.cache.get_key_value_and_then(key, hash, with_entry) + self.cache + .get_key_value_and_then(hash, |k| (k as &K).borrow() == key, with_entry) } #[inline] fn remove_entry(&self, key: &Q, hash: u64) -> Option> where - Arc: Borrow, + K: Borrow, Q: Hash + Eq + ?Sized, { self.cache - .remove_entry(key, hash) + .remove_entry(hash, |k| (k as &K).borrow() == key) .map(|(key, entry)| KvEntry::new(key, entry)) } @@ -965,25 +967,24 @@ where S: BuildHasher, { fn get_value_entry(&self, key: &Arc, hash: u64) -> Option>> { - self.cache.get(key, hash) + self.cache.get(hash, |k| k == key) } - fn remove_key_value_if( + fn remove_key_value_if( &self, key: &Arc, hash: u64, - condition: F, + condition: impl FnMut(&Arc, &TrioArc>) -> bool, ) -> Option>> where K: Send + Sync + 'static, V: Clone + Send + Sync + 'static, - F: FnMut(&Arc, &TrioArc>) -> bool, { // Lock the key for removal if blocking removal notification is enabled. let kl = self.maybe_key_lock(key); let _klg = &kl.as_ref().map(|kl| kl.lock()); - let maybe_entry = self.cache.remove_if(key, hash, condition); + let maybe_entry = self.cache.remove_if(hash, |k| k == key, condition); if let Some(entry) = &maybe_entry { if self.is_removal_notifier_enabled() { self.notify_single_removal(Arc::clone(key), entry, RemovalCause::Explicit); @@ -1253,7 +1254,7 @@ where let kl = self.maybe_key_lock(&kh.key); let _klg = &kl.as_ref().map(|kl| kl.lock()); - let removed = self.cache.remove(&Arc::clone(&kh.key), kh.hash); + let removed = self.cache.remove(kh.hash, |k| k == &kh.key); if let Some(entry) = removed { if eviction_state.is_notifier_enabled() { let key = Arc::clone(&kh.key); @@ -1282,8 +1283,9 @@ where let kl = self.maybe_key_lock(element.key()); let _klg = &kl.as_ref().map(|kl| kl.lock()); - if let Some((vic_key, vic_entry)) = - self.cache.remove_entry(element.key(), element.hash()) + if let Some((vic_key, vic_entry)) = self + .cache + .remove_entry(element.hash(), |k| k == element.key()) { if eviction_state.is_notifier_enabled() { eviction_state.add_removed_entry( @@ -1315,7 +1317,7 @@ where // Remove the candidate from the cache (hash map). let key = Arc::clone(&kh.key); - self.cache.remove(&key, kh.hash); + self.cache.remove(kh.hash, |k| k == &key); if eviction_state.is_notifier_enabled() { eviction_state.add_removed_entry(key, &entry, RemovalCause::Size); } @@ -1372,7 +1374,7 @@ where next_victim = victim.next_node(); let vic_elem = &victim.element; - if let Some(vic_entry) = cache.get(vic_elem.key(), vic_elem.hash()) { + if let Some(vic_entry) = cache.get(vic_elem.hash(), |k| k == vic_elem.key()) { victims.add_policy_weight(vic_entry.policy_weight()); victims.add_frequency(freq, vic_elem.hash()); victim_nodes.push(NonNull::from(victim)); @@ -1544,9 +1546,11 @@ where // expired. This check is needed because it is possible that the entry in // the map has been updated or deleted but its deque node we checked // above has not been updated yet. - let maybe_entry = self - .cache - .remove_if(key, hash, |_, v| is_expired_entry_ao(tti, va, v, now)); + let maybe_entry = self.cache.remove_if( + hash, + |k| k == key, + |_, v| is_expired_entry_ao(tti, va, v, now), + ); if let Some(entry) = maybe_entry { if eviction_state.is_notifier_enabled() { @@ -1575,7 +1579,7 @@ where deq: &mut Deque>, write_order_deq: &mut Deque>, ) -> bool { - if let Some(entry) = self.cache.get(key, hash) { + if let Some(entry) = self.cache.get(hash, |k| (k.borrow() as &K) == key) { if entry.is_dirty() { // The key exists and the entry has been updated. Deques::move_to_back_ao_in_deque(deq_name, deq, &entry); @@ -1631,9 +1635,11 @@ where let kl = self.maybe_key_lock(key); let _klg = &kl.as_ref().map(|kl| kl.lock()); - let maybe_entry = self - .cache - .remove_if(key, hash, |_, v| is_expired_entry_wo(ttl, va, v, now)); + let maybe_entry = self.cache.remove_if( + hash, + |k| k == key, + |_, v| is_expired_entry_wo(ttl, va, v, now), + ); if let Some(entry) = maybe_entry { if eviction_state.is_notifier_enabled() { @@ -1641,7 +1647,7 @@ where eviction_state.add_removed_entry(key, &entry, *cause); } Self::handle_remove(deqs, entry, &mut eviction_state.counters); - } else if let Some(entry) = self.cache.get(key, hash) { + } else if let Some(entry) = self.cache.get(hash, |k| k == key) { if entry.last_modified().is_none() { deqs.move_to_back_ao(&entry); deqs.move_to_back_wo(&entry); @@ -1786,13 +1792,17 @@ where let kl = self.maybe_key_lock(&key); let _klg = &kl.as_ref().map(|kl| kl.lock()); - let maybe_entry = self.cache.remove_if(&key, hash, |_, v| { - if let Some(lm) = v.last_modified() { - lm == ts - } else { - false - } - }); + let maybe_entry = self.cache.remove_if( + hash, + |k| k == &key, + |_, v| { + if let Some(lm) = v.last_modified() { + lm == ts + } else { + false + } + }, + ); if let Some(entry) = maybe_entry { if eviction_state.is_notifier_enabled() { diff --git a/src/sync_base/invalidator.rs b/src/sync_base/invalidator.rs index ba120b0e..a0885a9c 100644 --- a/src/sync_base/invalidator.rs +++ b/src/sync_base/invalidator.rs @@ -32,16 +32,15 @@ pub(crate) type PredicateFun = Arc bool + Send + Sync + pub(crate) trait GetOrRemoveEntry { fn get_value_entry(&self, key: &Arc, hash: u64) -> Option>>; - fn remove_key_value_if( + fn remove_key_value_if( &self, key: &Arc, hash: u64, - condition: F, + condition: impl FnMut(&Arc, &TrioArc>) -> bool, ) -> Option>> where K: Send + Sync + 'static, - V: Clone + Send + Sync + 'static, - F: FnMut(&Arc, &TrioArc>) -> bool; + V: Clone + Send + Sync + 'static; } pub(crate) struct KeyDateLite { diff --git a/src/sync_base/key_lock.rs b/src/sync_base/key_lock.rs index 03dacfdf..7253273a 100644 --- a/src/sync_base/key_lock.rs +++ b/src/sync_base/key_lock.rs @@ -10,13 +10,15 @@ use triomphe::Arc as TrioArc; const LOCK_MAP_NUM_SEGMENTS: usize = 64; +type LockMap = SegmentedHashMap, TrioArc>, S>; + // We need the `where` clause here because of the Drop impl. pub(crate) struct KeyLock<'a, K, S> where - Arc: Eq + Hash, + K: Eq + Hash, S: BuildHasher, { - map: &'a SegmentedHashMap, TrioArc>, S>, + map: &'a LockMap, key: Arc, hash: u64, lock: TrioArc>, @@ -24,20 +26,23 @@ where impl<'a, K, S> Drop for KeyLock<'a, K, S> where - Arc: Eq + Hash, + K: Eq + Hash, S: BuildHasher, { fn drop(&mut self) { if TrioArc::count(&self.lock) <= 1 { - self.map - .remove_if(&self.key, self.hash, |_k, v| TrioArc::count(v) <= 1); + self.map.remove_if( + self.hash, + |k| k == &self.key, + |_k, v| TrioArc::count(v) <= 1, + ); } } } impl<'a, K, S> KeyLock<'a, K, S> where - Arc: Eq + Hash, + K: Eq + Hash, S: BuildHasher, { fn new(map: &'a LockMap, key: &Arc, hash: u64, lock: TrioArc>) -> Self { @@ -54,15 +59,13 @@ where } } -type LockMap = SegmentedHashMap, TrioArc>, S>; - pub(crate) struct KeyLockMap { locks: LockMap, } impl KeyLockMap where - Arc: Eq + Hash, + K: Eq + Hash, S: BuildHasher, { pub(crate) fn with_hasher(hasher: S) -> Self {