From cf074afdc295dea283672b31e697a9113bd37abd Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Thu, 16 Jan 2025 23:13:14 +0800 Subject: [PATCH 1/4] Support `Equivalent` trait for the key type `K` --- Cargo.toml | 3 +- src/cht/segment.rs | 5 ++- src/future/base_cache.rs | 40 +++++++++------------ src/future/cache.rs | 50 ++++++++++----------------- src/future/entry_selector.rs | 7 ++-- src/future/value_initializer.rs | 12 +++---- src/lib.rs | 3 ++ src/sync/cache.rs | 61 +++++++++++++-------------------- src/sync/entry_selector.rs | 7 ++-- src/sync/segment.rs | 30 ++++++---------- src/sync/value_initializer.rs | 4 +-- src/sync_base/base_cache.rs | 49 ++++++++++---------------- 12 files changed, 111 insertions(+), 160 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2525e4e9..58696c63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ unstable-debug-counters = ["future", "once_cell"] crossbeam-channel = "0.5.5" crossbeam-epoch = "0.9.9" crossbeam-utils = "0.8" +equivalent = "1.0" parking_lot = "0.12" portable-atomic = "1.6" smallvec = "1.8" @@ -55,7 +56,7 @@ tagptr = "0.2" thiserror = "1.0" uuid = { version = "1.1", features = ["v4"] } -# Optional dependencies (enabled by default) +# Optional dependencies (quanta) quanta = { version = "0.12.2", optional = true } # Optional dependencies (future) diff --git a/src/cht/segment.rs b/src/cht/segment.rs index 7bd301bb..8a0bbec6 100644 --- a/src/cht/segment.rs +++ b/src/cht/segment.rs @@ -38,13 +38,13 @@ use crate::cht::map::{ use super::iter::{Iter, ScanningGet}; use std::{ - borrow::Borrow, hash::{BuildHasher, Hash}, ptr, sync::atomic::{self, AtomicUsize, Ordering}, }; use crossbeam_epoch::Atomic; +use equivalent::Equivalent; /// A lock-free hash map implemented with segmented bucket pointer arrays, open /// addressing, and linear probing. @@ -500,8 +500,7 @@ impl HashMap { #[inline] pub(crate) fn hash(&self, key: &Q) -> u64 where - Q: Hash + Eq + ?Sized, - K: Borrow, + Q: Equivalent + Hash + ?Sized, { bucket::hash(&self.build_hasher, key) } diff --git a/src/future/base_cache.rs b/src/future/base_cache.rs index cbec1bb1..f52ceebf 100644 --- a/src/future/base_cache.rs +++ b/src/future/base_cache.rs @@ -38,6 +38,7 @@ use common::concurrent::debug_counters::CacheDebugStats; use async_lock::{Mutex, MutexGuard, RwLock}; use crossbeam_channel::{Receiver, Sender, TrySendError}; use crossbeam_utils::atomic::AtomicCell; +use equivalent::Equivalent; use futures_util::future::BoxFuture; use smallvec::SmallVec; use std::{ @@ -215,16 +216,14 @@ where #[inline] pub(crate) fn hash(&self, key: &Q) -> u64 where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { self.inner.hash(key) } pub(crate) fn contains_key_with_hash(&self, key: &Q, hash: u64) -> bool where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { // TODO: Maybe we can just call ScanningGet::scanning_get. self.inner @@ -250,8 +249,7 @@ where record_read: bool, ) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, I: FnMut(&V) -> bool, { if self.is_map_disabled() { @@ -367,8 +365,7 @@ where pub(crate) fn get_key_with_hash(&self, key: &Q, hash: u64) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { self.inner .get_key_value_and(key, hash, |k, _entry| Arc::clone(k)) @@ -377,8 +374,7 @@ where #[inline] pub(crate) fn remove_entry(&self, key: &Q, hash: u64) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { self.inner.remove_entry(key, hash) } @@ -427,8 +423,8 @@ where } fn scanning_get(&self, key: &Arc) -> Option { - let hash = self.hash(key); - self.inner.get_key_value_and_then(key, hash, |k, entry| { + let hash = self.hash(&**key); + self.inner.get_key_value_and_then(&**key, hash, |k, entry| { let i = &self.inner; let (ttl, tti, va) = (&i.time_to_live(), &i.time_to_idle(), &i.valid_after()); let now = self.current_time(); @@ -1208,8 +1204,7 @@ where #[inline] fn hash(&self, key: &Q) -> u64 where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { let mut hasher = self.build_hasher.build_hasher(); key.hash(&mut hasher); @@ -1219,33 +1214,30 @@ where #[inline] fn get_key_value_and(&self, key: &Q, hash: u64, with_entry: F) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, F: FnOnce(&Arc, &MiniArc>) -> T, { self.cache - .get_key_value_and(hash, |k| (k as &K).borrow() == key, with_entry) + .get_key_value_and(hash, |k| key.equivalent(k as &K), with_entry) } #[inline] fn get_key_value_and_then(&self, key: &Q, hash: u64, with_entry: F) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, F: FnOnce(&Arc, &MiniArc>) -> Option, { self.cache - .get_key_value_and_then(hash, |k| (k as &K).borrow() == key, with_entry) + .get_key_value_and_then(hash, |k| key.equivalent(k as &K), with_entry) } #[inline] fn remove_entry(&self, key: &Q, hash: u64) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { self.cache - .remove_entry(hash, |k| (k as &K).borrow() == key) + .remove_entry(hash, |k| key.equivalent(k as &K)) .map(|(key, entry)| KvEntry::new(key, entry)) } @@ -2363,7 +2355,7 @@ where if !kd.is_dirty() { if let Some(ts) = kd.last_modified() { let key = kd.key(); - let hash = self.hash(key); + let hash = self.hash(&**key); candidates.push(KeyDateLite::new(key, hash, ts)); len += 1; } diff --git a/src/future/cache.rs b/src/future/cache.rs index 32325e93..313035fb 100644 --- a/src/future/cache.rs +++ b/src/future/cache.rs @@ -1,3 +1,5 @@ +use equivalent::Equivalent; + use super::{ base_cache::BaseCache, value_initializer::{InitResult, ValueInitializer}, @@ -16,7 +18,6 @@ use crate::{ use crate::common::concurrent::debug_counters::CacheDebugStats; use std::{ - borrow::Borrow, collections::hash_map::RandomState, fmt, future::Future, @@ -858,8 +859,7 @@ where /// on the borrowed form _must_ match those for the key type. pub fn contains_key(&self, key: &Q) -> bool where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { self.base.contains_key_with_hash(key, self.base.hash(key)) } @@ -876,8 +876,7 @@ where /// [rustdoc-std-arc]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html pub async fn get(&self, key: &Q) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { let ignore_if = None as Option<&mut fn(&V) -> bool>; @@ -961,8 +960,7 @@ where /// ``` pub fn entry_by_ref<'a, Q>(&'a self, key: &'a Q) -> RefKeyEntrySelector<'a, K, Q, V, S> where - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { let hash = self.base.hash(key); RefKeyEntrySelector::new(key, hash, self) @@ -1065,8 +1063,7 @@ where /// cache, the key will be cloned to create new entry in the cache. pub async fn get_with_by_ref(&self, key: &Q, init: impl Future) -> V where - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { futures_util::pin_mut!(init); let hash = self.base.hash(key); @@ -1204,8 +1201,7 @@ where pub async fn optionally_get_with_by_ref(&self, key: &Q, init: F) -> Option where F: Future>, - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { futures_util::pin_mut!(init); let hash = self.base.hash(key); @@ -1328,8 +1324,7 @@ where where F: Future>, E: Send + Sync + 'static, - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { futures_util::pin_mut!(init); let hash = self.base.hash(key); @@ -1356,8 +1351,7 @@ where /// on the borrowed form _must_ match those for the key type. pub async fn invalidate(&self, key: &Q) where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { let hash = self.base.hash(key); self.invalidate_with_hash(key, hash, false).await; @@ -1372,8 +1366,7 @@ where /// on the borrowed form _must_ match those for the key type. pub async fn remove(&self, key: &Q) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { let hash = self.base.hash(key); self.invalidate_with_hash(key, hash, true).await @@ -1542,7 +1535,7 @@ where ) -> Entry { let maybe_entry = self .base - .get_with_hash(&key, hash, replace_if.as_mut(), need_key, true) + .get_with_hash(&*key, hash, replace_if.as_mut(), need_key, true) .await; if let Some(entry) = maybe_entry { entry @@ -1561,8 +1554,7 @@ where need_key: bool, ) -> Entry where - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { let maybe_entry = self .base @@ -1616,7 +1608,7 @@ where ) -> Entry { match self .base - .get_with_hash(&key, hash, never_ignore(), true, true) + .get_with_hash(&*key, hash, never_ignore(), true, true) .await { Some(entry) => entry, @@ -1636,8 +1628,7 @@ where init: impl FnOnce() -> V, ) -> Entry where - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { match self .base @@ -1667,7 +1658,7 @@ where { let entry = self .base - .get_with_hash(&key, hash, never_ignore(), need_key, true) + .get_with_hash(&*key, hash, never_ignore(), need_key, true) .await; if entry.is_some() { return entry; @@ -1686,8 +1677,7 @@ where ) -> Option> where F: Future>, - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { let entry = self .base @@ -1748,7 +1738,7 @@ where { if let Some(entry) = self .base - .get_with_hash(&key, hash, never_ignore(), need_key, true) + .get_with_hash(&*key, hash, never_ignore(), need_key, true) .await { return Ok(entry); @@ -1768,8 +1758,7 @@ where where F: Future>, E: Send + Sync + 'static, - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { if let Some(entry) = self .base @@ -1939,8 +1928,7 @@ where need_value: bool, ) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { use futures_util::FutureExt; diff --git a/src/future/entry_selector.rs b/src/future/entry_selector.rs index b38d1c73..62a33f65 100644 --- a/src/future/entry_selector.rs +++ b/src/future/entry_selector.rs @@ -1,9 +1,10 @@ +use equivalent::Equivalent; + use crate::{ops::compute, Entry}; use super::Cache; use std::{ - borrow::Borrow, future::Future, hash::{BuildHasher, Hash}, sync::Arc, @@ -681,8 +682,8 @@ where impl<'a, K, Q, V, S> RefKeyEntrySelector<'a, K, Q, V, S> where - K: Borrow + Hash + Eq + Send + Sync + 'static, - Q: ToOwned + Hash + Eq + ?Sized, + K: Hash + Eq + Send + Sync + 'static, + Q: Equivalent + ToOwned + Hash + ?Sized, V: Clone + Send + Sync + 'static, S: BuildHasher + Clone + Send + Sync + 'static, { diff --git a/src/future/value_initializer.rs b/src/future/value_initializer.rs index 729dc8cd..506892f6 100644 --- a/src/future/value_initializer.rs +++ b/src/future/value_initializer.rs @@ -223,7 +223,7 @@ where // Check if the value has already been inserted by other thread. if let Some(value) = cache .base - .get_with_hash(c_key, c_hash, ignore_if.as_mut(), false, false) + .get_with_hash(&**c_key, c_hash, ignore_if.as_mut(), false, false) .await .map(Entry::into_value) { @@ -321,7 +321,7 @@ where let ignore_if = None as Option<&mut fn(&V) -> bool>; let maybe_entry = cache .base - .get_with_hash(&c_key, c_hash, ignore_if, true, true) + .get_with_hash(&*c_key, c_hash, ignore_if, true, true) .await; let maybe_value = if allow_nop { maybe_entry.as_ref().map(|ent| ent.value().clone()) @@ -384,7 +384,7 @@ where } } Op::Remove => { - let maybe_prev_v = cache.invalidate_with_hash(&c_key, c_hash, true).await; + let maybe_prev_v = cache.invalidate_with_hash(&*c_key, c_hash, true).await; if let Some(prev_v) = maybe_prev_v { crossbeam_epoch::pin().flush(); let entry = Entry::new(Some(c_key), prev_v, false, false); @@ -430,7 +430,7 @@ where let ignore_if = None as Option<&mut fn(&V) -> bool>; let maybe_entry = cache .base - .get_with_hash(&c_key, c_hash, ignore_if, true, true) + .get_with_hash(&*c_key, c_hash, ignore_if, true, true) .await; let maybe_value = maybe_entry.as_ref().map(|ent| ent.value().clone()); @@ -460,7 +460,7 @@ where let ignore_if = None as Option<&mut fn(&V) -> bool>; let maybe_entry = cache .base - .get_with_hash(&c_key, c_hash, ignore_if, true, true) + .get_with_hash(&*c_key, c_hash, ignore_if, true, true) .await; let maybe_value = if allow_nop { maybe_entry.as_ref().map(|ent| ent.value().clone()) @@ -522,7 +522,7 @@ where } } Op::Remove => { - let maybe_prev_v = cache.invalidate_with_hash(&c_key, c_hash, true).await; + let maybe_prev_v = cache.invalidate_with_hash(&*c_key, c_hash, true).await; if let Some(prev_v) = maybe_prev_v { crossbeam_epoch::pin().flush(); let entry = Entry::new(Some(c_key), prev_v, false, false); diff --git a/src/lib.rs b/src/lib.rs index 5766dcc7..57ead33b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -236,6 +236,9 @@ compile_error!( `moka` crate. Please update your dependencies in Cargo.toml" ); +// Reexport(s) +pub use equivalent; + #[cfg(feature = "future")] #[cfg_attr(docsrs, doc(cfg(feature = "future")))] pub mod future; diff --git a/src/sync/cache.rs b/src/sync/cache.rs index 5dd4ec74..2cbf19ea 100644 --- a/src/sync/cache.rs +++ b/src/sync/cache.rs @@ -22,8 +22,8 @@ use crate::{ }; use crossbeam_channel::{Sender, TrySendError}; +use equivalent::Equivalent; use std::{ - borrow::Borrow, collections::hash_map::RandomState, fmt, hash::{BuildHasher, Hash}, @@ -774,16 +774,14 @@ where /// on the borrowed form _must_ match those for the key type. pub fn contains_key(&self, key: &Q) -> bool where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { self.base.contains_key_with_hash(key, self.base.hash(key)) } pub(crate) fn contains_key_with_hash(&self, key: &Q, hash: u64) -> bool where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { self.base.contains_key_with_hash(key, hash) } @@ -800,8 +798,7 @@ where /// [rustdoc-std-arc]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html pub fn get(&self, key: &Q) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { self.base .get_with_hash(key, self.base.hash(key), false) @@ -810,8 +807,7 @@ where pub(crate) fn get_with_hash(&self, key: &Q, hash: u64, need_key: bool) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { self.base.get_with_hash(key, hash, need_key) } @@ -872,8 +868,7 @@ where /// ``` pub fn entry_by_ref<'a, Q>(&'a self, key: &'a Q) -> RefKeyEntrySelector<'a, K, Q, V, S> where - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { let hash = self.base.hash(key); RefKeyEntrySelector::new(key, hash, self) @@ -965,8 +960,7 @@ where /// cache, the key will be cloned to create new entry in the cache. pub fn get_with_by_ref(&self, key: &Q, init: impl FnOnce() -> V) -> V where - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { let hash = self.base.hash(key); let replace_if = None as Option bool>; @@ -1000,7 +994,7 @@ where need_key: bool, ) -> Entry { self.base - .get_with_hash_and_ignore_if(&key, hash, replace_if.as_mut(), need_key) + .get_with_hash_and_ignore_if(&*key, hash, replace_if.as_mut(), need_key) .unwrap_or_else(|| self.insert_with_hash_and_fun(key, hash, init, replace_if, need_key)) } @@ -1018,8 +1012,7 @@ where need_key: bool, ) -> Entry where - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { self.base .get_with_hash_and_ignore_if(key, hash, replace_if.as_mut(), need_key) @@ -1039,7 +1032,7 @@ where ) -> Entry { let get = || { self.base - .get_with_hash_without_recording(&key, hash, replace_if.as_mut()) + .get_with_hash_without_recording(&*key, hash, replace_if.as_mut()) }; let insert = |v| self.insert_with_hash(key.clone(), hash, v); @@ -1071,7 +1064,7 @@ where hash: u64, init: impl FnOnce() -> V, ) -> Entry { - match self.base.get_with_hash(&key, hash, true) { + match self.base.get_with_hash(&*key, hash, true) { Some(entry) => entry, None => { let value = init(); @@ -1088,8 +1081,7 @@ where init: impl FnOnce() -> V, ) -> Entry where - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { match self.base.get_with_hash(key, hash, true) { Some(entry) => entry, @@ -1203,8 +1195,7 @@ where pub fn optionally_get_with_by_ref(&self, key: &Q, init: F) -> Option where F: FnOnce() -> Option, - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { let hash = self.base.hash(key); self.get_or_optionally_insert_with_hash_by_ref_and_fun(key, hash, init, false) @@ -1221,7 +1212,7 @@ where where F: FnOnce() -> Option, { - let entry = self.get_with_hash(&key, hash, need_key); + let entry = self.get_with_hash(&*key, hash, need_key); if entry.is_some() { return entry; } @@ -1238,8 +1229,7 @@ where ) -> Option> where F: FnOnce() -> Option, - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { let entry = self.get_with_hash(key, hash, need_key); if entry.is_some() { @@ -1263,7 +1253,7 @@ where let get = || { let ignore_if = None as Option<&mut fn(&V) -> bool>; self.base - .get_with_hash_without_recording(&key, hash, ignore_if) + .get_with_hash_without_recording(&*key, hash, ignore_if) }; let insert = |v| self.insert_with_hash(key.clone(), hash, v); @@ -1396,8 +1386,7 @@ where where F: FnOnce() -> Result, E: Send + Sync + 'static, - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { let hash = self.base.hash(key); self.get_or_try_insert_with_hash_by_ref_and_fun(key, hash, init, false) @@ -1415,7 +1404,7 @@ where F: FnOnce() -> Result, E: Send + Sync + 'static, { - if let Some(entry) = self.get_with_hash(&key, hash, need_key) { + if let Some(entry) = self.get_with_hash(&*key, hash, need_key) { return Ok(entry); } @@ -1432,8 +1421,7 @@ where where F: FnOnce() -> Result, E: Send + Sync + 'static, - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { if let Some(entry) = self.get_with_hash(key, hash, false) { return Ok(entry); @@ -1457,7 +1445,7 @@ where let get = || { let ignore_if = None as Option<&mut fn(&V) -> bool>; self.base - .get_with_hash_without_recording(&key, hash, ignore_if) + .get_with_hash_without_recording(&*key, hash, ignore_if) }; let insert = |v| self.insert_with_hash(key.clone(), hash, v); @@ -1569,8 +1557,7 @@ where /// on the borrowed form _must_ match those for the key type. pub fn invalidate(&self, key: &Q) where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { let hash = self.base.hash(key); self.invalidate_with_hash(key, hash, false); @@ -1585,8 +1572,7 @@ where /// on the borrowed form _must_ match those for the key type. pub fn remove(&self, key: &Q) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { let hash = self.base.hash(key); self.invalidate_with_hash(key, hash, true) @@ -1594,8 +1580,7 @@ where pub(crate) fn invalidate_with_hash(&self, key: &Q, hash: u64, need_value: bool) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { // Lock the key for removal if blocking removal notification is enabled. let mut kl = None; diff --git a/src/sync/entry_selector.rs b/src/sync/entry_selector.rs index 2d156176..7e0b2143 100644 --- a/src/sync/entry_selector.rs +++ b/src/sync/entry_selector.rs @@ -1,9 +1,10 @@ +use equivalent::Equivalent; + use crate::{ops::compute, Entry}; use super::Cache; use std::{ - borrow::Borrow, hash::{BuildHasher, Hash}, sync::Arc, }; @@ -567,8 +568,8 @@ where impl<'a, K, Q, V, S> RefKeyEntrySelector<'a, K, Q, V, S> where - K: Borrow + Hash + Eq + Send + Sync + 'static, - Q: ToOwned + Hash + Eq + ?Sized, + K: Hash + Eq + Send + Sync + 'static, + Q: Equivalent + ToOwned + Hash + ?Sized, V: Clone + Send + Sync + 'static, S: BuildHasher + Clone + Send + Sync + 'static, { diff --git a/src/sync/segment.rs b/src/sync/segment.rs index 3a55378c..1b0d3cdd 100644 --- a/src/sync/segment.rs +++ b/src/sync/segment.rs @@ -1,3 +1,5 @@ +use equivalent::Equivalent; + use super::{cache::Cache, CacheBuilder, OwnedKeyEntrySelector, RefKeyEntrySelector}; use crate::common::concurrent::Weigher; use crate::common::time::Clock; @@ -10,7 +12,6 @@ use crate::{ }; use std::{ - borrow::Borrow, collections::hash_map::RandomState, fmt, hash::{BuildHasher, Hash, Hasher}, @@ -248,8 +249,7 @@ where /// on the borrowed form _must_ match those for the key type. pub fn contains_key(&self, key: &Q) -> bool where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { let hash = self.inner.hash(key); self.inner.select(hash).contains_key_with_hash(key, hash) @@ -267,8 +267,7 @@ where /// [rustdoc-std-arc]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html pub fn get(&self, key: &Q) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + Eq + ?Sized, { let hash = self.inner.hash(key); self.inner @@ -288,8 +287,7 @@ where pub fn entry_by_ref<'a, Q>(&'a self, key: &'a Q) -> RefKeyEntrySelector<'a, K, Q, V, S> where - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { let hash = self.inner.hash(key); let cache = self.inner.select(hash); @@ -340,8 +338,7 @@ where /// cache, the key will be cloned to create new entry in the cache. pub fn get_with_by_ref(&self, key: &Q, init: impl FnOnce() -> V) -> V where - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { let hash = self.inner.hash(key); let replace_if = None as Option bool>; @@ -405,8 +402,7 @@ where pub fn optionally_get_with_by_ref(&self, key: &Q, init: F) -> Option where F: FnOnce() -> Option, - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { let hash = self.inner.hash(key); self.inner @@ -451,8 +447,7 @@ where where F: FnOnce() -> Result, E: Send + Sync + 'static, - K: Borrow, - Q: ToOwned + Hash + Eq + ?Sized, + Q: Equivalent + ToOwned + Hash + ?Sized, { let hash = self.inner.hash(key); self.inner @@ -479,8 +474,7 @@ where /// on the borrowed form _must_ match those for the key type. pub fn invalidate(&self, key: &Q) where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { let hash = self.inner.hash(key); self.inner @@ -497,8 +491,7 @@ where /// on the borrowed form _must_ match those for the key type. pub fn remove(&self, key: &Q) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { let hash = self.inner.hash(key); self.inner @@ -762,8 +755,7 @@ where #[inline] fn hash(&self, key: &Q) -> u64 where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { let mut hasher = self.build_hasher.build_hasher(); key.hash(&mut hasher); diff --git a/src/sync/value_initializer.rs b/src/sync/value_initializer.rs index cf29b14a..42d02a1f 100644 --- a/src/sync/value_initializer.rs +++ b/src/sync/value_initializer.rs @@ -228,7 +228,7 @@ where let ignore_if = None as Option<&mut fn(&V) -> bool>; let maybe_entry = cache .base - .get_with_hash_and_ignore_if(&c_key, c_hash, ignore_if, true); + .get_with_hash_and_ignore_if(&*c_key, c_hash, ignore_if, true); let maybe_value = if allow_nop { maybe_entry.as_ref().map(|ent| ent.value().clone()) } else { @@ -286,7 +286,7 @@ where } } Op::Remove => { - let maybe_prev_v = cache.invalidate_with_hash(&c_key, c_hash, true); + let maybe_prev_v = cache.invalidate_with_hash(&*c_key, c_hash, true); if let Some(prev_v) = maybe_prev_v { crossbeam_epoch::pin().flush(); let entry = Entry::new(Some(c_key), prev_v, false, false); diff --git a/src/sync_base/base_cache.rs b/src/sync_base/base_cache.rs index 3ba2abf6..a42c20ce 100644 --- a/src/sync_base/base_cache.rs +++ b/src/sync_base/base_cache.rs @@ -32,6 +32,7 @@ use crate::{ use crossbeam_channel::{Receiver, Sender, TrySendError}; use crossbeam_utils::atomic::AtomicCell; +use equivalent::Equivalent; use parking_lot::{Mutex, RwLock}; use smallvec::SmallVec; use std::{ @@ -189,16 +190,14 @@ where #[inline] pub(crate) fn hash(&self, key: &Q) -> u64 where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { self.inner.hash(key) } pub(crate) fn contains_key_with_hash(&self, key: &Q, hash: u64) -> bool where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { // TODO: Maybe we can just call ScanningGet::scanning_get. self.inner @@ -217,8 +216,7 @@ where pub(crate) fn get_with_hash(&self, key: &Q, hash: u64, need_key: bool) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { // Define a closure to record a read op. let record = |op, now| { @@ -237,8 +235,7 @@ where need_key: bool, ) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, I: FnMut(&V) -> bool, { // Define a closure to record a read op. @@ -256,8 +253,7 @@ where ignore_if: Option<&mut I>, ) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, I: FnMut(&V) -> bool, { // Define a closure that skips to record a read op. @@ -275,8 +271,7 @@ where need_key: bool, ) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, R: Fn(ReadOp, Instant), I: FnMut(&V) -> bool, { @@ -376,8 +371,7 @@ where pub(crate) fn get_key_with_hash(&self, key: &Q, hash: u64) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { self.inner .get_key_value_and(key, hash, |k, _entry| Arc::clone(k)) @@ -386,8 +380,7 @@ where #[inline] pub(crate) fn remove_entry(&self, key: &Q, hash: u64) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { self.inner.remove_entry(key, hash) } @@ -436,8 +429,8 @@ where } fn scanning_get(&self, key: &Arc) -> Option { - let hash = self.hash(key); - self.inner.get_key_value_and_then(key, hash, |k, entry| { + let hash = self.hash(&**key); + self.inner.get_key_value_and_then(&**key, hash, |k, entry| { let i = &self.inner; let (ttl, tti, va) = (&i.time_to_live(), &i.time_to_idle(), &i.valid_after()); let now = self.current_time(); @@ -1067,8 +1060,7 @@ where #[inline] fn hash(&self, key: &Q) -> u64 where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { let mut hasher = self.build_hasher.build_hasher(); key.hash(&mut hasher); @@ -1078,33 +1070,30 @@ where #[inline] fn get_key_value_and(&self, key: &Q, hash: u64, with_entry: F) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, F: FnOnce(&Arc, &MiniArc>) -> T, { self.cache - .get_key_value_and(hash, |k| (k as &K).borrow() == key, with_entry) + .get_key_value_and(hash, |k| key.equivalent(k as &K), with_entry) } #[inline] fn get_key_value_and_then(&self, key: &Q, hash: u64, with_entry: F) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, F: FnOnce(&Arc, &MiniArc>) -> Option, { self.cache - .get_key_value_and_then(hash, |k| (k as &K).borrow() == key, with_entry) + .get_key_value_and_then(hash, |k| key.equivalent(k as &K), with_entry) } #[inline] fn remove_entry(&self, key: &Q, hash: u64) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Equivalent + Hash + ?Sized, { self.cache - .remove_entry(hash, |k| (k as &K).borrow() == key) + .remove_entry(hash, |k| key.equivalent(k as &K)) .map(|(key, entry)| KvEntry::new(key, entry)) } @@ -2168,7 +2157,7 @@ where if !kd.is_dirty() { if let Some(ts) = kd.last_modified() { let key = kd.key(); - let hash = self.hash(key); + let hash = self.hash(&**key); candidates.push(KeyDateLite::new(key, hash, ts)); len += 1; } From 4c092d5a94fc8b0a8a24966beb8beab828cc2995 Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Thu, 16 Jan 2025 23:41:23 +0800 Subject: [PATCH 2/4] Bump the version to v0.12.11 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 58696c63..56940789 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "moka" -version = "0.12.10" +version = "0.12.11" edition = "2021" # Rust 1.70 was released on June 1, 2023. rust-version = "1.70" From b764a14df2a959fb4f6e415240c2e3feeb3971e8 Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Fri, 17 Jan 2025 06:40:47 +0800 Subject: [PATCH 3/4] Update the change log --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 152e9ffe..65984a6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Moka Cache — Change Log +## Version 0.12.11 + +### Added + +- Support `Equivalent` trait for the key type `K` of the caches. + ([#492][gh-pull-0492]) + + ## Version 0.12.10 ### Changed @@ -959,6 +967,7 @@ The minimum supported Rust version (MSRV) is now 1.51.0 (Mar 25, 2021). [gh-issue-0034]: https://github.com/moka-rs/moka/issues/34/ [gh-issue-0031]: https://github.com/moka-rs/moka/issues/31/ +[gh-pull-0492]: https://github.com/moka-rs/moka/pull/492/ [gh-pull-0482]: https://github.com/moka-rs/moka/pull/482/ [gh-pull-0481]: https://github.com/moka-rs/moka/pull/481/ [gh-pull-0480]: https://github.com/moka-rs/moka/pull/480/ From fd68e8046368800881fe4faae6972e98649c8eb9 Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Sat, 18 Jan 2025 20:51:35 +0800 Subject: [PATCH 4/4] Reexport the `Equivalent` trait instead of the whole `equivalet` module --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 57ead33b..51f74c5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -237,7 +237,7 @@ compile_error!( ); // Reexport(s) -pub use equivalent; +pub use equivalent::Equivalent; #[cfg(feature = "future")] #[cfg_attr(docsrs, doc(cfg(feature = "future")))]