Skip to content

Commit 0198c59

Browse files
authored
Merge pull request #331 from tfreiberg-fastly/tfreiberg/entry-ref
Add (try_)entry_ref functions and EntryRef types
2 parents ec9f65c + a784833 commit 0198c59

File tree

4 files changed

+433
-0
lines changed

4 files changed

+433
-0
lines changed

src/lib.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ use crate::lock::RwLock;
2727

2828
#[cfg(feature = "raw-api")]
2929
pub use crate::lock::{RawRwLock, RwLock};
30+
use crate::mapref::entry_ref::EntryRef;
31+
use crate::mapref::entry_ref::OccupiedEntryRef;
32+
use crate::mapref::entry_ref::VacantEntryRef;
3033

3134
use cfg_if::cfg_if;
3235
use core::fmt;
@@ -889,6 +892,17 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
889892
self._try_entry(key)
890893
}
891894

895+
/// Advanced entry API that tries to mimic `hashbrown::HashMap::entry_ref`.
896+
/// See the documentation on `dashmap::mapref::entry_ref` for more details.
897+
///
898+
/// **Locking behaviour:** May deadlock if called when holding any sort of reference into the map.
899+
pub fn entry_ref<'q, Q>(&'a self, key: &'q Q) -> EntryRef<'a, 'q, K, Q, V>
900+
where
901+
Q: Hash + Equivalent<K>,
902+
{
903+
self._entry_ref(key)
904+
}
905+
892906
/// Advanced entry API that tries to mimic `std::collections::HashMap::try_reserve`.
893907
/// Tries to reserve capacity for at least `shard * additional`
894908
/// and may reserve more space to avoid frequent reallocations.
@@ -1182,6 +1196,69 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> DashMap<K, V, S>
11821196
}
11831197
}
11841198

1199+
fn _entry_ref<'q, Q>(&'a self, key: &'q Q) -> EntryRef<'a, 'q, K, Q, V>
1200+
where
1201+
Q: Hash + Equivalent<K>,
1202+
{
1203+
let hash = self.hash_u64(&key);
1204+
1205+
let idx = self.determine_shard(hash as usize);
1206+
1207+
let shard = self.shards[idx].write();
1208+
// SAFETY: The data will not outlive the guard, since we pass the guard to `Entry`.
1209+
let (guard, shard) = unsafe { RwLockWriteGuardDetached::detach_from(shard) };
1210+
1211+
match shard.entry(
1212+
hash,
1213+
|(k, _v)| key.equivalent(k),
1214+
|(k, _v)| {
1215+
let mut hasher = self.hasher.build_hasher();
1216+
k.hash(&mut hasher);
1217+
hasher.finish()
1218+
},
1219+
) {
1220+
hash_table::Entry::Occupied(entry) => {
1221+
EntryRef::Occupied(OccupiedEntryRef::new(guard, key, entry))
1222+
}
1223+
hash_table::Entry::Vacant(entry) => {
1224+
EntryRef::Vacant(VacantEntryRef::new(guard, key, entry))
1225+
}
1226+
}
1227+
}
1228+
1229+
fn _try_entry_ref<'q, Q>(&'a self, key: &'q Q) -> Option<EntryRef<'a, 'q, K, Q, V>>
1230+
where
1231+
Q: Hash + Equivalent<K>,
1232+
{
1233+
let hash = self.hash_u64(&key);
1234+
1235+
let idx = self.determine_shard(hash as usize);
1236+
1237+
let shard = match self.shards[idx].try_write() {
1238+
Some(shard) => shard,
1239+
None => return None,
1240+
};
1241+
// SAFETY: The data will not outlive the guard, since we pass the guard to `Entry`.
1242+
let (guard, shard) = unsafe { RwLockWriteGuardDetached::detach_from(shard) };
1243+
1244+
match shard.entry(
1245+
hash,
1246+
|(k, _v)| key.equivalent(k),
1247+
|(k, _v)| {
1248+
let mut hasher = self.hasher.build_hasher();
1249+
k.hash(&mut hasher);
1250+
hasher.finish()
1251+
},
1252+
) {
1253+
hash_table::Entry::Occupied(entry) => {
1254+
Some(EntryRef::Occupied(OccupiedEntryRef::new(guard, key, entry)))
1255+
}
1256+
hash_table::Entry::Vacant(entry) => {
1257+
Some(EntryRef::Vacant(VacantEntryRef::new(guard, key, entry)))
1258+
}
1259+
}
1260+
}
1261+
11851262
fn _clear(&self) {
11861263
self._retain(|_, _| false)
11871264
}

src/mapref/entry.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,42 @@ mod tests {
214214

215215
use super::*;
216216

217+
#[test]
218+
fn test_insert_into_vacant() {
219+
let map: DashMap<u32, u32> = DashMap::new();
220+
221+
let entry = map.entry(1);
222+
223+
assert!(matches!(entry, Entry::Vacant(_)));
224+
225+
let val = entry.insert(2);
226+
227+
assert_eq!(*val, 2);
228+
229+
drop(val);
230+
231+
assert_eq!(*map.get(&1).unwrap(), 2);
232+
}
233+
234+
#[test]
235+
fn test_insert_into_occupied() {
236+
let map: DashMap<u32, u32> = DashMap::new();
237+
238+
map.insert(1, 1000);
239+
240+
let entry = map.entry(1);
241+
242+
assert!(matches!(&entry, Entry::Occupied(entry) if *entry.get() == 1000));
243+
244+
let val = entry.insert(2);
245+
246+
assert_eq!(*val, 2);
247+
248+
drop(val);
249+
250+
assert_eq!(*map.get(&1).unwrap(), 2);
251+
}
252+
217253
#[test]
218254
fn test_insert_entry_into_vacant() {
219255
let map: DashMap<u32, u32> = DashMap::new();

0 commit comments

Comments
 (0)