From 5cd3f349fb93da009999ec6e9472dfe0647d278a Mon Sep 17 00:00:00 2001 From: JustForFun88 <100504524+JustForFun88@users.noreply.github.com> Date: Sun, 15 May 2022 15:05:22 +0500 Subject: [PATCH 1/3] Adding `map-inner` feature for getting access to map fields --- Cargo.toml | 1 + src/map.rs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 2a6b180018..1b28676419 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ rustc-dep-of-std = [ "rustc-internal-api", ] raw = [] +map-inner = ["raw"] # Enables usage of `#[inline]` on far more functions than by default in this # crate. This may lead to a performance increase but often comes at a compile diff --git a/src/map.rs b/src/map.rs index 4f1baebf15..f31be3b7e9 100644 --- a/src/map.rs +++ b/src/map.rs @@ -186,8 +186,15 @@ pub enum DefaultHashBuilder {} /// // use the values stored in map /// ``` pub struct HashMap { + #[cfg(any(feature = "rustc-internal-api", not(feature = "map-inner")))] pub(crate) hash_builder: S, + #[cfg(any(feature = "rustc-internal-api", not(feature = "map-inner")))] pub(crate) table: RawTable<(K, V), A>, + + #[cfg(all(feature = "map-inner", not(feature = "rustc-internal-api")))] + pub hash_builder: S, + #[cfg(all(feature = "map-inner", not(feature = "rustc-internal-api")))] + pub table: RawTable<(K, V), A>, } impl Clone for HashMap { From cbe6d15234e7be268adae1247a713877f168a4d8 Mon Sep 17 00:00:00 2001 From: JustForFun88 <100504524+JustForFun88@users.noreply.github.com> Date: Tue, 17 May 2022 07:24:35 +0500 Subject: [PATCH 2/3] Using function instead of feature --- Cargo.toml | 1 - src/map.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1b28676419..2a6b180018 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,6 @@ rustc-dep-of-std = [ "rustc-internal-api", ] raw = [] -map-inner = ["raw"] # Enables usage of `#[inline]` on far more functions than by default in this # crate. This may lead to a performance increase but often comes at a compile diff --git a/src/map.rs b/src/map.rs index f31be3b7e9..5d176d869d 100644 --- a/src/map.rs +++ b/src/map.rs @@ -186,15 +186,8 @@ pub enum DefaultHashBuilder {} /// // use the values stored in map /// ``` pub struct HashMap { - #[cfg(any(feature = "rustc-internal-api", not(feature = "map-inner")))] pub(crate) hash_builder: S, - #[cfg(any(feature = "rustc-internal-api", not(feature = "map-inner")))] pub(crate) table: RawTable<(K, V), A>, - - #[cfg(all(feature = "map-inner", not(feature = "rustc-internal-api")))] - pub hash_builder: S, - #[cfg(all(feature = "map-inner", not(feature = "rustc-internal-api")))] - pub table: RawTable<(K, V), A>, } impl Clone for HashMap { @@ -2019,6 +2012,67 @@ impl HashMap { pub fn raw_entry(&self) -> RawEntryBuilder<'_, K, V, S, A> { RawEntryBuilder { map: self } } + + /// Returns a mutable reference to the [`RawTable`] used underneath [`HashMap`]. + /// This function is only available if the `raw` feature of the crate is enabled. + /// + /// # Note + /// + /// Calling the function safe, but using raw hash table API's may require + /// unsafe functions or blocks. + /// + /// `RawTable` API gives the lowest level of control under the map that can be useful + /// for extending the HashMap's API, but may lead to *[undefined behavior]*. + /// + /// [`HashMap`]: struct.HashMap.html + /// [`RawTable`]: raw/struct.RawTable.html + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// + /// # Examples + /// + /// ``` + /// use core::hash::{BuildHasher, Hash}; + /// use hashbrown::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.extend([("a", 10), ("b", 20), ("c", 30)]); + /// assert_eq!(map.len(), 3); + /// + /// // Let's imagine that we have a value and a hash of the key, but not the key itself. + /// // However, if you want to remove the value from the map by hash and value, and you + /// // know exactly that the value is unique, then you can create a function like this: + /// fn remove_by_hash( + /// map: &mut HashMap, + /// hash: u64, + /// is_match: F, + /// ) -> Option<(K, V)> + /// where + /// F: Fn(&K, &V) -> bool, + /// { + /// let raw_table = map.raw_table(); + /// match raw_table.find(hash, |(k, v)| is_match(k, v)) { + /// Some(bucket) => Some(unsafe { raw_table.remove(bucket) }), + /// None => None, + /// } + /// } + /// + /// fn compute_hash(hash_builder: &S, key: &K) -> u64 { + /// use core::hash::Hasher; + /// let mut state = hash_builder.build_hasher(); + /// key.hash(&mut state); + /// state.finish() + /// } + /// + /// let hash = compute_hash(map.hasher(), "a"); + /// assert_eq!(remove_by_hash(&mut map, hash, |_, v| *v == 10), Some(("a", 10))); + /// assert_eq!(map.get(&"a"), None); + /// assert_eq!(map.len(), 2); + /// ``` + #[cfg(feature = "raw")] + #[cfg_attr(feature = "inline-more", inline)] + pub fn raw_table(&mut self) -> &mut RawTable<(K, V), A> { + &mut self.table + } } impl PartialEq for HashMap From c773eb2e8f23a076b84c1fb19db6c519b7f1dc8a Mon Sep 17 00:00:00 2001 From: JustForFun88 <100504524+JustForFun88@users.noreply.github.com> Date: Tue, 17 May 2022 10:02:21 +0500 Subject: [PATCH 3/3] Making example more ergonomic --- src/map.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/map.rs b/src/map.rs index 5d176d869d..a7d3fa125d 100644 --- a/src/map.rs +++ b/src/map.rs @@ -2047,10 +2047,10 @@ impl HashMap { /// is_match: F, /// ) -> Option<(K, V)> /// where - /// F: Fn(&K, &V) -> bool, + /// F: Fn(&(K, V)) -> bool, /// { /// let raw_table = map.raw_table(); - /// match raw_table.find(hash, |(k, v)| is_match(k, v)) { + /// match raw_table.find(hash, is_match) { /// Some(bucket) => Some(unsafe { raw_table.remove(bucket) }), /// None => None, /// } @@ -2064,7 +2064,7 @@ impl HashMap { /// } /// /// let hash = compute_hash(map.hasher(), "a"); - /// assert_eq!(remove_by_hash(&mut map, hash, |_, v| *v == 10), Some(("a", 10))); + /// assert_eq!(remove_by_hash(&mut map, hash, |(_, v)| *v == 10), Some(("a", 10))); /// assert_eq!(map.get(&"a"), None); /// assert_eq!(map.len(), 2); /// ```