From 94d79d98df8bdb572511b4f052ca9d0bea0e890e Mon Sep 17 00:00:00 2001 From: "Heinz N. Gies" Date: Mon, 6 Jan 2025 10:41:23 +0100 Subject: [PATCH] Update hashbrown and adjust API Signed-off-by: Heinz N. Gies --- Cargo.toml | 4 +- benches/compare.rs | 2 +- src/entry.rs | 78 +++++++++++++-------------- src/iter.rs | 22 +++++--- src/lib.rs | 45 ++++++++-------- src/raw_entry.rs | 8 +-- src/vecmap/entry.rs | 126 +++++++++++++++++++++++--------------------- 7 files changed, 151 insertions(+), 134 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b8ffd42..57364cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,8 @@ repository = "https://github.com/Licenser/halfbrown" version = "0.2.5" [dependencies] -hashbrown = "0.14" -rustc-hash = { version = "1.1", optional = true } +hashbrown = "0.15" +rustc-hash = { version = "2.1", optional = true } serde = { version = "1", default-features = false, optional = true } arrayvec = { version = "0.7", optional = true } [dev-dependencies] diff --git a/benches/compare.rs b/benches/compare.rs index 2fd8d72..6fac128 100644 --- a/benches/compare.rs +++ b/benches/compare.rs @@ -230,7 +230,7 @@ fn bench_group(b: &mut Criterion, name: &str, bench_input: BenchInput) { |data| { let mut m = halfbrown::HashMap::with_capacity(bench_input.initial_cap); for e in data { - m.insert_nocheck(e, e); + unsafe { m.insert_nocheck(e, e) }; } }, BatchSize::SmallInput, diff --git a/src/entry.rs b/src/entry.rs index 1554baa..e4679fa 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -438,57 +438,57 @@ impl<'a, K, V, const N: usize, S> OccupiedEntry<'a, K, V, N, S> { /// # Examples /// /// ``` - /// use hashbrown::hash_map::{Entry, HashMap}; + /// use halfbrown::{Entry, HashMap}; /// use std::rc::Rc; /// - /// let mut map: HashMap, u32> = HashMap::new(); - /// map.insert(Rc::new("Stringthing".to_string()), 15); - /// - /// let my_key = Rc::new("Stringthing".to_string()); + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.insert("poneyland", 42); + /// + /// let entry = match map.entry("poneyland") { + /// Entry::Occupied(e) => { + /// e.replace_entry_with(|k, v| { + /// assert_eq!(k, &"poneyland"); + /// assert_eq!(v, 42); + /// Some(v + 1) + /// }) + /// } + /// Entry::Vacant(_) => panic!(), + /// }; /// - /// if let Entry::Occupied(entry) = map.entry(my_key) { - /// // Also replace the key with a handle to our other key. - /// let (old_key, old_value): (Rc, u32) = entry.replace_entry(16); + /// match entry { + /// Entry::Occupied(e) => { + /// assert_eq!(e.key(), &"poneyland"); + /// assert_eq!(e.get(), &43); + /// } + /// Entry::Vacant(_) => panic!(), /// } /// - /// ``` - #[inline] - pub fn replace_entry(self, value: V) -> (K, V) { - match self.0 { - OccupiedEntryInt::Map(m) => m.replace_entry(value), - OccupiedEntryInt::Vec(m) => m.replace_entry(value), - } - } - - /// Replaces the key in the hash map with the key used to create this entry. - /// - /// # Examples - /// - /// ``` - /// use hashbrown::hash_map::{Entry, HashMap}; - /// use std::rc::Rc; - /// - /// let mut map: HashMap, u32> = HashMap::new(); - /// let mut known_strings: Vec> = Vec::new(); - /// - /// // Initialise known strings, run program, etc. + /// assert_eq!(map["poneyland"], 43); /// - /// reclaim_memory(&mut map, &known_strings); + /// let entry = match map.entry("poneyland") { + /// Entry::Occupied(e) => e.replace_entry_with(|_k, _v| None), + /// Entry::Vacant(_) => panic!(), + /// }; /// - /// fn reclaim_memory(map: &mut HashMap, u32>, known_strings: &[Rc] ) { - /// for s in known_strings { - /// if let Entry::Occupied(entry) = map.entry(s.clone()) { - /// // Replaces the entry's key with our version of it in `known_strings`. - /// entry.replace_key(); - /// } + /// match entry { + /// Entry::Vacant(e) => { + /// assert_eq!(e.key(), &"poneyland"); /// } + /// Entry::Occupied(_) => panic!(), /// } + /// + /// assert!(!map.contains_key("poneyland")); + /// /// ``` #[inline] - pub fn replace_key(self) -> K { + pub fn replace_entry_with(self, f: F) -> Entry<'a, K, V, N, S> + where + F: FnOnce(&K, V) -> Option, + V: Clone, + { match self.0 { - OccupiedEntryInt::Map(m) => m.replace_key(), - OccupiedEntryInt::Vec(m) => m.replace_key(), + OccupiedEntryInt::Map(m) => m.replace_entry_with(f).into(), + OccupiedEntryInt::Vec(m) => m.replace_entry_with(f).into(), } } } diff --git a/src/iter.rs b/src/iter.rs index d95a4c1..e8acd08 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -8,7 +8,7 @@ use std::iter::{FromIterator, FusedIterator, IntoIterator}; pub struct Iter<'a, K, V>(IterInt<'a, K, V>); /// Manual implementation so that `Clone` isn't required for `K` nor `V` -impl<'a, K, V> Clone for Iter<'a, K, V> { +impl Clone for Iter<'_, K, V> { fn clone(&self) -> Self { Iter(self.0.clone()) } @@ -27,7 +27,7 @@ pub(crate) enum IterInt<'a, K, V> { } /// Manual implementation so that `Clone` isn't required for `K` nor `V` -impl<'a, K, V> Clone for IterInt<'a, K, V> { +impl Clone for IterInt<'_, K, V> { fn clone(&self) -> Self { match self { IterInt::Map(i) => IterInt::Map(i.clone()), @@ -60,7 +60,7 @@ impl<'a, K, V> Iterator for Iter<'a, K, V> { } } -impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> { +impl ExactSizeIterator for Iter<'_, K, V> { #[inline] fn len(&self) -> usize { match &self.0 { @@ -70,7 +70,7 @@ impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> { } } -impl<'a, K, V> FusedIterator for Iter<'a, K, V> {} +impl FusedIterator for Iter<'_, K, V> {} /// Into iterator for a Halfbrown map pub struct IntoIter(IntoIterInt); @@ -149,6 +149,16 @@ impl<'a, K, V, S, const N: usize> IntoIterator for &'a SizedHashMap } } +impl<'a, K, V, S, const N: usize> IntoIterator for &'a mut SizedHashMap { + type Item = (&'a K, &'a V); + type IntoIter = Iter<'a, K, V>; + + #[inline] + fn into_iter(self) -> Iter<'a, K, V> { + self.iter() + } +} + impl FromIterator<(K, V)> for SizedHashMap where K: Eq + Hash, @@ -198,7 +208,7 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> { } } -impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> { +impl ExactSizeIterator for IterMut<'_, K, V> { #[inline] fn len(&self) -> usize { match &self.0 { @@ -208,4 +218,4 @@ impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> { } } -impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} +impl FusedIterator for IterMut<'_, K, V> {} diff --git a/src/lib.rs b/src/lib.rs index c95a30b..920f9e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,17 +19,14 @@ //! their copyright. #![warn(unused_extern_crates)] -#![cfg_attr( -feature = "cargo-clippy", -deny( -clippy::all, -clippy::unwrap_used, -clippy::unnecessary_unwrap, -clippy::pedantic -), -// We might want to revisit inline_always -allow(clippy::module_name_repetitions, clippy::inline_always) +#![deny( + clippy::all, + clippy::unwrap_used, + clippy::unnecessary_unwrap, + clippy::pedantic )] +// We might want to revisit inline_always +#![allow(clippy::module_name_repetitions, clippy::inline_always)] #![deny(missing_docs)] mod entry; @@ -58,7 +55,7 @@ pub type DefaultHashBuilder = core::hash::BuildHasherDefault = SizedHashMap; /// Maximum nymber of elements before the representaiton is swapped from /// Vec to `HashMap` - /// `SizedHashMap` implementation that alternates between a vector /// and a hashmap to improve performance for low key counts. With a configurable upper vector limit #[derive(Clone)] @@ -172,8 +168,8 @@ impl SizedHashMap SizedHashMap SizedHashMap = HashMap::with_hasher(hasher); @@ -830,8 +826,13 @@ where /// Inserts element, this ignores check in the vector /// map if keys are present - it's a fast way to build /// a new map when uniqueness is known ahead of time. + /// + /// # Safety + /// + /// Used for building new hashmaps wher eit is known that + /// the keys are unique. #[inline] - pub fn insert_nocheck(&mut self, k: K, v: V) + pub unsafe fn insert_nocheck(&mut self, k: K, v: V) where S: Default, { @@ -1054,7 +1055,7 @@ enum DrainInt<'a, K, V, const N: usize> { Vec(VecDrain<'a, (K, V), N>), } -impl<'a, K, V, const N: usize> Iterator for Drain<'a, K, V, N> { +impl Iterator for Drain<'_, K, V, N> { type Item = (K, V); #[inline] fn next(&mut self) -> Option { @@ -1108,7 +1109,7 @@ mod tests { fn scale_up_via_no_check() { let mut v = SizedHashMap::<_, _, _, TEST_SIZE>::new(); for i in 1..TEST_SIZE + 2 { - v.insert_nocheck(i, i); + unsafe { v.insert_nocheck(i, i) }; } } @@ -1119,7 +1120,7 @@ mod tests { let mut v = SizedHashMap::<_, _, _, TEST_SIZE>::new(); for i in 1..TEST_SIZE + 1 { v.insert(3 * i, i); - v.insert_nocheck(3 * i + 1, i); + unsafe { v.insert_nocheck(3 * i + 1, i) }; v.entry(3 * i + 2).or_insert(i); } } diff --git a/src/raw_entry.rs b/src/raw_entry.rs index e48b13e..a69efb6 100644 --- a/src/raw_entry.rs +++ b/src/raw_entry.rs @@ -301,7 +301,7 @@ where /// # Examples /// /// ``` - /// use hashbrown::HashMap; + /// use halfbrown::HashMap; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// let entry = map.raw_entry_mut().from_key("horseyland").insert("horseyland", 37); @@ -339,7 +339,7 @@ where /// # Examples /// /// ``` - /// use hashbrown::HashMap; + /// use halfbrown::HashMap; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// @@ -367,7 +367,7 @@ where /// # Examples /// /// ``` - /// use hashbrown::HashMap; + /// use halfbrown::HashMap; /// /// let mut map: HashMap<&str, String> = HashMap::new(); /// @@ -399,7 +399,7 @@ where /// # Examples /// /// ``` - /// use hashbrown::HashMap; + /// use halfbrown::HashMap; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// diff --git a/src/vecmap/entry.rs b/src/vecmap/entry.rs index b850d98..50e4f27 100644 --- a/src/vecmap/entry.rs +++ b/src/vecmap/entry.rs @@ -187,7 +187,7 @@ impl<'a, K, V, S, const N: usize> OccupiedEntry<'a, K, V, S, N> { /// # Examples /// /// ``` - /// use hashbrown::HashMap; + /// use halfbrown::HashMap; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// map.entry("poneyland").or_insert(12); @@ -203,8 +203,8 @@ impl<'a, K, V, S, const N: usize> OccupiedEntry<'a, K, V, S, N> { /// # Examples /// /// ``` - /// use hashbrown::HashMap; - /// use hashbrown::hash_map::Entry; + /// use halfbrown::HashMap; + /// use halfbrown::Entry; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// map.entry("poneyland").or_insert(12); @@ -226,8 +226,8 @@ impl<'a, K, V, S, const N: usize> OccupiedEntry<'a, K, V, S, N> { /// # Examples /// /// ``` - /// use hashbrown::HashMap; - /// use hashbrown::hash_map::Entry; + /// use halfbrown::HashMap; + /// use halfbrown::Entry; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// map.entry("poneyland").or_insert(12); @@ -251,8 +251,8 @@ impl<'a, K, V, S, const N: usize> OccupiedEntry<'a, K, V, S, N> { /// # Examples /// /// ``` - /// use hashbrown::HashMap; - /// use hashbrown::hash_map::Entry; + /// use halfbrown::HashMap; + /// use halfbrown::Entry; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// map.entry("poneyland").or_insert(12); @@ -283,8 +283,8 @@ impl<'a, K, V, S, const N: usize> OccupiedEntry<'a, K, V, S, N> { /// # Examples /// /// ``` - /// use hashbrown::HashMap; - /// use hashbrown::hash_map::Entry; + /// use halfbrown::HashMap; + /// use halfbrown::Entry; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// map.entry("poneyland").or_insert(12); @@ -306,8 +306,8 @@ impl<'a, K, V, S, const N: usize> OccupiedEntry<'a, K, V, S, N> { /// # Examples /// /// ``` - /// use hashbrown::HashMap; - /// use hashbrown::hash_map::Entry; + /// use halfbrown::HashMap; + /// use halfbrown::Entry; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// map.entry("poneyland").or_insert(12); @@ -330,8 +330,8 @@ impl<'a, K, V, S, const N: usize> OccupiedEntry<'a, K, V, S, N> { /// # Examples /// /// ``` - /// use hashbrown::HashMap; - /// use hashbrown::hash_map::Entry; + /// use halfbrown::HashMap; + /// use halfbrown::Entry; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// map.entry("poneyland").or_insert(12); @@ -353,60 +353,66 @@ impl<'a, K, V, S, const N: usize> OccupiedEntry<'a, K, V, S, N> { /// # Examples /// /// ``` - /// use hashbrown::hash_map::{Entry, HashMap}; + /// use halfbrown::{Entry, HashMap}; /// use std::rc::Rc; /// - /// let mut map: HashMap, u32> = HashMap::new(); - /// map.insert(Rc::new("Stringthing".to_string()), 15); - /// - /// let my_key = Rc::new("Stringthing".to_string()); + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.insert("poneyland", 42); + /// + /// let entry = match map.entry("poneyland") { + /// Entry::Occupied(e) => { + /// e.replace_entry_with(|k, v| { + /// assert_eq!(k, &"poneyland"); + /// assert_eq!(v, 42); + /// Some(v + 1) + /// }) + /// } + /// Entry::Vacant(_) => panic!(), + /// }; /// - /// if let Entry::Occupied(entry) = map.entry(my_key) { - /// // Also replace the key with a handle to our other key. - /// let (old_key, old_value): (Rc, u32) = entry.replace_entry(16); + /// match entry { + /// Entry::Occupied(e) => { + /// assert_eq!(e.key(), &"poneyland"); + /// assert_eq!(e.get(), &43); + /// } + /// Entry::Vacant(_) => panic!(), /// } /// - /// ``` - #[inline] - #[allow(clippy::unwrap_used)] - pub fn replace_entry(self, value: V) -> (K, V) { - let entry = unsafe { self.map.v.get_unchecked_mut(self.idx) }; - - let old_key = mem::replace(&mut entry.0, self.key.unwrap()); - let old_value = mem::replace(&mut entry.1, value); - - (old_key, old_value) - } - - /// Replaces the key in the hash map with the key used to create this entry. - /// - /// # Examples - /// - /// ``` - /// use hashbrown::hash_map::{Entry, HashMap}; - /// use std::rc::Rc; - /// - /// let mut map: HashMap, u32> = HashMap::new(); - /// let mut known_strings: Vec> = Vec::new(); - /// - /// // Initialise known strings, run program, etc. + /// assert_eq!(map["poneyland"], 43); /// - /// reclaim_memory(&mut map, &known_strings); + /// let entry = match map.entry("poneyland") { + /// Entry::Occupied(e) => e.replace_entry_with(|_k, _v| None), + /// Entry::Vacant(_) => panic!(), + /// }; /// - /// fn reclaim_memory(map: &mut HashMap, u32>, known_strings: &[Rc] ) { - /// for s in known_strings { - /// if let Entry::Occupied(entry) = map.entry(s.clone()) { - /// // Replaces the entry's key with our version of it in `known_strings`. - /// entry.replace_key(); - /// } + /// match entry { + /// Entry::Vacant(e) => { + /// assert_eq!(e.key(), &"poneyland"); /// } + /// Entry::Occupied(_) => panic!(), /// } + /// + /// assert!(!map.contains_key("poneyland")); + /// /// ``` #[inline] #[allow(clippy::unwrap_used)] - pub fn replace_key(self) -> K { - let entry = unsafe { self.map.v.get_unchecked_mut(self.idx) }; - mem::replace(&mut entry.0, self.key.unwrap()) + pub fn replace_entry_with(self, f: F) -> Entry<'a, K, V, N, S> + where + F: FnOnce(&K, V) -> Option, + { + let (key, value) = unsafe { self.map.remove_idx(self.idx) }; + + if let Some(v) = f(&key, value) { + let idx = self.map.insert_idx(key, v); + Entry::Occupied(OccupiedEntry { + idx, + key: self.key, + map: self.map, + }) + } else { + Entry::Vacant(VacantEntry { key, map: self.map }) + } } } @@ -435,7 +441,7 @@ impl<'a, K, V, const N: usize, S> VacantEntry<'a, K, V, N, S> { /// # Examples /// /// ``` - /// use hashbrown::HashMap; + /// use halfbrown::HashMap; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); @@ -450,8 +456,8 @@ impl<'a, K, V, const N: usize, S> VacantEntry<'a, K, V, N, S> { /// # Examples /// /// ``` - /// use hashbrown::HashMap; - /// use hashbrown::hash_map::Entry; + /// use halfbrown::HashMap; + /// use halfbrown::Entry; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// @@ -470,8 +476,8 @@ impl<'a, K, V, const N: usize, S> VacantEntry<'a, K, V, N, S> { /// # Examples /// /// ``` - /// use hashbrown::HashMap; - /// use hashbrown::hash_map::Entry; + /// use halfbrown::HashMap; + /// use halfbrown::Entry; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); ///