diff --git a/Cargo.lock b/Cargo.lock index 1310ca3..dc0b28b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "autocfg" version = "1.4.0" @@ -62,6 +68,18 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "gimli" version = "0.31.1" @@ -70,9 +88,14 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "hermit-abi" diff --git a/Cargo.toml b/Cargo.toml index 35fc1e8..4afa35e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ categories = ["asynchronous", "concurrency", "data-structures"] [dependencies] crossbeam-utils = "0.8.20" -hashbrown = { version = "0.14.5", default-features = false, features = ["raw"] } +hashbrown = { version = "0.15.0" } [dev-dependencies] tokio = { version = "1.41.0", features = ["full"] } diff --git a/src/shard.rs b/src/shard.rs index 732ebff..0489d05 100644 --- a/src/shard.rs +++ b/src/shard.rs @@ -4,7 +4,7 @@ mod futures; use futures::{Read, Write}; -pub(crate) type Inner = hashbrown::raw::RawTable<(K, V)>; +pub(crate) type Inner = hashbrown::HashTable<(K, V)>; pub(crate) type ShardReader<'a, K, V> = RwLockReadGuard<'a, Inner>; pub(crate) type ShardWriter<'a, K, V> = RwLockWriteGuard<'a, Inner>; diff --git a/src/shard_map.rs b/src/shard_map.rs index bfe854d..18dddd1 100644 --- a/src/shard_map.rs +++ b/src/shard_map.rs @@ -19,11 +19,12 @@ //! }); //! ``` use std::{ - hash::{BuildHasher, Hasher, RandomState}, + hash::{BuildHasher, RandomState}, sync::{Arc, OnceLock}, }; use crossbeam_utils::CachePadded; +use hashbrown::hash_table::Entry; use crate::{ mapref::{MapRef, MapRefMut}, @@ -31,9 +32,9 @@ use crate::{ }; struct Inner { - shift: usize, shards: Box<[CachePadded>]>, hasher: S, + shift: usize, } impl std::ops::Deref for Inner { @@ -170,14 +171,6 @@ where } } - fn hash_u64(&self, k: &K) -> u64 { - let mut hasher = self.inner.hasher.build_hasher(); - - k.hash(&mut hasher); - - hasher.finish() - } - #[inline] fn shard_for_hash(&self, hash: usize) -> usize { // 7 high bits for the HashBrown simd tag @@ -186,7 +179,7 @@ where #[inline] fn shard(&self, key: &K) -> (&CachePadded>, u64) { - let hash = self.hash_u64(key); + let hash = self.inner.hasher.hash_one(key); let shard_idx = self.shard_for_hash(hash as usize); @@ -213,19 +206,19 @@ where let (shard, hash) = self.shard(&key); let mut writer = shard.write().await; - let (old, slot) = match writer.find_or_find_insert_slot( + let (old, slot) = match writer.entry( hash, |(k, _)| k == &key, - |(k, _)| self.hash_u64(k), + |(k, _)| self.inner.hasher.hash_one(k), ) { - Ok(bucket) => { - let ((_, old), slot) = unsafe { writer.remove(bucket) }; + Entry::Occupied(entry) => { + let ((_, old), slot) = entry.remove(); (Some(old), slot) } - Err(slot) => (None, slot), + Entry::Vacant(slot) => (None, slot), }; - unsafe { writer.insert_in_slot(hash, slot, (key, value)) }; + slot.insert((key, value)); old } @@ -255,7 +248,7 @@ where let (shard, hash) = self.shard(key); let reader = shard.read().await; - if let Some((k, v)) = reader.get(hash, |(k, _)| k == key) { + if let Some((k, v)) = reader.find(hash, |(k, _)| k == key) { let (k, v) = (k as *const K, v as *const V); // SAFETY: The key and value are guaranteed to be valid for the lifetime of the reader. unsafe { Some(MapRef::new(reader, &*k, &*v)) } @@ -293,7 +286,7 @@ where let (shard, hash) = self.shard(key); let mut writer = shard.write().await; - if let Some((k, v)) = writer.get_mut(hash, |(k, _)| k == key) { + if let Some((k, v)) = writer.find_mut(hash, |(k, _)| k == key) { let (k, v) = (k as *const K, v as *mut V); // SAFETY: The key and value are guaranteed to be valid for the lifetime of the writer. unsafe { Some(MapRefMut::new(writer, &*k, &mut *v)) } @@ -356,8 +349,11 @@ where pub async fn remove(&self, key: &K) -> Option { let (shard, hash) = self.shard(key); - match shard.write().await.remove_entry(hash, |(k, _)| k == key) { - Some((_, v)) => Some(v), + match shard.write().await.find_entry(hash, |(k, _)| k == key) { + Ok(occupied) => { + let ((_, v), _) = occupied.remove(); + Some(v) + } _ => None, } }