diff --git a/Cargo.lock b/Cargo.lock index 6befc1f1..eb890b34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,6 +58,7 @@ dependencies = [ "arbitrary", "cfg-if", "crossbeam-utils", + "equivalent", "hashbrown", "lock_api", "once_cell", @@ -73,6 +74,12 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "hashbrown" version = "0.15.2" diff --git a/Cargo.toml b/Cargo.toml index b449b15c..44f0ab5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "dashmap" version = "6.1.0" authors = ["Joel Wejdenstål "] edition = "2021" -rust-version = "1.65" +rust-version = "1.70" license = "MIT" repository = "https://github.com/xacrimon/dashmap" homepage = "https://github.com/xacrimon/dashmap" @@ -22,6 +22,7 @@ inline = ["hashbrown/inline-more"] [dependencies] lock_api = "0.4.12" parking_lot_core = "0.9.10" +equivalent = "1.0.1" hashbrown = { version = "0.15.2", default-features = false } serde = { version = "1.0.217", optional = true, features = ["derive"] } cfg-if = "1.0.0" diff --git a/README.md b/README.md index 2eb721eb..01f476cd 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,16 @@ This allows you to put a DashMap in an `Arc` and share it between threads whi DashMap puts great effort into performance and aims to be as fast as possible. If you have any suggestions or tips do not hesitate to open an issue or a PR. +The current MSRV is 1.70 and is not changed in patch releases. You can pin a minor version if you want +perfect stability. Though `dashmap` always stays at least 15 full stable releases (or about 9 months) behind the current stable release. + [![version](https://img.shields.io/crates/v/dashmap)](https://crates.io/crates/dashmap) [![documentation](https://docs.rs/dashmap/badge.svg)](https://docs.rs/dashmap) [![downloads](https://img.shields.io/crates/d/dashmap)](https://crates.io/crates/dashmap) -[![minimum rustc version](https://img.shields.io/badge/rustc-1.65-orange.svg)](https://crates.io/crates/dashmap) +[![minimum rustc version](https://img.shields.io/badge/rustc-1.70-orange.svg)](https://crates.io/crates/dashmap) ## Cargo features diff --git a/rust-toolchain.toml b/rust-toolchain.toml index a32876aa..191a4a4c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.65" +channel = "1.70" components = ["rustfmt", "clippy"] profile = "minimal" diff --git a/src/lib.rs b/src/lib.rs index cb3bc882..aac40a19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,12 +29,12 @@ use crate::lock::RwLock; pub use crate::lock::{RawRwLock, RwLock}; use cfg_if::cfg_if; -use core::borrow::Borrow; use core::fmt; use core::hash::{BuildHasher, Hash, Hasher}; use core::iter::FromIterator; use core::ops::{BitAnd, BitOr, Shl, Shr, Sub}; use crossbeam_utils::CachePadded; +pub use equivalent::Equivalent; use hashbrown::hash_table; use iter::{Iter, IterMut, OwningIter}; use lock::{RwLockReadGuardDetached, RwLockWriteGuardDetached}; @@ -389,8 +389,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap { /// ``` pub fn determine_map(&self, key: &Q) -> usize where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { let hash = self.hash_usize(&key); self.determine_shard(hash) @@ -508,8 +507,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap { /// ``` pub fn remove(&self, key: &Q) -> Option<(K, V)> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self._remove(key) } @@ -537,16 +535,14 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap { /// ``` pub fn remove_if(&self, key: &Q, f: impl FnOnce(&K, &V) -> bool) -> Option<(K, V)> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self._remove_if(key, f) } pub fn remove_if_mut(&self, key: &Q, f: impl FnOnce(&K, &mut V) -> bool) -> Option<(K, V)> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self._remove_if_mut(key, f) } @@ -601,8 +597,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap { /// ``` pub fn get(&'a self, key: &Q) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self._get(key) } @@ -623,8 +618,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap { /// ``` pub fn get_mut(&'a self, key: &Q) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self._get_mut(key) } @@ -650,8 +644,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap { /// ``` pub fn try_get(&'a self, key: &Q) -> TryResult> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self._try_get(key) } @@ -678,8 +671,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap { /// ``` pub fn try_get_mut(&'a self, key: &Q) -> TryResult> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self._try_get_mut(key) } @@ -806,8 +798,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap { /// If the given closure panics, then `alter` will abort the process pub fn alter(&self, key: &Q, f: impl FnOnce(&K, V) -> V) where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self._alter(key, f); } @@ -857,8 +848,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap { /// If the given closure panics, then `view` will abort the process pub fn view(&self, key: &Q, f: impl FnOnce(&K, &V) -> R) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self._view(key, f) } @@ -878,8 +868,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap { /// ``` pub fn contains_key(&self, key: &Q) -> bool where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self._contains_key(key) } @@ -936,8 +925,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap fn _remove(&self, key: &Q) -> Option<(K, V)> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { let hash = self.hash_u64(&key); @@ -945,7 +933,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap let mut shard = self.shards[idx].write(); - if let Ok(entry) = shard.find_entry(hash, |(k, _v)| key == k.borrow()) { + if let Ok(entry) = shard.find_entry(hash, |(k, _v)| key.equivalent(k)) { let ((k, v), _) = entry.remove(); Some((k, v)) } else { @@ -955,8 +943,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap fn _remove_if(&self, key: &Q, f: impl FnOnce(&K, &V) -> bool) -> Option<(K, V)> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { let hash = self.hash_u64(&key); @@ -964,7 +951,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap let mut shard = self.shards[idx].write(); - if let Ok(entry) = shard.find_entry(hash, |(k, _v)| key == k.borrow()) { + if let Ok(entry) = shard.find_entry(hash, |(k, _v)| key.equivalent(k)) { let (k, v) = entry.get(); if f(k, v) { let ((k, v), _) = entry.remove(); @@ -979,8 +966,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap fn _remove_if_mut(&self, key: &Q, f: impl FnOnce(&K, &mut V) -> bool) -> Option<(K, V)> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { let hash = self.hash_u64(&key); @@ -988,7 +974,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap let mut shard = self.shards[idx].write(); - if let Ok(mut entry) = shard.find_entry(hash, |(k, _v)| key == k.borrow()) { + if let Ok(mut entry) = shard.find_entry(hash, |(k, _v)| key.equivalent(k)) { let (k, v) = entry.get_mut(); if f(k, v) { let ((k, v), _) = entry.remove(); @@ -1011,8 +997,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap fn _get(&'a self, key: &Q) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { let hash = self.hash_u64(&key); @@ -1022,7 +1007,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap // SAFETY: The data will not outlive the guard, since we pass the guard to `Ref`. let (guard, shard) = unsafe { RwLockReadGuardDetached::detach_from(shard) }; - if let Some((k, v)) = shard.find(hash, |(k, _v)| key == k.borrow()) { + if let Some((k, v)) = shard.find(hash, |(k, _v)| key.equivalent(k)) { Some(Ref::new(guard, k, v)) } else { None @@ -1031,8 +1016,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap fn _get_mut(&'a self, key: &Q) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { let hash = self.hash_u64(&key); @@ -1042,7 +1026,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap // SAFETY: The data will not outlive the guard, since we pass the guard to `RefMut`. let (guard, shard) = unsafe { RwLockWriteGuardDetached::detach_from(shard) }; - if let Some((k, v)) = shard.find_mut(hash, |(k, _v)| key == k.borrow()) { + if let Some((k, v)) = shard.find_mut(hash, |(k, _v)| key.equivalent(k)) { Some(RefMut::new(guard, k, v)) } else { None @@ -1051,8 +1035,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap fn _try_get(&'a self, key: &Q) -> TryResult> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { let hash = self.hash_u64(&key); @@ -1065,7 +1048,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap // SAFETY: The data will not outlive the guard, since we pass the guard to `Ref`. let (guard, shard) = unsafe { RwLockReadGuardDetached::detach_from(shard) }; - if let Some((k, v)) = shard.find(hash, |(k, _v)| key == k.borrow()) { + if let Some((k, v)) = shard.find(hash, |(k, _v)| key.equivalent(k)) { TryResult::Present(Ref::new(guard, k, v)) } else { TryResult::Absent @@ -1074,8 +1057,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap fn _try_get_mut(&'a self, key: &Q) -> TryResult> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { let hash = self.hash_u64(&key); @@ -1088,7 +1070,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap // SAFETY: The data will not outlive the guard, since we pass the guard to `RefMut`. let (guard, shard) = unsafe { RwLockWriteGuardDetached::detach_from(shard) }; - if let Some((k, v)) = shard.find_mut(hash, |(k, _v)| key == k.borrow()) { + if let Some((k, v)) = shard.find_mut(hash, |(k, _v)| key.equivalent(k)) { TryResult::Present(RefMut::new(guard, k, v)) } else { TryResult::Absent @@ -1123,8 +1105,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap fn _alter(&self, key: &Q, f: impl FnOnce(&K, V) -> V) where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { if let Some(mut r) = self.get_mut(key) { util::map_in_place_2(r.pair_mut(), f); @@ -1138,8 +1119,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap fn _view(&self, key: &Q, f: impl FnOnce(&K, &V) -> R) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self.get(key).map(|r| { let (k, v) = r.pair(); @@ -1208,8 +1188,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap fn _contains_key(&'a self, key: &Q) -> bool where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self._get(key).is_some() } @@ -1245,8 +1224,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> Shl<(K, V)> for &'a D impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone, Q> Shr<&Q> for &'a DashMap where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { type Output = Ref<'a, K, V>; @@ -1257,8 +1235,7 @@ where impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone, Q> BitOr<&Q> for &'a DashMap where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { type Output = RefMut<'a, K, V>; @@ -1269,8 +1246,7 @@ where impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone, Q> Sub<&Q> for &'a DashMap where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { type Output = Option<(K, V)>; @@ -1281,8 +1257,7 @@ where impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone, Q> BitAnd<&Q> for &'a DashMap where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { type Output = bool; diff --git a/src/read_only.rs b/src/read_only.rs index 5fff7451..f31af489 100644 --- a/src/read_only.rs +++ b/src/read_only.rs @@ -1,10 +1,10 @@ use crate::lock::RwLock; use crate::{DashMap, HashMap}; use cfg_if::cfg_if; -use core::borrow::Borrow; use core::fmt; use core::hash::{BuildHasher, Hash}; use crossbeam_utils::CachePadded; +use equivalent::Equivalent; use std::collections::hash_map::RandomState; /// A read-only view into a `DashMap`. Allows to obtain raw references to the stored values. @@ -58,8 +58,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView /// Returns `true` if the map contains a value for the specified key. pub fn contains_key(&'a self, key: &Q) -> bool where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self.get(key).is_some() } @@ -67,8 +66,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView /// Returns a reference to the value corresponding to the key. pub fn get(&'a self, key: &Q) -> Option<&'a V> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self.get_key_value(key).map(|(_k, v)| v) } @@ -76,8 +74,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView /// Returns the key-value pair corresponding to the supplied key. pub fn get_key_value(&'a self, key: &Q) -> Option<(&'a K, &'a V)> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { let hash = self.map.hash_u64(&key); @@ -87,7 +84,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView let shard = unsafe { &*shard.data_ptr() }; shard - .find(hash, |(k, _v)| key == k.borrow()) + .find(hash, |(k, _v)| key.equivalent(k)) .map(|(k, v)| (k, v)) } diff --git a/src/set.rs b/src/set.rs index 18af9117..0fbd1c53 100644 --- a/src/set.rs +++ b/src/set.rs @@ -6,12 +6,12 @@ use crate::DashMap; #[cfg(feature = "raw-api")] use crate::HashMap; use cfg_if::cfg_if; -use core::borrow::Borrow; use core::fmt; use core::hash::{BuildHasher, Hash}; use core::iter::FromIterator; #[cfg(feature = "raw-api")] use crossbeam_utils::CachePadded; +use equivalent::Equivalent; use std::collections::hash_map::RandomState; /// DashSet is a thin wrapper around [`DashMap`] using `()` as the value type. It uses @@ -163,8 +163,7 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet { /// ``` pub fn determine_map(&self, key: &Q) -> usize where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self.inner.determine_map(key) } @@ -220,8 +219,7 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet { /// ``` pub fn remove(&self, key: &Q) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self.inner.remove(key).map(|(k, _)| k) } @@ -247,8 +245,7 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet { /// ``` pub fn remove_if(&self, key: &Q, f: impl FnOnce(&K) -> bool) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { // TODO: Don't create another closure around f self.inner.remove_if(key, |k, _| f(k)).map(|(k, _)| k) @@ -284,8 +281,7 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet { /// ``` pub fn get(&'a self, key: &Q) -> Option> where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self.inner.get(key).map(Ref::new) } @@ -380,8 +376,7 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet { /// ``` pub fn contains(&self, key: &Q) -> bool where - K: Borrow, - Q: Hash + Eq + ?Sized, + Q: Hash + Equivalent + ?Sized, { self.inner.contains_key(key) }