From 6c9f3323816f4c5180e6b95d457cedb54987e6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Muci=C3=B1o?= Date: Sat, 25 Jul 2020 20:02:09 -0500 Subject: [PATCH 1/5] Inicial implementation/port of the RawEntry API and the relevant tests. --- src/map.rs | 706 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 706 insertions(+) diff --git a/src/map.rs b/src/map.rs index 6bfab07..699de1c 100644 --- a/src/map.rs +++ b/src/map.rs @@ -2168,6 +2168,562 @@ fn assert_covariance() { } } +impl HashMap { + /// Creates a raw entry builder for the HashMap. + /// + /// Raw entries provide the lowest level of control for searching and + /// manipulating a map. They must be manually initialized with a hash and + /// then manually searched. After this, insertions into a vacant entry + /// still require an owned key to be provided. + /// + /// Raw entries are useful for such exotic situations as: + /// + /// * Hash memoization + /// * Deferring the creation of an owned key until it is known to be required + /// * Using a search key that doesn't work with the Borrow trait + /// * Using custom comparison logic without newtype wrappers + /// + /// Because raw entries provide much more low-level control, it's much easier + /// to put the HashMap into an inconsistent state which, while memory-safe, + /// will cause the map to produce seemingly random results. Higher-level and + /// more foolproof APIs like `entry` should be preferred when possible. + /// + /// In particular, the hash used to initialized the raw entry must still be + /// consistent with the hash of the key that is ultimately stored in the entry. + /// This is because implementations of HashMap may need to recompute hashes + /// when resizing, at which point only the keys are available. + /// + /// Raw entries give mutable access to the keys. This must not be used + /// to modify how the key would compare or hash, as the map will not re-evaluate + /// where the key should go, meaning the keys may become "lost" if their + /// location does not reflect their state. For instance, if you change a key + /// so that the map now contains keys which compare equal, search may start + /// acting erratically, with two keys randomly masking each other. Implementations + /// are free to assume this doesn't happen (within the limits of memory-safety). + #[cfg_attr(feature = "inline-more", inline)] + pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<'_, K, V, S> { + RawEntryBuilderMut { map: self } + } + + /// Creates a raw immutable entry builder for the HashMap. + /// + /// Raw entries provide the lowest level of control for searching and + /// manipulating a map. They must be manually initialized with a hash and + /// then manually searched. + /// + /// This is useful for + /// * Hash memoization + /// * Using a search key that doesn't work with the Borrow trait + /// * Using custom comparison logic without newtype wrappers + /// + /// Unless you are in such a situation, higher-level and more foolproof APIs like + /// `get` should be preferred. + /// + /// Immutable raw entries have very limited use; you might instead want `raw_entry_mut`. + #[cfg_attr(feature = "inline-more", inline)] + pub fn raw_entry(&self) -> RawEntryBuilder<'_, K, V, S> { + RawEntryBuilder { map: self } + } +} + +/// A builder for computing where in a [`HashMap`] a key-value pair would be stored. +/// +/// See the [`HashMap::raw_entry_mut`] docs for usage examples. +/// +/// [`HashMap::raw_entry_mut`]: struct.HashMap.html#method.raw_entry_mut +pub struct RawEntryBuilderMut<'a, K, V, S> { + map: &'a mut HashMap, +} + +/// A view into a single entry in a map, which may either be vacant or occupied. +/// +/// This is a lower-level version of [`Entry`]. +/// +/// This `enum` is constructed through the [`raw_entry_mut`] method on [`HashMap`], +/// then calling one of the methods of that [`RawEntryBuilderMut`]. +/// +/// [`HashMap`]: struct.HashMap.html +/// [`Entry`]: enum.Entry.html +/// [`raw_entry_mut`]: struct.HashMap.html#method.raw_entry_mut +/// [`RawEntryBuilderMut`]: struct.RawEntryBuilderMut.html +pub enum RawEntryMut<'a, K, V, S> { + /// An occupied entry. + Occupied(RawOccupiedEntryMut<'a, K, V>), + /// A vacant entry. + Vacant(RawVacantEntryMut<'a, K, V, S>), +} + +/// A view into an occupied entry in a `HashMap`. +/// It is part of the [`RawEntryMut`] enum. +/// +/// [`RawEntryMut`]: enum.RawEntryMut.html +pub struct RawOccupiedEntryMut<'a, K, V> { + elem: Bucket<(K, V)>, + table: &'a mut RawTable<(K, V)>, +} + +unsafe impl Send for RawOccupiedEntryMut<'_, K, V> +where + K: Send, + V: Send, +{ +} +unsafe impl Sync for RawOccupiedEntryMut<'_, K, V> +where + K: Sync, + V: Sync, +{ +} + +/// A view into a vacant entry in a `HashMap`. +/// It is part of the [`RawEntryMut`] enum. +/// +/// [`RawEntryMut`]: enum.RawEntryMut.html +pub struct RawVacantEntryMut<'a, K, V, S> { + table: &'a mut RawTable<(K, V)>, + hash_builder: &'a S, +} + +/// A builder for computing where in a [`HashMap`] a key-value pair would be stored. +/// +/// See the [`HashMap::raw_entry`] docs for usage examples. +/// +/// [`HashMap::raw_entry`]: struct.HashMap.html#method.raw_entry +pub struct RawEntryBuilder<'a, K, V, S> { + map: &'a HashMap, +} + +impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> { + /// Creates a `RawEntryMut` from the given key. + #[cfg_attr(feature = "inline-more", inline)] + #[allow(clippy::wrong_self_convention)] + pub fn from_key(self, k: &Q) -> RawEntryMut<'a, K, V, S> + where + S: BuildHasher, + K: Borrow, + Q: Hash + Eq, + { + let mut hasher = self.map.hash_builder.build_hasher(); + k.hash(&mut hasher); + self.from_key_hashed_nocheck(hasher.finish(), k) + } + + /// Creates a `RawEntryMut` from the given key and its hash. + #[inline] + #[allow(clippy::wrong_self_convention)] + pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> RawEntryMut<'a, K, V, S> + where + K: Borrow, + Q: Eq, + { + self.from_hash(hash, |q| q.borrow().eq(k)) + } +} + +impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> { + /// Creates a `RawEntryMut` from the given hash. + #[cfg_attr(feature = "inline-more", inline)] + #[allow(clippy::wrong_self_convention)] + pub fn from_hash(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S> + where + for<'b> F: FnMut(&'b K) -> bool, + { + self.search(hash, is_match) + } + + #[cfg_attr(feature = "inline-more", inline)] + fn search(self, hash: u64, mut is_match: F) -> RawEntryMut<'a, K, V, S> + where + for<'b> F: FnMut(&'b K) -> bool, + { + match self.map.table.find(hash, |(k, _)| is_match(k)) { + Some(elem) => RawEntryMut::Occupied(RawOccupiedEntryMut { + elem, + table: &mut self.map.table, + }), + None => RawEntryMut::Vacant(RawVacantEntryMut { + table: &mut self.map.table, + hash_builder: &self.map.hash_builder, + }), + } + } +} + +impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> { + /// Access an entry by key. + #[cfg_attr(feature = "inline-more", inline)] + #[allow(clippy::wrong_self_convention)] + pub fn from_key(self, k: &Q) -> Option<(&'a K, &'a V)> + where + S: BuildHasher, + K: Borrow, + Q: Hash + Eq, + { + let mut hasher = self.map.hash_builder.build_hasher(); + k.hash(&mut hasher); + self.from_key_hashed_nocheck(hasher.finish(), k) + } + + /// Access an entry by a key and its hash. + #[cfg_attr(feature = "inline-more", inline)] + #[allow(clippy::wrong_self_convention)] + pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> Option<(&'a K, &'a V)> + where + K: Borrow, + Q: Hash + Eq, + { + self.from_hash(hash, |q| q.borrow().eq(k)) + } + + #[cfg_attr(feature = "inline-more", inline)] + fn search(self, hash: u64, mut is_match: F) -> Option<(&'a K, &'a V)> + where + F: FnMut(&K) -> bool, + { + self.map + .table + .find(hash, |(k, _)| is_match(k)) + .map(|item| unsafe { + let &(ref key, ref value) = item.as_ref(); + (key, value) + }) + } + + /// Access an entry by hash. + #[cfg_attr(feature = "inline-more", inline)] + #[allow(clippy::wrong_self_convention)] + pub fn from_hash(self, hash: u64, is_match: F) -> Option<(&'a K, &'a V)> + where + F: FnMut(&K) -> bool, + { + self.search(hash, is_match) + } +} + +impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { + /// Sets the value of the entry, and returns a RawOccupiedEntryMut. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// let entry = map.raw_entry_mut().from_key("horseyland").insert("horseyland", 37); + /// + /// assert_eq!(entry.remove_entry(), ("horseyland", 37)); + /// ``` + #[cfg_attr(feature = "inline-more", inline)] + pub fn insert(self, key: K, value: V) -> RawOccupiedEntryMut<'a, K, V> + where + K: Hash, + S: BuildHasher, + { + match self { + RawEntryMut::Occupied(mut entry) => { + entry.insert(value); + entry + } + RawEntryMut::Vacant(entry) => entry.insert_entry(key, value), + } + } + + /// Ensures a value is in the entry by inserting the default if empty, and returns + /// mutable references to the key and value in the entry. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// map.raw_entry_mut().from_key("poneyland").or_insert("poneyland", 3); + /// assert_eq!(map["poneyland"], 3); + /// + /// *map.raw_entry_mut().from_key("poneyland").or_insert("poneyland", 10).1 *= 2; + /// assert_eq!(map["poneyland"], 6); + /// ``` + #[cfg_attr(feature = "inline-more", inline)] + pub fn or_insert(self, default_key: K, default_val: V) -> (&'a mut K, &'a mut V) + where + K: Hash, + S: BuildHasher, + { + match self { + RawEntryMut::Occupied(entry) => entry.into_key_value(), + RawEntryMut::Vacant(entry) => entry.insert(default_key, default_val), + } + } + + /// Ensures a value is in the entry by inserting the result of the default function if empty, + /// and returns mutable references to the key and value in the entry. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// + /// let mut map: HashMap<&str, String> = HashMap::new(); + /// + /// map.raw_entry_mut().from_key("poneyland").or_insert_with(|| { + /// ("poneyland", "hoho".to_string()) + /// }); + /// + /// assert_eq!(map["poneyland"], "hoho".to_string()); + /// ``` + #[cfg_attr(feature = "inline-more", inline)] + pub fn or_insert_with(self, default: F) -> (&'a mut K, &'a mut V) + where + F: FnOnce() -> (K, V), + K: Hash, + S: BuildHasher, + { + match self { + RawEntryMut::Occupied(entry) => entry.into_key_value(), + RawEntryMut::Vacant(entry) => { + let (k, v) = default(); + entry.insert(k, v) + } + } + } + + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// map.raw_entry_mut() + /// .from_key("poneyland") + /// .and_modify(|_k, v| { *v += 1 }) + /// .or_insert("poneyland", 42); + /// assert_eq!(map["poneyland"], 42); + /// + /// map.raw_entry_mut() + /// .from_key("poneyland") + /// .and_modify(|_k, v| { *v += 1 }) + /// .or_insert("poneyland", 0); + /// assert_eq!(map["poneyland"], 43); + /// ``` + #[cfg_attr(feature = "inline-more", inline)] + pub fn and_modify(self, f: F) -> Self + where + F: FnOnce(&mut K, &mut V), + { + match self { + RawEntryMut::Occupied(mut entry) => { + { + let (k, v) = entry.get_key_value_mut(); + f(k, v); + } + RawEntryMut::Occupied(entry) + } + RawEntryMut::Vacant(entry) => RawEntryMut::Vacant(entry), + } + } +} + +impl<'a, K, V> RawOccupiedEntryMut<'a, K, V> { + /// Gets a reference to the key in the entry. + #[cfg_attr(feature = "inline-more", inline)] + pub fn key(&self) -> &K { + unsafe { &self.elem.as_ref().0 } + } + + /// Gets a mutable reference to the key in the entry. + #[cfg_attr(feature = "inline-more", inline)] + pub fn key_mut(&mut self) -> &mut K { + unsafe { &mut self.elem.as_mut().0 } + } + + /// Converts the entry into a mutable reference to the key in the entry + /// with a lifetime bound to the map itself. + #[cfg_attr(feature = "inline-more", inline)] + pub fn into_key(self) -> &'a mut K { + unsafe { &mut self.elem.as_mut().0 } + } + + /// Gets a reference to the value in the entry. + #[cfg_attr(feature = "inline-more", inline)] + pub fn get(&self) -> &V { + unsafe { &self.elem.as_ref().1 } + } + + /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// with a lifetime bound to the map itself. + #[cfg_attr(feature = "inline-more", inline)] + pub fn into_mut(self) -> &'a mut V { + unsafe { &mut self.elem.as_mut().1 } + } + + /// Gets a mutable reference to the value in the entry. + #[cfg_attr(feature = "inline-more", inline)] + pub fn get_mut(&mut self) -> &mut V { + unsafe { &mut self.elem.as_mut().1 } + } + + /// Gets a reference to the key and value in the entry. + #[cfg_attr(feature = "inline-more", inline)] + pub fn get_key_value(&mut self) -> (&K, &V) { + unsafe { + let &(ref key, ref value) = self.elem.as_ref(); + (key, value) + } + } + + /// Gets a mutable reference to the key and value in the entry. + #[cfg_attr(feature = "inline-more", inline)] + pub fn get_key_value_mut(&mut self) -> (&mut K, &mut V) { + unsafe { + let &mut (ref mut key, ref mut value) = self.elem.as_mut(); + (key, value) + } + } + + /// Converts the OccupiedEntry into a mutable reference to the key and value in the entry + /// with a lifetime bound to the map itself. + #[cfg_attr(feature = "inline-more", inline)] + pub fn into_key_value(self) -> (&'a mut K, &'a mut V) { + unsafe { + let &mut (ref mut key, ref mut value) = self.elem.as_mut(); + (key, value) + } + } + + /// Sets the value of the entry, and returns the entry's old value. + #[cfg_attr(feature = "inline-more", inline)] + pub fn insert(&mut self, value: V) -> V { + mem::replace(self.get_mut(), value) + } + + /// Sets the value of the entry, and returns the entry's old value. + #[cfg_attr(feature = "inline-more", inline)] + pub fn insert_key(&mut self, key: K) -> K { + mem::replace(self.key_mut(), key) + } + + /// Takes the value out of the entry, and returns it. + #[cfg_attr(feature = "inline-more", inline)] + pub fn remove(self) -> V { + self.remove_entry().1 + } + + /// Take the ownership of the key and value from the map. + #[cfg_attr(feature = "inline-more", inline)] + pub fn remove_entry(self) -> (K, V) { + unsafe { self.table.remove(self.elem) } + } +} + +impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> { + /// Sets the value of the entry with the VacantEntry's key, + /// and returns a mutable reference to it. + #[cfg_attr(feature = "inline-more", inline)] + pub fn insert(self, key: K, value: V) -> (&'a mut K, &'a mut V) + where + K: Hash, + S: BuildHasher, + { + let mut hasher = self.hash_builder.build_hasher(); + key.hash(&mut hasher); + self.insert_hashed_nocheck(hasher.finish(), key, value) + } + + /// Sets the value of the entry with the VacantEntry's key, + /// and returns a mutable reference to it. + #[cfg_attr(feature = "inline-more", inline)] + #[allow(clippy::shadow_unrelated)] + pub fn insert_hashed_nocheck(self, hash: u64, key: K, value: V) -> (&'a mut K, &'a mut V) + where + K: Hash, + S: BuildHasher, + { + let hash_builder = self.hash_builder; + self.insert_with_hasher(hash, key, value, |k| make_hash(hash_builder, k)) + } + + /// Set the value of an entry with a custom hasher function. + #[cfg_attr(feature = "inline-more", inline)] + pub fn insert_with_hasher( + self, + hash: u64, + key: K, + value: V, + hasher: H, + ) -> (&'a mut K, &'a mut V) + where + H: Fn(&K) -> u64, + { + unsafe { + let elem = self.table.insert(hash, (key, value), |x| hasher(&x.0)); + let &mut (ref mut k, ref mut v) = elem.as_mut(); + (k, v) + } + } + + #[cfg_attr(feature = "inline-more", inline)] + fn insert_entry(self, key: K, value: V) -> RawOccupiedEntryMut<'a, K, V> + where + K: Hash, + S: BuildHasher, + { + let hash_builder = self.hash_builder; + let mut hasher = self.hash_builder.build_hasher(); + key.hash(&mut hasher); + + let elem = self.table.insert(hasher.finish(), (key, value), |k| { + make_hash(hash_builder, &k.0) + }); + + RawOccupiedEntryMut { + elem, + table: self.table, + } + } +} + +impl Debug for RawEntryBuilderMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawEntryBuilder").finish() + } +} + +impl Debug for RawEntryMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + RawEntryMut::Vacant(ref v) => f.debug_tuple("RawEntry").field(v).finish(), + RawEntryMut::Occupied(ref o) => f.debug_tuple("RawEntry").field(o).finish(), + } + } +} + +impl Debug for RawOccupiedEntryMut<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawOccupiedEntryMut") + .field("key", self.key()) + .field("value", self.get()) + .finish() + } +} + +impl Debug for RawVacantEntryMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawVacantEntryMut").finish() + } +} + +impl Debug for RawEntryBuilder<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawEntryBuilder").finish() + } +} + #[cfg(test)] #[cfg(not(tarpaulin_include))] // don't count for coverage mod test_map { @@ -3247,4 +3803,154 @@ mod test_map { assert_eq!(m.table.len(), left); } } + + #[test] + fn test_raw_entry() { + use super::RawEntryMut::{Occupied, Vacant}; + + let xs = [(1i32, 10i32), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; + + let mut map: HashMap<_, _> = xs.iter().cloned().collect(); + + let compute_hash = |map: &HashMap, k: i32| -> u64 { + use core::hash::{BuildHasher, Hash, Hasher}; + + let mut hasher = map.hasher().build_hasher(); + k.hash(&mut hasher); + hasher.finish() + }; + + // Existing key (insert) + match map.raw_entry_mut().from_key(&1) { + Vacant(_) => unreachable!(), + Occupied(mut view) => { + assert_eq!(view.get(), &10); + assert_eq!(view.insert(100), 10); + } + } + let hash1 = compute_hash(&map, 1); + assert_eq!(map.raw_entry().from_key(&1).unwrap(), (&1, &100)); + assert_eq!( + map.raw_entry().from_hash(hash1, |k| *k == 1).unwrap(), + (&1, &100) + ); + assert_eq!( + map.raw_entry().from_key_hashed_nocheck(hash1, &1).unwrap(), + (&1, &100) + ); + assert_eq!(map.len(), 6); + + // Existing key (update) + match map.raw_entry_mut().from_key(&2) { + Vacant(_) => unreachable!(), + Occupied(mut view) => { + let v = view.get_mut(); + let new_v = (*v) * 10; + *v = new_v; + } + } + let hash2 = compute_hash(&map, 2); + assert_eq!(map.raw_entry().from_key(&2).unwrap(), (&2, &200)); + assert_eq!( + map.raw_entry().from_hash(hash2, |k| *k == 2).unwrap(), + (&2, &200) + ); + assert_eq!( + map.raw_entry().from_key_hashed_nocheck(hash2, &2).unwrap(), + (&2, &200) + ); + assert_eq!(map.len(), 6); + + // Existing key (take) + let hash3 = compute_hash(&map, 3); + match map.raw_entry_mut().from_key_hashed_nocheck(hash3, &3) { + Vacant(_) => unreachable!(), + Occupied(view) => { + assert_eq!(view.remove_entry(), (3, 30)); + } + } + assert_eq!(map.raw_entry().from_key(&3), None); + assert_eq!(map.raw_entry().from_hash(hash3, |k| *k == 3), None); + assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash3, &3), None); + assert_eq!(map.len(), 5); + + // Nonexistent key (insert) + match map.raw_entry_mut().from_key(&10) { + Occupied(_) => unreachable!(), + Vacant(view) => { + assert_eq!(view.insert(10, 1000), (&mut 10, &mut 1000)); + } + } + assert_eq!(map.raw_entry().from_key(&10).unwrap(), (&10, &1000)); + assert_eq!(map.len(), 6); + + // Ensure all lookup methods produce equivalent results. + for k in 0..12 { + let hash = compute_hash(&map, k); + let v = map.get(&k).cloned(); + let kv = v.as_ref().map(|v| (&k, v)); + + assert_eq!(map.raw_entry().from_key(&k), kv); + assert_eq!(map.raw_entry().from_hash(hash, |q| *q == k), kv); + assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash, &k), kv); + + match map.raw_entry_mut().from_key(&k) { + Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv), + Vacant(_) => assert_eq!(v, None), + } + match map.raw_entry_mut().from_key_hashed_nocheck(hash, &k) { + Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv), + Vacant(_) => assert_eq!(v, None), + } + match map.raw_entry_mut().from_hash(hash, |q| *q == k) { + Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv), + Vacant(_) => assert_eq!(v, None), + } + } + } +} + +#[test] +fn test_key_without_hash_impl() { + #[derive(Debug)] + struct IntWrapper(u64); + + let mut m: HashMap = HashMap::default(); + { + assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_none()); + } + { + let vacant_entry = match m.raw_entry_mut().from_hash(0, |k| k.0 == 0) { + RawEntryMut::Occupied(..) => panic!("Found entry for key 0"), + RawEntryMut::Vacant(e) => e, + }; + vacant_entry.insert_with_hasher(0, IntWrapper(0), (), |k| k.0); + } + { + assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_some()); + assert!(m.raw_entry().from_hash(1, |k| k.0 == 1).is_none()); + assert!(m.raw_entry().from_hash(2, |k| k.0 == 2).is_none()); + } + { + let vacant_entry = match m.raw_entry_mut().from_hash(1, |k| k.0 == 1) { + RawEntryMut::Vacant(e) => e, + RawEntryMut::Occupied(..) => panic!("Found entry for key 1"), + }; + vacant_entry.insert_with_hasher(1, IntWrapper(1), (), |k| k.0); + } + { + assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_some()); + assert!(m.raw_entry().from_hash(1, |k| k.0 == 1).is_some()); + assert!(m.raw_entry().from_hash(2, |k| k.0 == 2).is_none()); + } + { + let occupied_entry = match m.raw_entry_mut().from_hash(0, |k| k.0 == 0) { + RawEntryMut::Occupied(e) => e, + RawEntryMut::Vacant(..) => panic!("Couldn't find entry for key 0"), + }; + occupied_entry.remove(); + } + assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_none()); + assert!(m.raw_entry().from_hash(1, |k| k.0 == 1).is_some()); + assert!(m.raw_entry().from_hash(2, |k| k.0 == 2).is_none()); } From 79f6c78d69c79397ca21ceb35f887459e49748da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Muci=C3=B1o?= Date: Mon, 27 Jul 2020 20:59:35 -0500 Subject: [PATCH 2/5] Additional tests for RawEntry Api. --- src/map.rs | 361 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 313 insertions(+), 48 deletions(-) diff --git a/src/map.rs b/src/map.rs index 699de1c..bebc177 100644 --- a/src/map.rs +++ b/src/map.rs @@ -2729,7 +2729,7 @@ impl Debug for RawEntryBuilder<'_, K, V, S> { mod test_map { use super::DefaultHashBuilder; use super::Entry::{Occupied, Vacant}; - use super::HashMap; + use super::{HashMap, RawEntryMut}; use crate::TryReserveError::*; use rand::{rngs::SmallRng, Rng, SeedableRng}; use std::cell::RefCell; @@ -2857,6 +2857,74 @@ mod test_map { } } + #[test] + fn test_split_insert_raw() { + // the code below assumes that R is 4 + assert_eq!(crate::raw::R, 4); + + let mut m = HashMap::new(); + assert_eq!(m.capacity(), 0); + + // three inserts won't split + for i in 1..=3 { + m.raw_entry_mut().from_key(&i).insert(i, i); + assert_eq!(m.raw_entry().map.get(&i), Some(&i)); + assert_eq!(m.raw_entry().map.capacity(), 3); + } + // fourth insert will split and migrate all elements + assert!(!m.raw_entry().map.is_split()); + m.raw_entry_mut().from_key(&4).insert(4, 4); + // capacity should now be doubled + assert_eq!(m.raw_entry().map.capacity(), 7); + // and there should be no leftovers + assert!(!m.raw_entry().map.is_split()); + assert_eq!(m.raw_entry().map.table.len(), 4); + for i in 1..=4 { + assert_eq!(m.raw_entry().map.get(&i), Some(&i)); + } + + // move to next split point + for i in 5..=7 { + m.raw_entry_mut().from_key(&i).insert(i, i); + assert_eq!(m.raw_entry().map.get(&i), Some(&i)); + assert_eq!(m.raw_entry().map.capacity(), 7); + } + + // next insert will split, and move some, but not all (since R < old.len()) + m.raw_entry_mut().map.insert(8, 8); + // capacity should now be doubled + assert_eq!(m.raw_entry_mut().map.capacity(), 14); + // and there should be leftovers + assert!(m.raw_entry().map.is_split()); + assert_eq!(m.raw_entry().map.table.main().len(), 1 + crate::raw::R); + assert_eq!( + m.raw_entry().map.table.leftovers().map(|t| t.len()), + Some(8 - (1 + crate::raw::R)) + ); + for i in 1..=8 { + assert_eq!(m.raw_entry().map.get(&i), Some(&i)); + } + // check that the iterators do the right thing when split: + assert_eq!(m.raw_entry().map.iter().count(), 8); + for i in 1..=8 { + assert!(m.raw_entry().map.iter().any(|(&e, _)| e == i)); + } + assert_eq!(m.raw_entry_mut().map.iter_mut().count(), 8); + for i in 1..=8 { + assert!(m.raw_entry_mut().map.iter_mut().any(|(&e, _)| e == i)); + } + + // if we do another insert, it will move the rest of the items from the old map + m.insert(9, 9); + assert!(!m.raw_entry().map.is_split()); + assert_eq!(m.raw_entry().map.table.len(), 9); + // it should not have changed capacity + assert_eq!(m.raw_entry().map.capacity(), 14); + for i in 1..=9 { + assert_eq!(m.raw_entry().map.get(&i), Some(&i)); + } + } + #[test] fn test_clone() { let mut m = HashMap::new(); @@ -3056,6 +3124,26 @@ mod test_map { assert_eq!(m.len(), 1); } + #[test] + fn test_empty_raw_entry() { + let mut m: HashMap = HashMap::new(); + assert_eq!(m.raw_entry().map.len(), 0); + assert!(*m.entry(0).or_insert(true)); + assert_eq!(m.raw_entry().map.len(), 1); + } + + #[test] + fn test_empty_raw_entry_mut() { + use super::RawEntryMut::{Occupied, Vacant}; + let mut m: HashMap = HashMap::new(); + match m.raw_entry_mut().from_key(&0) { + Occupied(_) => panic!(), + Vacant(_) => {} + } + assert!(*m.raw_entry_mut().from_key(&0).or_insert(0, true).1); + assert_eq!(m.len(), 1); + } + #[test] fn test_empty_iter() { let mut m: HashMap = HashMap::new(); @@ -3145,6 +3233,97 @@ mod test_map { } } + #[test] + fn test_lots_of_raw_insertions() { + let mut m = HashMap::new(); + + #[cfg(not(any(tarpaulin, miri)))] + const N: usize = 10; + #[cfg(any(tarpaulin, miri))] + const N: usize = 5; + + #[cfg(not(any(tarpaulin, miri)))] + const M: usize = 1001; + #[cfg(tarpaulin)] + const M: usize = 101; + #[cfg(miri)] + const M: usize = 16; + + // Try this a few times to make sure we never screw up the hashmap's + // internal state. + for _ in 0..N { + assert!(m.is_empty()); + + for i in 1..M { + m.raw_entry_mut().from_key(&i).insert(i, i); + assert_eq!(m.raw_entry().from_key(&i).unwrap(), (&i, &i)); + + for j in 1..=i { + let r = m.raw_entry().from_key(&j); + assert_eq!(r, Some((&j, &j))); + } + + for j in i + 1..M { + let r = m.raw_entry().from_key(&j); + assert_eq!(r, None); + } + } + + for i in M..(M * 2 - 1) { + assert!(!m.contains_key(&i)); + } + + // remove forwards + for i in 1..M { + assert!(m.remove(&i).is_some()); + + for j in 1..=i { + assert!(!m.contains_key(&j)); + } + + for j in i + 1..M { + assert!(m.contains_key(&j)); + } + } + + for i in 1..M { + assert!(!m.contains_key(&i)); + } + + for i in 1..M { + m.raw_entry_mut().from_key(&i).or_insert(i, i); + } + + for i in (1..M).rev() { + assert!(m.remove(&i).is_some()); + + for j in i..M { + assert!(!m.contains_key(&j)); + } + + for j in 1..i { + assert!(m.contains_key(&j)); + } + } + + for i in 1..M { + m.raw_entry_mut().from_key(&i).or_insert_with(|| (i, i)); + } + + for i in (1..M).rev() { + assert!(m.remove(&i).is_some()); + + for j in i..M { + assert!(!m.contains_key(&j)); + } + + for j in 1..i { + assert!(m.contains_key(&j)); + } + } + } + } + #[test] fn test_find_mut() { let mut m = HashMap::new(); @@ -3166,6 +3345,41 @@ mod test_map { assert_eq!(m.get(&5), Some(&new)); } + #[test] + fn test_raw_entry_mut_and_modify() { + let mut m = HashMap::new(); + + #[cfg(not(any(tarpaulin, miri)))] + const M: usize = 1001; + + for i in 0..M { + m.raw_entry_mut() + .from_key(&i) + .and_modify(|_k, v| *v += 1) + .or_insert(i, i); + } + + for i in (0..M).rev() { + assert_eq!( + *m.raw_entry_mut() + .from_key(&i) + .and_modify(|_k, _v| ()) + .or_insert(i, i) + .0, + i + ); + + assert_eq!( + *m.raw_entry_mut() + .from_key(&i) + .and_modify(|_k, v| *v += 1) + .or_insert(i, i) + .1, + i + 1 + ); + } + } + #[test] fn test_insert_overwrite() { let mut m = HashMap::new(); @@ -3808,7 +4022,18 @@ mod test_map { fn test_raw_entry() { use super::RawEntryMut::{Occupied, Vacant}; - let xs = [(1i32, 10i32), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; + let xs = [ + (1i32, 10i32), + (2, 20), + (3, 30), + (4, 40), + (5, 50), + (6, 60), + (7, 70), + (8, 80), + (9, 90), + (10, 100), + ]; let mut map: HashMap<_, _> = xs.iter().cloned().collect(); @@ -3838,7 +4063,7 @@ mod test_map { map.raw_entry().from_key_hashed_nocheck(hash1, &1).unwrap(), (&1, &100) ); - assert_eq!(map.len(), 6); + assert_eq!(map.len(), 10); // Existing key (update) match map.raw_entry_mut().from_key(&2) { @@ -3859,7 +4084,7 @@ mod test_map { map.raw_entry().from_key_hashed_nocheck(hash2, &2).unwrap(), (&2, &200) ); - assert_eq!(map.len(), 6); + assert_eq!(map.len(), 10); // Existing key (take) let hash3 = compute_hash(&map, 3); @@ -3872,17 +4097,17 @@ mod test_map { assert_eq!(map.raw_entry().from_key(&3), None); assert_eq!(map.raw_entry().from_hash(hash3, |k| *k == 3), None); assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash3, &3), None); - assert_eq!(map.len(), 5); + assert_eq!(map.len(), 9); // Nonexistent key (insert) - match map.raw_entry_mut().from_key(&10) { + match map.raw_entry_mut().from_key(&11) { Occupied(_) => unreachable!(), Vacant(view) => { - assert_eq!(view.insert(10, 1000), (&mut 10, &mut 1000)); + assert_eq!(view.insert(11, 1100), (&mut 11, &mut 1100)); } } - assert_eq!(map.raw_entry().from_key(&10).unwrap(), (&10, &1000)); - assert_eq!(map.len(), 6); + assert_eq!(map.raw_entry().from_key(&10).unwrap(), (&10, &100)); + assert_eq!(map.len(), 10); // Ensure all lookup methods produce equivalent results. for k in 0..12 { @@ -3908,49 +4133,89 @@ mod test_map { } } } -} -#[test] -fn test_key_without_hash_impl() { - #[derive(Debug)] - struct IntWrapper(u64); + #[test] + fn test_raw_occupied_entry_mut() { + #[cfg(not(any(tarpaulin, miri)))] + const M: usize = 1001; + #[cfg(tarpaulin)] + const M: usize = 101; + #[cfg(miri)] + const M: usize = 16; + + #[cfg(not(any(tarpaulin, miri)))] + const N: usize = 10; + #[cfg(any(tarpaulin, miri))] + const N: usize = 5; - let mut m: HashMap = HashMap::default(); - { - assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_none()); - } - { - let vacant_entry = match m.raw_entry_mut().from_hash(0, |k| k.0 == 0) { - RawEntryMut::Occupied(..) => panic!("Found entry for key 0"), - RawEntryMut::Vacant(e) => e, - }; - vacant_entry.insert_with_hasher(0, IntWrapper(0), (), |k| k.0); - } - { - assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_some()); - assert!(m.raw_entry().from_hash(1, |k| k.0 == 1).is_none()); - assert!(m.raw_entry().from_hash(2, |k| k.0 == 2).is_none()); - } - { - let vacant_entry = match m.raw_entry_mut().from_hash(1, |k| k.0 == 1) { - RawEntryMut::Vacant(e) => e, - RawEntryMut::Occupied(..) => panic!("Found entry for key 1"), - }; - vacant_entry.insert_with_hasher(1, IntWrapper(1), (), |k| k.0); + let mut m = HashMap::new(); + + for i in 0..M { + let mut entry = m.raw_entry_mut().from_key(&i).insert(i, i); + assert_eq!(entry.key(), &i); + assert_eq!(entry.get(), &i); + assert_eq!(entry.get_key_value(), (&i, &i)); + assert_eq!(entry.key_mut(), &i); + assert_eq!(entry.get_mut(), &i); + assert_eq!(entry.into_key(), &i); + } + + for i in M..(M + N) { + let mut entry = m.raw_entry_mut().from_key(&i).insert(i, i); + entry.insert_key(i + 1); + assert_eq!(entry.into_key_value().0, &mut (i + 1)); + } + + for i in (0..M).rev() { + let entry = m.raw_entry_mut().from_key(&i).insert(i, i); + assert_eq!(entry.remove_entry(), (i, i)); + } + + assert_eq!(m.raw_entry().map.len(), N); } - { - assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_some()); + + #[test] + fn test_key_without_hash_impl() { + #[derive(Debug)] + struct IntWrapper(u64); + + let mut m: HashMap = HashMap::default(); + { + assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_none()); + } + { + let vacant_entry = match m.raw_entry_mut().from_hash(0, |k| k.0 == 0) { + RawEntryMut::Occupied(..) => panic!("Found entry for key 0"), + RawEntryMut::Vacant(e) => e, + }; + vacant_entry.insert_with_hasher(0, IntWrapper(0), (), |k| k.0); + } + { + assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_some()); + assert!(m.raw_entry().from_hash(1, |k| k.0 == 1).is_none()); + assert!(m.raw_entry().from_hash(2, |k| k.0 == 2).is_none()); + } + { + let vacant_entry = match m.raw_entry_mut().from_hash(1, |k| k.0 == 1) { + RawEntryMut::Occupied(..) => panic!("Found entry for key 1"), + RawEntryMut::Vacant(e) => e, + }; + vacant_entry.insert_with_hasher(1, IntWrapper(1), (), |k| k.0); + } + { + assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_some()); + assert!(m.raw_entry().from_hash(1, |k| k.0 == 1).is_some()); + assert!(m.raw_entry().from_hash(2, |k| k.0 == 2).is_none()); + } + { + let occupied_entry = match m.raw_entry_mut().from_hash(0, |k| k.0 == 0) { + RawEntryMut::Occupied(e) => e, + RawEntryMut::Vacant(..) => panic!("Couldn't find entry for key 0"), + }; + occupied_entry.remove(); + } + assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_none()); assert!(m.raw_entry().from_hash(1, |k| k.0 == 1).is_some()); assert!(m.raw_entry().from_hash(2, |k| k.0 == 2).is_none()); } - { - let occupied_entry = match m.raw_entry_mut().from_hash(0, |k| k.0 == 0) { - RawEntryMut::Occupied(e) => e, - RawEntryMut::Vacant(..) => panic!("Couldn't find entry for key 0"), - }; - occupied_entry.remove(); - } - assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_none()); - assert!(m.raw_entry().from_hash(1, |k| k.0 == 1).is_some()); - assert!(m.raw_entry().from_hash(2, |k| k.0 == 2).is_none()); } From 645b036a44ae7423341b7476eb98b819b97246f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Muci=C3=B1o?= Date: Mon, 27 Jul 2020 21:27:57 -0500 Subject: [PATCH 3/5] Fix miri and tarpaulin variables. --- src/map.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/map.rs b/src/map.rs index bebc177..d6f0f43 100644 --- a/src/map.rs +++ b/src/map.rs @@ -3351,6 +3351,10 @@ mod test_map { #[cfg(not(any(tarpaulin, miri)))] const M: usize = 1001; + #[cfg(tarpaulin)] + const M: usize = 101; + #[cfg(miri)] + const M: usize = 16; for i in 0..M { m.raw_entry_mut() From 1ee2c14cc915354302d78d4e84b8a292405eba2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Muci=C3=B1o?= Date: Tue, 28 Jul 2020 14:40:10 -0500 Subject: [PATCH 4/5] Move code around so it mimics the layout of Hashbrown. --- src/map.rs | 2140 ++++++++++++++++++++++++++-------------------------- 1 file changed, 1070 insertions(+), 1070 deletions(-) diff --git a/src/map.rs b/src/map.rs index d6f0f43..5c08780 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1035,6 +1035,64 @@ where } } +impl HashMap { + /// Creates a raw entry builder for the HashMap. + /// + /// Raw entries provide the lowest level of control for searching and + /// manipulating a map. They must be manually initialized with a hash and + /// then manually searched. After this, insertions into a vacant entry + /// still require an owned key to be provided. + /// + /// Raw entries are useful for such exotic situations as: + /// + /// * Hash memoization + /// * Deferring the creation of an owned key until it is known to be required + /// * Using a search key that doesn't work with the Borrow trait + /// * Using custom comparison logic without newtype wrappers + /// + /// Because raw entries provide much more low-level control, it's much easier + /// to put the HashMap into an inconsistent state which, while memory-safe, + /// will cause the map to produce seemingly random results. Higher-level and + /// more foolproof APIs like `entry` should be preferred when possible. + /// + /// In particular, the hash used to initialized the raw entry must still be + /// consistent with the hash of the key that is ultimately stored in the entry. + /// This is because implementations of HashMap may need to recompute hashes + /// when resizing, at which point only the keys are available. + /// + /// Raw entries give mutable access to the keys. This must not be used + /// to modify how the key would compare or hash, as the map will not re-evaluate + /// where the key should go, meaning the keys may become "lost" if their + /// location does not reflect their state. For instance, if you change a key + /// so that the map now contains keys which compare equal, search may start + /// acting erratically, with two keys randomly masking each other. Implementations + /// are free to assume this doesn't happen (within the limits of memory-safety). + #[cfg_attr(feature = "inline-more", inline)] + pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<'_, K, V, S> { + RawEntryBuilderMut { map: self } + } + + /// Creates a raw immutable entry builder for the HashMap. + /// + /// Raw entries provide the lowest level of control for searching and + /// manipulating a map. They must be manually initialized with a hash and + /// then manually searched. + /// + /// This is useful for + /// * Hash memoization + /// * Using a search key that doesn't work with the Borrow trait + /// * Using custom comparison logic without newtype wrappers + /// + /// Unless you are in such a situation, higher-level and more foolproof APIs like + /// `get` should be preferred. + /// + /// Immutable raw entries have very limited use; you might instead want `raw_entry_mut`. + #[cfg_attr(feature = "inline-more", inline)] + pub fn raw_entry(&self) -> RawEntryBuilder<'_, K, V, S> { + RawEntryBuilder { map: self } + } +} + impl PartialEq for HashMap where K: Eq + Hash, @@ -1266,341 +1324,210 @@ pub struct ValuesMut<'a, K, V> { inner: IterMut<'a, K, V>, } +/// A builder for computing where in a [`HashMap`] a key-value pair would be stored. +/// +/// See the [`HashMap::raw_entry_mut`] docs for usage examples. +/// +/// [`HashMap::raw_entry_mut`]: struct.HashMap.html#method.raw_entry_mut +pub struct RawEntryBuilderMut<'a, K, V, S> { + map: &'a mut HashMap, +} + /// A view into a single entry in a map, which may either be vacant or occupied. /// -/// This `enum` is constructed from the [`entry`] method on [`HashMap`]. +/// This is a lower-level version of [`Entry`]. +/// +/// This `enum` is constructed through the [`raw_entry_mut`] method on [`HashMap`], +/// then calling one of the methods of that [`RawEntryBuilderMut`]. /// /// [`HashMap`]: struct.HashMap.html -/// [`entry`]: struct.HashMap.html#method.entry -pub enum Entry<'a, K, V, S> { +/// [`Entry`]: enum.Entry.html +/// [`raw_entry_mut`]: struct.HashMap.html#method.raw_entry_mut +/// [`RawEntryBuilderMut`]: struct.RawEntryBuilderMut.html +pub enum RawEntryMut<'a, K, V, S> { /// An occupied entry. - Occupied(OccupiedEntry<'a, K, V, S>), - + Occupied(RawOccupiedEntryMut<'a, K, V>), /// A vacant entry. - Vacant(VacantEntry<'a, K, V, S>), -} - -impl Debug for Entry<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Entry::Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), - Entry::Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(), - } - } + Vacant(RawVacantEntryMut<'a, K, V, S>), } /// A view into an occupied entry in a `HashMap`. -/// It is part of the [`Entry`] enum. +/// It is part of the [`RawEntryMut`] enum. /// -/// [`Entry`]: enum.Entry.html -pub struct OccupiedEntry<'a, K, V, S> { - key: Option, +/// [`RawEntryMut`]: enum.RawEntryMut.html +pub struct RawOccupiedEntryMut<'a, K, V> { elem: Bucket<(K, V)>, - table: &'a mut HashMap, + table: &'a mut RawTable<(K, V)>, } -unsafe impl Send for OccupiedEntry<'_, K, V, S> +unsafe impl Send for RawOccupiedEntryMut<'_, K, V> where K: Send, V: Send, - S: Send, { } -unsafe impl Sync for OccupiedEntry<'_, K, V, S> +unsafe impl Sync for RawOccupiedEntryMut<'_, K, V> where K: Sync, V: Sync, - S: Sync, { } -impl Debug for OccupiedEntry<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OccupiedEntry") - .field("key", self.key()) - .field("value", self.get()) - .finish() - } +/// A view into a vacant entry in a `HashMap`. +/// It is part of the [`RawEntryMut`] enum. +/// +/// [`RawEntryMut`]: enum.RawEntryMut.html +pub struct RawVacantEntryMut<'a, K, V, S> { + table: &'a mut RawTable<(K, V)>, + hash_builder: &'a S, } -/// A view into a vacant entry in a `HashMap`. -/// It is part of the [`Entry`] enum. +/// A builder for computing where in a [`HashMap`] a key-value pair would be stored. /// -/// [`Entry`]: enum.Entry.html -pub struct VacantEntry<'a, K, V, S> { - hash: u64, - key: K, - table: &'a mut HashMap, +/// See the [`HashMap::raw_entry`] docs for usage examples. +/// +/// [`HashMap::raw_entry`]: struct.HashMap.html#method.raw_entry +pub struct RawEntryBuilder<'a, K, V, S> { + map: &'a HashMap, } -impl Debug for VacantEntry<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("VacantEntry").field(self.key()).finish() +impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> { + /// Creates a `RawEntryMut` from the given key. + #[cfg_attr(feature = "inline-more", inline)] + #[allow(clippy::wrong_self_convention)] + pub fn from_key(self, k: &Q) -> RawEntryMut<'a, K, V, S> + where + S: BuildHasher, + K: Borrow, + Q: Hash + Eq, + { + let mut hasher = self.map.hash_builder.build_hasher(); + k.hash(&mut hasher); + self.from_key_hashed_nocheck(hasher.finish(), k) + } + + /// Creates a `RawEntryMut` from the given key and its hash. + #[inline] + #[allow(clippy::wrong_self_convention)] + pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> RawEntryMut<'a, K, V, S> + where + K: Borrow, + Q: Eq, + { + self.from_hash(hash, |q| q.borrow().eq(k)) } } -impl<'a, K, V, S> IntoIterator for &'a HashMap { - type Item = (&'a K, &'a V); - type IntoIter = Iter<'a, K, V>; +impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> { + /// Creates a `RawEntryMut` from the given hash. + #[cfg_attr(feature = "inline-more", inline)] + #[allow(clippy::wrong_self_convention)] + pub fn from_hash(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S> + where + for<'b> F: FnMut(&'b K) -> bool, + { + self.search(hash, is_match) + } #[cfg_attr(feature = "inline-more", inline)] - fn into_iter(self) -> Iter<'a, K, V> { - self.iter() + fn search(self, hash: u64, mut is_match: F) -> RawEntryMut<'a, K, V, S> + where + for<'b> F: FnMut(&'b K) -> bool, + { + match self.map.table.find(hash, |(k, _)| is_match(k)) { + Some(elem) => RawEntryMut::Occupied(RawOccupiedEntryMut { + elem, + table: &mut self.map.table, + }), + None => RawEntryMut::Vacant(RawVacantEntryMut { + table: &mut self.map.table, + hash_builder: &self.map.hash_builder, + }), + } } } -impl<'a, K, V, S> IntoIterator for &'a mut HashMap { - type Item = (&'a K, &'a mut V); - type IntoIter = IterMut<'a, K, V>; +impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> { + /// Access an entry by key. + #[cfg_attr(feature = "inline-more", inline)] + #[allow(clippy::wrong_self_convention)] + pub fn from_key(self, k: &Q) -> Option<(&'a K, &'a V)> + where + S: BuildHasher, + K: Borrow, + Q: Hash + Eq, + { + let mut hasher = self.map.hash_builder.build_hasher(); + k.hash(&mut hasher); + self.from_key_hashed_nocheck(hasher.finish(), k) + } + /// Access an entry by a key and its hash. #[cfg_attr(feature = "inline-more", inline)] - fn into_iter(self) -> IterMut<'a, K, V> { - self.iter_mut() + #[allow(clippy::wrong_self_convention)] + pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> Option<(&'a K, &'a V)> + where + K: Borrow, + Q: Hash + Eq, + { + self.from_hash(hash, |q| q.borrow().eq(k)) } -} -impl IntoIterator for HashMap { - type Item = (K, V); - type IntoIter = IntoIter; + #[cfg_attr(feature = "inline-more", inline)] + fn search(self, hash: u64, mut is_match: F) -> Option<(&'a K, &'a V)> + where + F: FnMut(&K) -> bool, + { + self.map + .table + .find(hash, |(k, _)| is_match(k)) + .map(|item| unsafe { + let &(ref key, ref value) = item.as_ref(); + (key, value) + }) + } - /// Creates a consuming iterator, that is, one that moves each key-value - /// pair out of the map in arbitrary order. The map cannot be used after - /// calling this. + /// Access an entry by hash. + #[cfg_attr(feature = "inline-more", inline)] + #[allow(clippy::wrong_self_convention)] + pub fn from_hash(self, hash: u64, is_match: F) -> Option<(&'a K, &'a V)> + where + F: FnMut(&K) -> bool, + { + self.search(hash, is_match) + } +} + +impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { + /// Sets the value of the entry, and returns a RawOccupiedEntryMut. /// /// # Examples /// /// ``` /// use griddle::HashMap; /// - /// let mut map = HashMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// let entry = map.raw_entry_mut().from_key("horseyland").insert("horseyland", 37); /// - /// // Not possible with .iter() - /// let vec: Vec<(&str, i32)> = map.into_iter().collect(); - /// ``` - #[cfg_attr(feature = "inline-more", inline)] - fn into_iter(self) -> IntoIter { - IntoIter { - inner: self.table.into_iter(), - } - } -} - -impl<'a, K, V> Iterator for Iter<'a, K, V> { - type Item = (&'a K, &'a V); - - #[cfg_attr(feature = "inline-more", inline)] - fn next(&mut self) -> Option<(&'a K, &'a V)> { - self.inner.next().map(|x| unsafe { - let r = x.as_ref(); - (&r.0, &r.1) - }) - } - #[cfg_attr(feature = "inline-more", inline)] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} -impl ExactSizeIterator for Iter<'_, K, V> { - #[cfg_attr(feature = "inline-more", inline)] - fn len(&self) -> usize { - self.inner.len() - } -} - -impl FusedIterator for Iter<'_, K, V> {} - -impl<'a, K, V> Iterator for IterMut<'a, K, V> { - type Item = (&'a K, &'a mut V); - - #[cfg_attr(feature = "inline-more", inline)] - fn next(&mut self) -> Option<(&'a K, &'a mut V)> { - self.inner.next().map(|x| unsafe { - let r = x.as_mut(); - (&r.0, &mut r.1) - }) - } - #[cfg_attr(feature = "inline-more", inline)] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} -impl ExactSizeIterator for IterMut<'_, K, V> { - #[cfg_attr(feature = "inline-more", inline)] - fn len(&self) -> usize { - self.inner.len() - } -} -impl FusedIterator for IterMut<'_, K, V> {} - -impl fmt::Debug for IterMut<'_, K, V> -where - K: fmt::Debug, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -impl Iterator for IntoIter { - type Item = (K, V); - - #[cfg_attr(feature = "inline-more", inline)] - fn next(&mut self) -> Option<(K, V)> { - self.inner.next() - } - #[cfg_attr(feature = "inline-more", inline)] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} -impl ExactSizeIterator for IntoIter { - #[cfg_attr(feature = "inline-more", inline)] - fn len(&self) -> usize { - self.inner.len() - } -} -impl FusedIterator for IntoIter {} - -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -impl<'a, K, V> Iterator for Keys<'a, K, V> { - type Item = &'a K; - - #[cfg_attr(feature = "inline-more", inline)] - fn next(&mut self) -> Option<&'a K> { - self.inner.next().map(|(k, _)| k) - } - #[cfg_attr(feature = "inline-more", inline)] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} -impl ExactSizeIterator for Keys<'_, K, V> { - #[cfg_attr(feature = "inline-more", inline)] - fn len(&self) -> usize { - self.inner.len() - } -} -impl FusedIterator for Keys<'_, K, V> {} - -impl<'a, K, V> Iterator for Values<'a, K, V> { - type Item = &'a V; - - #[cfg_attr(feature = "inline-more", inline)] - fn next(&mut self) -> Option<&'a V> { - self.inner.next().map(|(_, v)| v) - } - #[cfg_attr(feature = "inline-more", inline)] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} -impl ExactSizeIterator for Values<'_, K, V> { - #[cfg_attr(feature = "inline-more", inline)] - fn len(&self) -> usize { - self.inner.len() - } -} -impl FusedIterator for Values<'_, K, V> {} - -impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { - type Item = &'a mut V; - - #[cfg_attr(feature = "inline-more", inline)] - fn next(&mut self) -> Option<&'a mut V> { - self.inner.next().map(|(_, v)| v) - } - #[cfg_attr(feature = "inline-more", inline)] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} -impl ExactSizeIterator for ValuesMut<'_, K, V> { - #[cfg_attr(feature = "inline-more", inline)] - fn len(&self) -> usize { - self.inner.len() - } -} -impl FusedIterator for ValuesMut<'_, K, V> {} - -impl fmt::Debug for ValuesMut<'_, K, V> -where - K: fmt::Debug, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.inner.iter()).finish() - } -} - -impl<'a, K, V> Iterator for Drain<'a, K, V> { - type Item = (K, V); - - #[cfg_attr(feature = "inline-more", inline)] - fn next(&mut self) -> Option<(K, V)> { - self.inner.next() - } - #[cfg_attr(feature = "inline-more", inline)] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} -impl ExactSizeIterator for Drain<'_, K, V> { - #[cfg_attr(feature = "inline-more", inline)] - fn len(&self) -> usize { - self.inner.len() - } -} -impl FusedIterator for Drain<'_, K, V> {} - -impl fmt::Debug for Drain<'_, K, V> -where - K: fmt::Debug, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -impl<'a, K, V, S> Entry<'a, K, V, S> { - /// Sets the value of the entry, and returns an OccupiedEntry. - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// let entry = map.entry("horseyland").insert(37); - /// - /// assert_eq!(entry.key(), &"horseyland"); + /// assert_eq!(entry.remove_entry(), ("horseyland", 37)); /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn insert(self, value: V) -> OccupiedEntry<'a, K, V, S> + pub fn insert(self, key: K, value: V) -> RawOccupiedEntryMut<'a, K, V> where K: Hash, S: BuildHasher, { match self { - Entry::Occupied(mut entry) => { + RawEntryMut::Occupied(mut entry) => { entry.insert(value); entry } - Entry::Vacant(entry) => entry.insert_entry(value), + RawEntryMut::Vacant(entry) => entry.insert_entry(key, value), } } /// Ensures a value is in the entry by inserting the default if empty, and returns - /// a mutable reference to the value in the entry. + /// mutable references to the key and value in the entry. /// /// # Examples /// @@ -1609,26 +1536,26 @@ impl<'a, K, V, S> Entry<'a, K, V, S> { /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// - /// map.entry("poneyland").or_insert(3); + /// map.raw_entry_mut().from_key("poneyland").or_insert("poneyland", 3); /// assert_eq!(map["poneyland"], 3); /// - /// *map.entry("poneyland").or_insert(10) *= 2; + /// *map.raw_entry_mut().from_key("poneyland").or_insert("poneyland", 10).1 *= 2; /// assert_eq!(map["poneyland"], 6); /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn or_insert(self, default: V) -> &'a mut V + pub fn or_insert(self, default_key: K, default_val: V) -> (&'a mut K, &'a mut V) where K: Hash, S: BuildHasher, { match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.insert(default), + RawEntryMut::Occupied(entry) => entry.into_key_value(), + RawEntryMut::Vacant(entry) => entry.insert(default_key, default_val), } } /// Ensures a value is in the entry by inserting the result of the default function if empty, - /// and returns a mutable reference to the value in the entry. + /// and returns mutable references to the key and value in the entry. /// /// # Examples /// @@ -1636,772 +1563,703 @@ impl<'a, K, V, S> Entry<'a, K, V, S> { /// use griddle::HashMap; /// /// let mut map: HashMap<&str, String> = HashMap::new(); - /// let s = "hoho".to_string(); /// - /// map.entry("poneyland").or_insert_with(|| s); + /// map.raw_entry_mut().from_key("poneyland").or_insert_with(|| { + /// ("poneyland", "hoho".to_string()) + /// }); /// /// assert_eq!(map["poneyland"], "hoho".to_string()); /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn or_insert_with V>(self, default: F) -> &'a mut V + pub fn or_insert_with(self, default: F) -> (&'a mut K, &'a mut V) where + F: FnOnce() -> (K, V), K: Hash, S: BuildHasher, { match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.insert(default()), + RawEntryMut::Occupied(entry) => entry.into_key_value(), + RawEntryMut::Vacant(entry) => { + let (k, v) = default(); + entry.insert(k, v) + } } } - /// Ensures a value is in the entry by inserting, if empty, the result of the default function, - /// which takes the key as its argument, and returns a mutable reference to the value in the - /// entry. + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. /// /// # Examples /// /// ``` /// use griddle::HashMap; /// - /// let mut map: HashMap<&str, usize> = HashMap::new(); /// - /// map.entry("poneyland").or_insert_with_key(|key| key.chars().count()); + /// let mut map: HashMap<&str, u32> = HashMap::new(); /// - /// assert_eq!(map["poneyland"], 9); - /// ``` - #[cfg_attr(feature = "inline-more", inline)] - pub fn or_insert_with_key V>(self, default: F) -> &'a mut V - where - K: Hash, - S: BuildHasher, - { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => { - let value = default(entry.key()); - entry.insert(value) - } - } - } - - /// Returns a reference to this entry's key. - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[cfg_attr(feature = "inline-more", inline)] - pub fn key(&self) -> &K { - match *self { - Entry::Occupied(ref entry) => entry.key(), - Entry::Vacant(ref entry) => entry.key(), - } - } - - /// Provides in-place mutable access to an occupied entry before any - /// potential inserts into the map. - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// map.entry("poneyland") - /// .and_modify(|e| { *e += 1 }) - /// .or_insert(42); + /// map.raw_entry_mut() + /// .from_key("poneyland") + /// .and_modify(|_k, v| { *v += 1 }) + /// .or_insert("poneyland", 42); /// assert_eq!(map["poneyland"], 42); /// - /// map.entry("poneyland") - /// .and_modify(|e| { *e += 1 }) - /// .or_insert(42); + /// map.raw_entry_mut() + /// .from_key("poneyland") + /// .and_modify(|_k, v| { *v += 1 }) + /// .or_insert("poneyland", 0); /// assert_eq!(map["poneyland"], 43); /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn and_modify(self, f: F) -> Self where - F: FnOnce(&mut V), + F: FnOnce(&mut K, &mut V), { match self { - Entry::Occupied(mut entry) => { - f(entry.get_mut()); - Entry::Occupied(entry) + RawEntryMut::Occupied(mut entry) => { + { + let (k, v) = entry.get_key_value_mut(); + f(k, v); + } + RawEntryMut::Occupied(entry) } - Entry::Vacant(entry) => Entry::Vacant(entry), - } - } -} - -impl<'a, K, V: Default, S> Entry<'a, K, V, S> { - /// Ensures a value is in the entry by inserting the default value if empty, - /// and returns a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// - /// let mut map: HashMap<&str, Option> = HashMap::new(); - /// map.entry("poneyland").or_default(); - /// - /// assert_eq!(map["poneyland"], None); - /// ``` - #[cfg_attr(feature = "inline-more", inline)] - pub fn or_default(self) -> &'a mut V - where - K: Hash, - S: BuildHasher, - { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.insert(Default::default()), + RawEntryMut::Vacant(entry) => RawEntryMut::Vacant(entry), } } } -impl<'a, K, V, S> OccupiedEntry<'a, K, V, S> { +impl<'a, K, V> RawOccupiedEntryMut<'a, K, V> { /// Gets a reference to the key in the entry. - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn key(&self) -> &K { unsafe { &self.elem.as_ref().0 } } - /// Take the ownership of the key and value from the map. - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// use griddle::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// // We delete the entry from the map. - /// o.remove_entry(); - /// } - /// - /// assert_eq!(map.contains_key("poneyland"), false); - /// ``` + /// Gets a mutable reference to the key in the entry. #[cfg_attr(feature = "inline-more", inline)] - pub fn remove_entry(self) -> (K, V) { - unsafe { self.table.table.remove(self.elem) } + pub fn key_mut(&mut self) -> &mut K { + unsafe { &mut self.elem.as_mut().0 } + } + + /// Converts the entry into a mutable reference to the key in the entry + /// with a lifetime bound to the map itself. + #[cfg_attr(feature = "inline-more", inline)] + pub fn into_key(self) -> &'a mut K { + unsafe { &mut self.elem.as_mut().0 } } /// Gets a reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// use griddle::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// assert_eq!(o.get(), &12); - /// } - /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn get(&self) -> &V { unsafe { &self.elem.as_ref().1 } } + /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// with a lifetime bound to the map itself. + #[cfg_attr(feature = "inline-more", inline)] + pub fn into_mut(self) -> &'a mut V { + unsafe { &mut self.elem.as_mut().1 } + } + /// Gets a mutable reference to the value in the entry. - /// - /// If you need a reference to the `OccupiedEntry` which may outlive the - /// destruction of the `Entry` value, see [`into_mut`]. - /// - /// [`into_mut`]: #method.into_mut - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// use griddle::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// if let Entry::Occupied(mut o) = map.entry("poneyland") { - /// *o.get_mut() += 10; - /// assert_eq!(*o.get(), 22); - /// - /// // We can use the same Entry multiple times. - /// *o.get_mut() += 2; - /// } - /// - /// assert_eq!(map["poneyland"], 24); - /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn get_mut(&mut self) -> &mut V { unsafe { &mut self.elem.as_mut().1 } } - /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// Gets a reference to the key and value in the entry. + #[cfg_attr(feature = "inline-more", inline)] + pub fn get_key_value(&mut self) -> (&K, &V) { + unsafe { + let &(ref key, ref value) = self.elem.as_ref(); + (key, value) + } + } + + /// Gets a mutable reference to the key and value in the entry. + #[cfg_attr(feature = "inline-more", inline)] + pub fn get_key_value_mut(&mut self) -> (&mut K, &mut V) { + unsafe { + let &mut (ref mut key, ref mut value) = self.elem.as_mut(); + (key, value) + } + } + + /// Converts the OccupiedEntry into a mutable reference to the key and value in the entry /// with a lifetime bound to the map itself. - /// - /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. - /// - /// [`get_mut`]: #method.get_mut - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// use griddle::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// *o.into_mut() += 10; - /// } - /// - /// assert_eq!(map["poneyland"], 22); - /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn into_mut(self) -> &'a mut V { - unsafe { &mut self.elem.as_mut().1 } + pub fn into_key_value(self) -> (&'a mut K, &'a mut V) { + unsafe { + let &mut (ref mut key, ref mut value) = self.elem.as_mut(); + (key, value) + } } /// Sets the value of the entry, and returns the entry's old value. - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// use griddle::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(mut o) = map.entry("poneyland") { - /// assert_eq!(o.insert(15), 12); - /// } - /// - /// assert_eq!(map["poneyland"], 15); - /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn insert(&mut self, mut value: V) -> V { - let old_value = self.get_mut(); - mem::swap(&mut value, old_value); - value + pub fn insert(&mut self, value: V) -> V { + mem::replace(self.get_mut(), value) } - /// Takes the value out of the entry, and returns it. - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// use griddle::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// assert_eq!(o.remove(), 12); - /// } - /// - /// assert_eq!(map.contains_key("poneyland"), false); - /// ``` + /// Sets the value of the entry, and returns the entry's old value. #[cfg_attr(feature = "inline-more", inline)] - pub fn remove(self) -> V { - self.remove_entry().1 + pub fn insert_key(&mut self, key: K) -> K { + mem::replace(self.key_mut(), key) } - /// Replaces the entry, returning the old key and value. The new key in the hash map will be - /// the key used to create this entry. - /// - /// # Examples - /// - /// ``` - /// use griddle::hash_map::{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()); - /// - /// if let Entry::Occupied(entry) = map.entry(my_key.clone()) { - /// // Also replace the key with a handle to our other key. - /// let (old_key, old_value): (Rc, u32) = entry.replace_entry(16); - /// } - /// - /// assert_eq!(map[&my_key], 16); - /// ``` + /// Takes the value out of the entry, and returns it. #[cfg_attr(feature = "inline-more", inline)] - pub fn replace_entry(self, value: V) -> (K, V) { - let entry = unsafe { self.elem.as_mut() }; - - let old_key = mem::replace(&mut entry.0, self.key.unwrap()); - let old_value = mem::replace(&mut entry.1, value); - - (old_key, old_value) + pub fn remove(self) -> V { + self.remove_entry().1 } - /// Replaces the key in the hash map with the key used to create this entry. - /// - /// # Examples - /// - /// ``` - /// use griddle::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. - /// - /// reclaim_memory(&mut map, &known_strings); - /// - /// 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(); - /// } - /// } - /// } - /// ``` + /// Take the ownership of the key and value from the map. #[cfg_attr(feature = "inline-more", inline)] - pub fn replace_key(self) -> K { - let entry = unsafe { self.elem.as_mut() }; - mem::replace(&mut entry.0, self.key.unwrap()) + pub fn remove_entry(self) -> (K, V) { + unsafe { self.table.remove(self.elem) } } } -impl<'a, K, V, S> VacantEntry<'a, K, V, S> { - /// Gets a reference to the key that would be used when inserting a value - /// through the `VacantEntry`. - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[cfg_attr(feature = "inline-more", inline)] - pub fn key(&self) -> &K { - &self.key - } - - /// Take ownership of the key. - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// use griddle::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// if let Entry::Vacant(v) = map.entry("poneyland") { - /// v.into_key(); - /// } - /// ``` +impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> { + /// Sets the value of the entry with the VacantEntry's key, + /// and returns a mutable reference to it. #[cfg_attr(feature = "inline-more", inline)] - pub fn into_key(self) -> K { - self.key + pub fn insert(self, key: K, value: V) -> (&'a mut K, &'a mut V) + where + K: Hash, + S: BuildHasher, + { + let mut hasher = self.hash_builder.build_hasher(); + key.hash(&mut hasher); + self.insert_hashed_nocheck(hasher.finish(), key, value) } /// Sets the value of the entry with the VacantEntry's key, /// and returns a mutable reference to it. - /// - /// # Examples - /// - /// ``` - /// use griddle::HashMap; - /// use griddle::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// if let Entry::Vacant(o) = map.entry("poneyland") { - /// o.insert(37); - /// } - /// assert_eq!(map["poneyland"], 37); - /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn insert(self, value: V) -> &'a mut V + #[allow(clippy::shadow_unrelated)] + pub fn insert_hashed_nocheck(self, hash: u64, key: K, value: V) -> (&'a mut K, &'a mut V) where K: Hash, S: BuildHasher, { - let hash_builder = &self.table.hash_builder; - let bucket = self.table.table.insert(self.hash, (self.key, value), |x| { - make_hash(hash_builder, &x.0) - }); - unsafe { &mut bucket.as_mut().1 } + let hash_builder = self.hash_builder; + self.insert_with_hasher(hash, key, value, |k| make_hash(hash_builder, k)) } + /// Set the value of an entry with a custom hasher function. #[cfg_attr(feature = "inline-more", inline)] - fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, S> + pub fn insert_with_hasher( + self, + hash: u64, + key: K, + value: V, + hasher: H, + ) -> (&'a mut K, &'a mut V) + where + H: Fn(&K) -> u64, + { + unsafe { + let elem = self.table.insert(hash, (key, value), |x| hasher(&x.0)); + let &mut (ref mut k, ref mut v) = elem.as_mut(); + (k, v) + } + } + + #[cfg_attr(feature = "inline-more", inline)] + fn insert_entry(self, key: K, value: V) -> RawOccupiedEntryMut<'a, K, V> where K: Hash, S: BuildHasher, { - let hash_builder = &self.table.hash_builder; - let elem = self.table.table.insert(self.hash, (self.key, value), |x| { - make_hash(hash_builder, &x.0) + let hash_builder = self.hash_builder; + let mut hasher = self.hash_builder.build_hasher(); + key.hash(&mut hasher); + + let elem = self.table.insert(hasher.finish(), (key, value), |k| { + make_hash(hash_builder, &k.0) }); - OccupiedEntry { - key: None, + + RawOccupiedEntryMut { elem, table: self.table, } } } -impl FromIterator<(K, V)> for HashMap -where - K: Eq + Hash, - S: BuildHasher + Default, -{ - #[cfg_attr(feature = "inline-more", inline)] - fn from_iter>(iter: T) -> Self { - let iter = iter.into_iter(); - let mut map = Self::with_capacity_and_hasher(iter.size_hint().0, S::default()); - iter.for_each(|(k, v)| { - map.insert(k, v); - }); - map +impl Debug for RawEntryBuilderMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawEntryBuilder").finish() } } -/// Inserts all new key-values from the iterator and replaces values with existing -/// keys with new values returned from the iterator. -impl Extend<(K, V)> for HashMap -where - K: Eq + Hash, - S: BuildHasher, -{ - #[cfg_attr(feature = "inline-more", inline)] - fn extend>(&mut self, iter: T) { - // Keys may be already present or show multiple times in the iterator. - // Reserve the entire hint lower bound if the map is empty. - // Otherwise reserve half the hint (rounded up), so the map - // will only resize twice in the worst case. - let iter = iter.into_iter(); - let reserve = if self.is_empty() { - iter.size_hint().0 - } else { - (iter.size_hint().0 + 1) / 2 - }; - self.reserve(reserve); - iter.for_each(move |(k, v)| { - self.insert(k, v); - }); +impl Debug for RawEntryMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + RawEntryMut::Vacant(ref v) => f.debug_tuple("RawEntry").field(v).finish(), + RawEntryMut::Occupied(ref o) => f.debug_tuple("RawEntry").field(o).finish(), + } } } -impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap -where - K: Eq + Hash + Copy, - V: Copy, - S: BuildHasher, -{ - #[cfg_attr(feature = "inline-more", inline)] - fn extend>(&mut self, iter: T) { - self.extend(iter.into_iter().map(|(&key, &value)| (key, value))); +impl Debug for RawOccupiedEntryMut<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawOccupiedEntryMut") + .field("key", self.key()) + .field("value", self.get()) + .finish() } } -#[allow(dead_code)] -fn assert_covariance() { - fn map_key<'new>(v: HashMap<&'static str, u8>) -> HashMap<&'new str, u8> { - v +impl Debug for RawVacantEntryMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawVacantEntryMut").finish() } - fn map_val<'new>(v: HashMap) -> HashMap { - v +} + +impl Debug for RawEntryBuilder<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawEntryBuilder").finish() } - fn iter_key<'a, 'new>(v: Iter<'a, &'static str, u8>) -> Iter<'a, &'new str, u8> { - v - } - fn iter_val<'a, 'new>(v: Iter<'a, u8, &'static str>) -> Iter<'a, u8, &'new str> { - v - } - fn into_iter_key<'new>(v: IntoIter<&'static str, u8>) -> IntoIter<&'new str, u8> { - v - } - fn into_iter_val<'new>(v: IntoIter) -> IntoIter { - v - } - fn keys_key<'a, 'new>(v: Keys<'a, &'static str, u8>) -> Keys<'a, &'new str, u8> { - v - } - fn keys_val<'a, 'new>(v: Keys<'a, u8, &'static str>) -> Keys<'a, u8, &'new str> { - v +} + +/// A view into a single entry in a map, which may either be vacant or occupied. +/// +/// This `enum` is constructed from the [`entry`] method on [`HashMap`]. +/// +/// [`HashMap`]: struct.HashMap.html +/// [`entry`]: struct.HashMap.html#method.entry +pub enum Entry<'a, K, V, S> { + /// An occupied entry. + Occupied(OccupiedEntry<'a, K, V, S>), + + /// A vacant entry. + Vacant(VacantEntry<'a, K, V, S>), +} + +impl Debug for Entry<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Entry::Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), + Entry::Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(), + } } - fn values_key<'a, 'new>(v: Values<'a, &'static str, u8>) -> Values<'a, &'new str, u8> { - v +} + +/// A view into an occupied entry in a `HashMap`. +/// It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html +pub struct OccupiedEntry<'a, K, V, S> { + key: Option, + elem: Bucket<(K, V)>, + table: &'a mut HashMap, +} + +unsafe impl Send for OccupiedEntry<'_, K, V, S> +where + K: Send, + V: Send, + S: Send, +{ +} +unsafe impl Sync for OccupiedEntry<'_, K, V, S> +where + K: Sync, + V: Sync, + S: Sync, +{ +} + +impl Debug for OccupiedEntry<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OccupiedEntry") + .field("key", self.key()) + .field("value", self.get()) + .finish() } - fn values_val<'a, 'new>(v: Values<'a, u8, &'static str>) -> Values<'a, u8, &'new str> { - v +} + +/// A view into a vacant entry in a `HashMap`. +/// It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html +pub struct VacantEntry<'a, K, V, S> { + hash: u64, + key: K, + table: &'a mut HashMap, +} + +impl Debug for VacantEntry<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("VacantEntry").field(self.key()).finish() } - fn drain<'new>( - d: Drain<'static, &'static str, &'static str>, - ) -> Drain<'new, &'new str, &'new str> { - d +} + +impl<'a, K, V, S> IntoIterator for &'a HashMap { + type Item = (&'a K, &'a V); + type IntoIter = Iter<'a, K, V>; + + #[cfg_attr(feature = "inline-more", inline)] + fn into_iter(self) -> Iter<'a, K, V> { + self.iter() } } -impl HashMap { - /// Creates a raw entry builder for the HashMap. - /// - /// Raw entries provide the lowest level of control for searching and - /// manipulating a map. They must be manually initialized with a hash and - /// then manually searched. After this, insertions into a vacant entry - /// still require an owned key to be provided. - /// - /// Raw entries are useful for such exotic situations as: - /// - /// * Hash memoization - /// * Deferring the creation of an owned key until it is known to be required - /// * Using a search key that doesn't work with the Borrow trait - /// * Using custom comparison logic without newtype wrappers - /// - /// Because raw entries provide much more low-level control, it's much easier - /// to put the HashMap into an inconsistent state which, while memory-safe, - /// will cause the map to produce seemingly random results. Higher-level and - /// more foolproof APIs like `entry` should be preferred when possible. - /// - /// In particular, the hash used to initialized the raw entry must still be - /// consistent with the hash of the key that is ultimately stored in the entry. - /// This is because implementations of HashMap may need to recompute hashes - /// when resizing, at which point only the keys are available. - /// - /// Raw entries give mutable access to the keys. This must not be used - /// to modify how the key would compare or hash, as the map will not re-evaluate - /// where the key should go, meaning the keys may become "lost" if their - /// location does not reflect their state. For instance, if you change a key - /// so that the map now contains keys which compare equal, search may start - /// acting erratically, with two keys randomly masking each other. Implementations - /// are free to assume this doesn't happen (within the limits of memory-safety). +impl<'a, K, V, S> IntoIterator for &'a mut HashMap { + type Item = (&'a K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + #[cfg_attr(feature = "inline-more", inline)] - pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<'_, K, V, S> { - RawEntryBuilderMut { map: self } + fn into_iter(self) -> IterMut<'a, K, V> { + self.iter_mut() } +} - /// Creates a raw immutable entry builder for the HashMap. +impl IntoIterator for HashMap { + type Item = (K, V); + type IntoIter = IntoIter; + + /// Creates a consuming iterator, that is, one that moves each key-value + /// pair out of the map in arbitrary order. The map cannot be used after + /// calling this. /// - /// Raw entries provide the lowest level of control for searching and - /// manipulating a map. They must be manually initialized with a hash and - /// then manually searched. + /// # Examples /// - /// This is useful for - /// * Hash memoization - /// * Using a search key that doesn't work with the Borrow trait - /// * Using custom comparison logic without newtype wrappers + /// ``` + /// use griddle::HashMap; /// - /// Unless you are in such a situation, higher-level and more foolproof APIs like - /// `get` should be preferred. + /// let mut map = HashMap::new(); + /// map.insert("a", 1); + /// map.insert("b", 2); + /// map.insert("c", 3); /// - /// Immutable raw entries have very limited use; you might instead want `raw_entry_mut`. + /// // Not possible with .iter() + /// let vec: Vec<(&str, i32)> = map.into_iter().collect(); + /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn raw_entry(&self) -> RawEntryBuilder<'_, K, V, S> { - RawEntryBuilder { map: self } + fn into_iter(self) -> IntoIter { + IntoIter { + inner: self.table.into_iter(), + } } } -/// A builder for computing where in a [`HashMap`] a key-value pair would be stored. -/// -/// See the [`HashMap::raw_entry_mut`] docs for usage examples. -/// -/// [`HashMap::raw_entry_mut`]: struct.HashMap.html#method.raw_entry_mut -pub struct RawEntryBuilderMut<'a, K, V, S> { - map: &'a mut HashMap, -} +impl<'a, K, V> Iterator for Iter<'a, K, V> { + type Item = (&'a K, &'a V); -/// A view into a single entry in a map, which may either be vacant or occupied. -/// -/// This is a lower-level version of [`Entry`]. -/// -/// This `enum` is constructed through the [`raw_entry_mut`] method on [`HashMap`], -/// then calling one of the methods of that [`RawEntryBuilderMut`]. -/// -/// [`HashMap`]: struct.HashMap.html -/// [`Entry`]: enum.Entry.html -/// [`raw_entry_mut`]: struct.HashMap.html#method.raw_entry_mut -/// [`RawEntryBuilderMut`]: struct.RawEntryBuilderMut.html -pub enum RawEntryMut<'a, K, V, S> { - /// An occupied entry. - Occupied(RawOccupiedEntryMut<'a, K, V>), - /// A vacant entry. - Vacant(RawVacantEntryMut<'a, K, V, S>), + #[cfg_attr(feature = "inline-more", inline)] + fn next(&mut self) -> Option<(&'a K, &'a V)> { + self.inner.next().map(|x| unsafe { + let r = x.as_ref(); + (&r.0, &r.1) + }) + } + #[cfg_attr(feature = "inline-more", inline)] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} +impl ExactSizeIterator for Iter<'_, K, V> { + #[cfg_attr(feature = "inline-more", inline)] + fn len(&self) -> usize { + self.inner.len() + } } -/// A view into an occupied entry in a `HashMap`. -/// It is part of the [`RawEntryMut`] enum. -/// -/// [`RawEntryMut`]: enum.RawEntryMut.html -pub struct RawOccupiedEntryMut<'a, K, V> { - elem: Bucket<(K, V)>, - table: &'a mut RawTable<(K, V)>, +impl FusedIterator for Iter<'_, K, V> {} + +impl<'a, K, V> Iterator for IterMut<'a, K, V> { + type Item = (&'a K, &'a mut V); + + #[cfg_attr(feature = "inline-more", inline)] + fn next(&mut self) -> Option<(&'a K, &'a mut V)> { + self.inner.next().map(|x| unsafe { + let r = x.as_mut(); + (&r.0, &mut r.1) + }) + } + #[cfg_attr(feature = "inline-more", inline)] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } } +impl ExactSizeIterator for IterMut<'_, K, V> { + #[cfg_attr(feature = "inline-more", inline)] + fn len(&self) -> usize { + self.inner.len() + } +} +impl FusedIterator for IterMut<'_, K, V> {} -unsafe impl Send for RawOccupiedEntryMut<'_, K, V> +impl fmt::Debug for IterMut<'_, K, V> where - K: Send, - V: Send, + K: fmt::Debug, + V: fmt::Debug, { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() + } } -unsafe impl Sync for RawOccupiedEntryMut<'_, K, V> -where - K: Sync, - V: Sync, -{ + +impl Iterator for IntoIter { + type Item = (K, V); + + #[cfg_attr(feature = "inline-more", inline)] + fn next(&mut self) -> Option<(K, V)> { + self.inner.next() + } + #[cfg_attr(feature = "inline-more", inline)] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} +impl ExactSizeIterator for IntoIter { + #[cfg_attr(feature = "inline-more", inline)] + fn len(&self) -> usize { + self.inner.len() + } +} +impl FusedIterator for IntoIter {} + +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + +impl<'a, K, V> Iterator for Keys<'a, K, V> { + type Item = &'a K; + + #[cfg_attr(feature = "inline-more", inline)] + fn next(&mut self) -> Option<&'a K> { + self.inner.next().map(|(k, _)| k) + } + #[cfg_attr(feature = "inline-more", inline)] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} +impl ExactSizeIterator for Keys<'_, K, V> { + #[cfg_attr(feature = "inline-more", inline)] + fn len(&self) -> usize { + self.inner.len() + } +} +impl FusedIterator for Keys<'_, K, V> {} + +impl<'a, K, V> Iterator for Values<'a, K, V> { + type Item = &'a V; + + #[cfg_attr(feature = "inline-more", inline)] + fn next(&mut self) -> Option<&'a V> { + self.inner.next().map(|(_, v)| v) + } + #[cfg_attr(feature = "inline-more", inline)] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} +impl ExactSizeIterator for Values<'_, K, V> { + #[cfg_attr(feature = "inline-more", inline)] + fn len(&self) -> usize { + self.inner.len() + } +} +impl FusedIterator for Values<'_, K, V> {} + +impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { + type Item = &'a mut V; + + #[cfg_attr(feature = "inline-more", inline)] + fn next(&mut self) -> Option<&'a mut V> { + self.inner.next().map(|(_, v)| v) + } + #[cfg_attr(feature = "inline-more", inline)] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} +impl ExactSizeIterator for ValuesMut<'_, K, V> { + #[cfg_attr(feature = "inline-more", inline)] + fn len(&self) -> usize { + self.inner.len() + } } +impl FusedIterator for ValuesMut<'_, K, V> {} -/// A view into a vacant entry in a `HashMap`. -/// It is part of the [`RawEntryMut`] enum. -/// -/// [`RawEntryMut`]: enum.RawEntryMut.html -pub struct RawVacantEntryMut<'a, K, V, S> { - table: &'a mut RawTable<(K, V)>, - hash_builder: &'a S, +impl fmt::Debug for ValuesMut<'_, K, V> +where + K: fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.inner.iter()).finish() + } } -/// A builder for computing where in a [`HashMap`] a key-value pair would be stored. -/// -/// See the [`HashMap::raw_entry`] docs for usage examples. -/// -/// [`HashMap::raw_entry`]: struct.HashMap.html#method.raw_entry -pub struct RawEntryBuilder<'a, K, V, S> { - map: &'a HashMap, -} +impl<'a, K, V> Iterator for Drain<'a, K, V> { + type Item = (K, V); -impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> { - /// Creates a `RawEntryMut` from the given key. #[cfg_attr(feature = "inline-more", inline)] - #[allow(clippy::wrong_self_convention)] - pub fn from_key(self, k: &Q) -> RawEntryMut<'a, K, V, S> - where - S: BuildHasher, - K: Borrow, - Q: Hash + Eq, - { - let mut hasher = self.map.hash_builder.build_hasher(); - k.hash(&mut hasher); - self.from_key_hashed_nocheck(hasher.finish(), k) + fn next(&mut self) -> Option<(K, V)> { + self.inner.next() } - - /// Creates a `RawEntryMut` from the given key and its hash. - #[inline] - #[allow(clippy::wrong_self_convention)] - pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> RawEntryMut<'a, K, V, S> - where - K: Borrow, - Q: Eq, - { - self.from_hash(hash, |q| q.borrow().eq(k)) + #[cfg_attr(feature = "inline-more", inline)] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() } } - -impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> { - /// Creates a `RawEntryMut` from the given hash. +impl ExactSizeIterator for Drain<'_, K, V> { #[cfg_attr(feature = "inline-more", inline)] - #[allow(clippy::wrong_self_convention)] - pub fn from_hash(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S> - where - for<'b> F: FnMut(&'b K) -> bool, - { - self.search(hash, is_match) + fn len(&self) -> usize { + self.inner.len() + } +} +impl FusedIterator for Drain<'_, K, V> {} + +impl fmt::Debug for Drain<'_, K, V> +where + K: fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() } +} +impl<'a, K, V, S> Entry<'a, K, V, S> { + /// Sets the value of the entry, and returns an OccupiedEntry. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// let entry = map.entry("horseyland").insert(37); + /// + /// assert_eq!(entry.key(), &"horseyland"); + /// ``` #[cfg_attr(feature = "inline-more", inline)] - fn search(self, hash: u64, mut is_match: F) -> RawEntryMut<'a, K, V, S> + pub fn insert(self, value: V) -> OccupiedEntry<'a, K, V, S> where - for<'b> F: FnMut(&'b K) -> bool, + K: Hash, + S: BuildHasher, { - match self.map.table.find(hash, |(k, _)| is_match(k)) { - Some(elem) => RawEntryMut::Occupied(RawOccupiedEntryMut { - elem, - table: &mut self.map.table, - }), - None => RawEntryMut::Vacant(RawVacantEntryMut { - table: &mut self.map.table, - hash_builder: &self.map.hash_builder, - }), + match self { + Entry::Occupied(mut entry) => { + entry.insert(value); + entry + } + Entry::Vacant(entry) => entry.insert_entry(value), } } -} -impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> { - /// Access an entry by key. + /// Ensures a value is in the entry by inserting the default if empty, and returns + /// a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// map.entry("poneyland").or_insert(3); + /// assert_eq!(map["poneyland"], 3); + /// + /// *map.entry("poneyland").or_insert(10) *= 2; + /// assert_eq!(map["poneyland"], 6); + /// ``` #[cfg_attr(feature = "inline-more", inline)] - #[allow(clippy::wrong_self_convention)] - pub fn from_key(self, k: &Q) -> Option<(&'a K, &'a V)> + pub fn or_insert(self, default: V) -> &'a mut V where + K: Hash, S: BuildHasher, - K: Borrow, - Q: Hash + Eq, { - let mut hasher = self.map.hash_builder.build_hasher(); - k.hash(&mut hasher); - self.from_key_hashed_nocheck(hasher.finish(), k) + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert(default), + } } - /// Access an entry by a key and its hash. + /// Ensures a value is in the entry by inserting the result of the default function if empty, + /// and returns a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// + /// let mut map: HashMap<&str, String> = HashMap::new(); + /// let s = "hoho".to_string(); + /// + /// map.entry("poneyland").or_insert_with(|| s); + /// + /// assert_eq!(map["poneyland"], "hoho".to_string()); + /// ``` #[cfg_attr(feature = "inline-more", inline)] - #[allow(clippy::wrong_self_convention)] - pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> Option<(&'a K, &'a V)> + pub fn or_insert_with V>(self, default: F) -> &'a mut V where - K: Borrow, - Q: Hash + Eq, + K: Hash, + S: BuildHasher, { - self.from_hash(hash, |q| q.borrow().eq(k)) + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert(default()), + } } + /// Ensures a value is in the entry by inserting, if empty, the result of the default function, + /// which takes the key as its argument, and returns a mutable reference to the value in the + /// entry. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// + /// let mut map: HashMap<&str, usize> = HashMap::new(); + /// + /// map.entry("poneyland").or_insert_with_key(|key| key.chars().count()); + /// + /// assert_eq!(map["poneyland"], 9); + /// ``` #[cfg_attr(feature = "inline-more", inline)] - fn search(self, hash: u64, mut is_match: F) -> Option<(&'a K, &'a V)> + pub fn or_insert_with_key V>(self, default: F) -> &'a mut V where - F: FnMut(&K) -> bool, + K: Hash, + S: BuildHasher, { - self.map - .table - .find(hash, |(k, _)| is_match(k)) - .map(|item| unsafe { - let &(ref key, ref value) = item.as_ref(); - (key, value) - }) + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + let value = default(entry.key()); + entry.insert(value) + } + } } - /// Access an entry by hash. + /// Returns a reference to this entry's key. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` #[cfg_attr(feature = "inline-more", inline)] - #[allow(clippy::wrong_self_convention)] - pub fn from_hash(self, hash: u64, is_match: F) -> Option<(&'a K, &'a V)> - where - F: FnMut(&K) -> bool, - { - self.search(hash, is_match) + pub fn key(&self) -> &K { + match *self { + Entry::Occupied(ref entry) => entry.key(), + Entry::Vacant(ref entry) => entry.key(), + } } -} -impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { - /// Sets the value of the entry, and returns a RawOccupiedEntryMut. + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. /// /// # Examples /// @@ -2409,318 +2267,460 @@ impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { /// use griddle::HashMap; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// let entry = map.raw_entry_mut().from_key("horseyland").insert("horseyland", 37); /// - /// assert_eq!(entry.remove_entry(), ("horseyland", 37)); + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 42); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 43); /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn insert(self, key: K, value: V) -> RawOccupiedEntryMut<'a, K, V> + pub fn and_modify(self, f: F) -> Self where - K: Hash, - S: BuildHasher, + F: FnOnce(&mut V), { match self { - RawEntryMut::Occupied(mut entry) => { - entry.insert(value); - entry + Entry::Occupied(mut entry) => { + f(entry.get_mut()); + Entry::Occupied(entry) } - RawEntryMut::Vacant(entry) => entry.insert_entry(key, value), + Entry::Vacant(entry) => Entry::Vacant(entry), } } +} - /// Ensures a value is in the entry by inserting the default if empty, and returns - /// mutable references to the key and value in the entry. +impl<'a, K, V: Default, S> Entry<'a, K, V, S> { + /// Ensures a value is in the entry by inserting the default value if empty, + /// and returns a mutable reference to the value in the entry. /// /// # Examples /// /// ``` /// use griddle::HashMap; /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// map.raw_entry_mut().from_key("poneyland").or_insert("poneyland", 3); - /// assert_eq!(map["poneyland"], 3); + /// let mut map: HashMap<&str, Option> = HashMap::new(); + /// map.entry("poneyland").or_default(); /// - /// *map.raw_entry_mut().from_key("poneyland").or_insert("poneyland", 10).1 *= 2; - /// assert_eq!(map["poneyland"], 6); + /// assert_eq!(map["poneyland"], None); /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn or_insert(self, default_key: K, default_val: V) -> (&'a mut K, &'a mut V) + pub fn or_default(self) -> &'a mut V where K: Hash, S: BuildHasher, { match self { - RawEntryMut::Occupied(entry) => entry.into_key_value(), - RawEntryMut::Vacant(entry) => entry.insert(default_key, default_val), + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert(Default::default()), } } +} - /// Ensures a value is in the entry by inserting the result of the default function if empty, - /// and returns mutable references to the key and value in the entry. +impl<'a, K, V, S> OccupiedEntry<'a, K, V, S> { + /// Gets a reference to the key in the entry. /// /// # Examples /// /// ``` /// use griddle::HashMap; /// - /// let mut map: HashMap<&str, String> = HashMap::new(); - /// - /// map.raw_entry_mut().from_key("poneyland").or_insert_with(|| { - /// ("poneyland", "hoho".to_string()) - /// }); - /// - /// assert_eq!(map["poneyland"], "hoho".to_string()); + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn or_insert_with(self, default: F) -> (&'a mut K, &'a mut V) - where - F: FnOnce() -> (K, V), - K: Hash, - S: BuildHasher, - { - match self { - RawEntryMut::Occupied(entry) => entry.into_key_value(), - RawEntryMut::Vacant(entry) => { - let (k, v) = default(); - entry.insert(k, v) - } - } + pub fn key(&self) -> &K { + unsafe { &self.elem.as_ref().0 } } - /// Provides in-place mutable access to an occupied entry before any - /// potential inserts into the map. + /// Take the ownership of the key and value from the map. /// /// # Examples /// /// ``` /// use griddle::HashMap; - /// + /// use griddle::hash_map::Entry; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); /// - /// map.raw_entry_mut() - /// .from_key("poneyland") - /// .and_modify(|_k, v| { *v += 1 }) - /// .or_insert("poneyland", 42); - /// assert_eq!(map["poneyland"], 42); + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// // We delete the entry from the map. + /// o.remove_entry(); + /// } /// - /// map.raw_entry_mut() - /// .from_key("poneyland") - /// .and_modify(|_k, v| { *v += 1 }) - /// .or_insert("poneyland", 0); - /// assert_eq!(map["poneyland"], 43); + /// assert_eq!(map.contains_key("poneyland"), false); /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn and_modify(self, f: F) -> Self - where - F: FnOnce(&mut K, &mut V), - { - match self { - RawEntryMut::Occupied(mut entry) => { - { - let (k, v) = entry.get_key_value_mut(); - f(k, v); - } - RawEntryMut::Occupied(entry) - } - RawEntryMut::Vacant(entry) => RawEntryMut::Vacant(entry), - } - } -} - -impl<'a, K, V> RawOccupiedEntryMut<'a, K, V> { - /// Gets a reference to the key in the entry. - #[cfg_attr(feature = "inline-more", inline)] - pub fn key(&self) -> &K { - unsafe { &self.elem.as_ref().0 } - } - - /// Gets a mutable reference to the key in the entry. - #[cfg_attr(feature = "inline-more", inline)] - pub fn key_mut(&mut self) -> &mut K { - unsafe { &mut self.elem.as_mut().0 } - } - - /// Converts the entry into a mutable reference to the key in the entry - /// with a lifetime bound to the map itself. - #[cfg_attr(feature = "inline-more", inline)] - pub fn into_key(self) -> &'a mut K { - unsafe { &mut self.elem.as_mut().0 } + pub fn remove_entry(self) -> (K, V) { + unsafe { self.table.table.remove(self.elem) } } /// Gets a reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// use griddle::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// assert_eq!(o.get(), &12); + /// } + /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn get(&self) -> &V { unsafe { &self.elem.as_ref().1 } } - /// Converts the OccupiedEntry into a mutable reference to the value in the entry - /// with a lifetime bound to the map itself. - #[cfg_attr(feature = "inline-more", inline)] - pub fn into_mut(self) -> &'a mut V { - unsafe { &mut self.elem.as_mut().1 } - } - /// Gets a mutable reference to the value in the entry. + /// + /// If you need a reference to the `OccupiedEntry` which may outlive the + /// destruction of the `Entry` value, see [`into_mut`]. + /// + /// [`into_mut`]: #method.into_mut + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// use griddle::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// if let Entry::Occupied(mut o) = map.entry("poneyland") { + /// *o.get_mut() += 10; + /// assert_eq!(*o.get(), 22); + /// + /// // We can use the same Entry multiple times. + /// *o.get_mut() += 2; + /// } + /// + /// assert_eq!(map["poneyland"], 24); + /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn get_mut(&mut self) -> &mut V { unsafe { &mut self.elem.as_mut().1 } } - /// Gets a reference to the key and value in the entry. + /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// with a lifetime bound to the map itself. + /// + /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. + /// + /// [`get_mut`]: #method.get_mut + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// use griddle::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// *o.into_mut() += 10; + /// } + /// + /// assert_eq!(map["poneyland"], 22); + /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn get_key_value(&mut self) -> (&K, &V) { - unsafe { - let &(ref key, ref value) = self.elem.as_ref(); - (key, value) - } + pub fn into_mut(self) -> &'a mut V { + unsafe { &mut self.elem.as_mut().1 } } - /// Gets a mutable reference to the key and value in the entry. + /// Sets the value of the entry, and returns the entry's old value. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// use griddle::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(mut o) = map.entry("poneyland") { + /// assert_eq!(o.insert(15), 12); + /// } + /// + /// assert_eq!(map["poneyland"], 15); + /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn get_key_value_mut(&mut self) -> (&mut K, &mut V) { - unsafe { - let &mut (ref mut key, ref mut value) = self.elem.as_mut(); - (key, value) - } + pub fn insert(&mut self, mut value: V) -> V { + let old_value = self.get_mut(); + mem::swap(&mut value, old_value); + value } - /// Converts the OccupiedEntry into a mutable reference to the key and value in the entry - /// with a lifetime bound to the map itself. + /// Takes the value out of the entry, and returns it. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// use griddle::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// assert_eq!(o.remove(), 12); + /// } + /// + /// assert_eq!(map.contains_key("poneyland"), false); + /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn into_key_value(self) -> (&'a mut K, &'a mut V) { - unsafe { - let &mut (ref mut key, ref mut value) = self.elem.as_mut(); - (key, value) - } + pub fn remove(self) -> V { + self.remove_entry().1 } - /// Sets the value of the entry, and returns the entry's old value. + /// Replaces the entry, returning the old key and value. The new key in the hash map will be + /// the key used to create this entry. + /// + /// # Examples + /// + /// ``` + /// use griddle::hash_map::{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()); + /// + /// if let Entry::Occupied(entry) = map.entry(my_key.clone()) { + /// // Also replace the key with a handle to our other key. + /// let (old_key, old_value): (Rc, u32) = entry.replace_entry(16); + /// } + /// + /// assert_eq!(map[&my_key], 16); + /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn insert(&mut self, value: V) -> V { - mem::replace(self.get_mut(), value) - } + pub fn replace_entry(self, value: V) -> (K, V) { + let entry = unsafe { self.elem.as_mut() }; - /// Sets the value of the entry, and returns the entry's old value. - #[cfg_attr(feature = "inline-more", inline)] - pub fn insert_key(&mut self, key: K) -> K { - mem::replace(self.key_mut(), key) + let old_key = mem::replace(&mut entry.0, self.key.unwrap()); + let old_value = mem::replace(&mut entry.1, value); + + (old_key, old_value) } - /// Takes the value out of the entry, and returns it. + /// Replaces the key in the hash map with the key used to create this entry. + /// + /// # Examples + /// + /// ``` + /// use griddle::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. + /// + /// reclaim_memory(&mut map, &known_strings); + /// + /// 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(); + /// } + /// } + /// } + /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn remove(self) -> V { - self.remove_entry().1 + pub fn replace_key(self) -> K { + let entry = unsafe { self.elem.as_mut() }; + mem::replace(&mut entry.0, self.key.unwrap()) } +} - /// Take the ownership of the key and value from the map. +impl<'a, K, V, S> VacantEntry<'a, K, V, S> { + /// Gets a reference to the key that would be used when inserting a value + /// through the `VacantEntry`. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn remove_entry(self) -> (K, V) { - unsafe { self.table.remove(self.elem) } + pub fn key(&self) -> &K { + &self.key } -} -impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> { - /// Sets the value of the entry with the VacantEntry's key, - /// and returns a mutable reference to it. + /// Take ownership of the key. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// use griddle::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// if let Entry::Vacant(v) = map.entry("poneyland") { + /// v.into_key(); + /// } + /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn insert(self, key: K, value: V) -> (&'a mut K, &'a mut V) - where - K: Hash, - S: BuildHasher, - { - let mut hasher = self.hash_builder.build_hasher(); - key.hash(&mut hasher); - self.insert_hashed_nocheck(hasher.finish(), key, value) + pub fn into_key(self) -> K { + self.key } /// Sets the value of the entry with the VacantEntry's key, /// and returns a mutable reference to it. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashMap; + /// use griddle::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// if let Entry::Vacant(o) = map.entry("poneyland") { + /// o.insert(37); + /// } + /// assert_eq!(map["poneyland"], 37); + /// ``` #[cfg_attr(feature = "inline-more", inline)] - #[allow(clippy::shadow_unrelated)] - pub fn insert_hashed_nocheck(self, hash: u64, key: K, value: V) -> (&'a mut K, &'a mut V) + pub fn insert(self, value: V) -> &'a mut V where K: Hash, S: BuildHasher, { - let hash_builder = self.hash_builder; - self.insert_with_hasher(hash, key, value, |k| make_hash(hash_builder, k)) - } - - /// Set the value of an entry with a custom hasher function. - #[cfg_attr(feature = "inline-more", inline)] - pub fn insert_with_hasher( - self, - hash: u64, - key: K, - value: V, - hasher: H, - ) -> (&'a mut K, &'a mut V) - where - H: Fn(&K) -> u64, - { - unsafe { - let elem = self.table.insert(hash, (key, value), |x| hasher(&x.0)); - let &mut (ref mut k, ref mut v) = elem.as_mut(); - (k, v) - } + let hash_builder = &self.table.hash_builder; + let bucket = self.table.table.insert(self.hash, (self.key, value), |x| { + make_hash(hash_builder, &x.0) + }); + unsafe { &mut bucket.as_mut().1 } } #[cfg_attr(feature = "inline-more", inline)] - fn insert_entry(self, key: K, value: V) -> RawOccupiedEntryMut<'a, K, V> + fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, S> where K: Hash, S: BuildHasher, { - let hash_builder = self.hash_builder; - let mut hasher = self.hash_builder.build_hasher(); - key.hash(&mut hasher); - - let elem = self.table.insert(hasher.finish(), (key, value), |k| { - make_hash(hash_builder, &k.0) + let hash_builder = &self.table.hash_builder; + let elem = self.table.table.insert(self.hash, (self.key, value), |x| { + make_hash(hash_builder, &x.0) }); - - RawOccupiedEntryMut { + OccupiedEntry { + key: None, elem, table: self.table, } } } -impl Debug for RawEntryBuilderMut<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RawEntryBuilder").finish() +impl FromIterator<(K, V)> for HashMap +where + K: Eq + Hash, + S: BuildHasher + Default, +{ + #[cfg_attr(feature = "inline-more", inline)] + fn from_iter>(iter: T) -> Self { + let iter = iter.into_iter(); + let mut map = Self::with_capacity_and_hasher(iter.size_hint().0, S::default()); + iter.for_each(|(k, v)| { + map.insert(k, v); + }); + map } } -impl Debug for RawEntryMut<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - RawEntryMut::Vacant(ref v) => f.debug_tuple("RawEntry").field(v).finish(), - RawEntryMut::Occupied(ref o) => f.debug_tuple("RawEntry").field(o).finish(), - } +/// Inserts all new key-values from the iterator and replaces values with existing +/// keys with new values returned from the iterator. +impl Extend<(K, V)> for HashMap +where + K: Eq + Hash, + S: BuildHasher, +{ + #[cfg_attr(feature = "inline-more", inline)] + fn extend>(&mut self, iter: T) { + // Keys may be already present or show multiple times in the iterator. + // Reserve the entire hint lower bound if the map is empty. + // Otherwise reserve half the hint (rounded up), so the map + // will only resize twice in the worst case. + let iter = iter.into_iter(); + let reserve = if self.is_empty() { + iter.size_hint().0 + } else { + (iter.size_hint().0 + 1) / 2 + }; + self.reserve(reserve); + iter.for_each(move |(k, v)| { + self.insert(k, v); + }); } } -impl Debug for RawOccupiedEntryMut<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RawOccupiedEntryMut") - .field("key", self.key()) - .field("value", self.get()) - .finish() +impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap +where + K: Eq + Hash + Copy, + V: Copy, + S: BuildHasher, +{ + #[cfg_attr(feature = "inline-more", inline)] + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().map(|(&key, &value)| (key, value))); } } -impl Debug for RawVacantEntryMut<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RawVacantEntryMut").finish() +#[allow(dead_code)] +fn assert_covariance() { + fn map_key<'new>(v: HashMap<&'static str, u8>) -> HashMap<&'new str, u8> { + v } -} - -impl Debug for RawEntryBuilder<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RawEntryBuilder").finish() + fn map_val<'new>(v: HashMap) -> HashMap { + v + } + fn iter_key<'a, 'new>(v: Iter<'a, &'static str, u8>) -> Iter<'a, &'new str, u8> { + v + } + fn iter_val<'a, 'new>(v: Iter<'a, u8, &'static str>) -> Iter<'a, u8, &'new str> { + v + } + fn into_iter_key<'new>(v: IntoIter<&'static str, u8>) -> IntoIter<&'new str, u8> { + v + } + fn into_iter_val<'new>(v: IntoIter) -> IntoIter { + v + } + fn keys_key<'a, 'new>(v: Keys<'a, &'static str, u8>) -> Keys<'a, &'new str, u8> { + v + } + fn keys_val<'a, 'new>(v: Keys<'a, u8, &'static str>) -> Keys<'a, u8, &'new str> { + v + } + fn values_key<'a, 'new>(v: Values<'a, &'static str, u8>) -> Values<'a, &'new str, u8> { + v + } + fn values_val<'a, 'new>(v: Values<'a, u8, &'static str>) -> Values<'a, u8, &'new str> { + v + } + fn drain<'new>( + d: Drain<'static, &'static str, &'static str>, + ) -> Drain<'new, &'new str, &'new str> { + d } } From 6ca0eccf60db7d219c882e096645e6fdafa92c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Muci=C3=B1o?= Date: Sun, 9 Aug 2020 02:48:06 -0500 Subject: [PATCH 5/5] Port of the relevant changes from hashbrown regarding #183 "Avoid closures to improve compile times". --- src/map.rs | 98 ++++++++++++++++++++++++++++++++++++------------------ src/set.rs | 18 ++++++++-- 2 files changed, 81 insertions(+), 35 deletions(-) diff --git a/src/map.rs b/src/map.rs index c7505d3..70d6752 100644 --- a/src/map.rs +++ b/src/map.rs @@ -787,7 +787,11 @@ where K: Borrow, Q: Hash + Eq, { - self.get_key_value(k).map(|(_, v)| v) + // Avoid `Option::map` because it bloats LLVM IR. + match self.get_key_value(k) { + Some((_, v)) => Some(v), + None => None, + } } /// Returns the key-value pair corresponding to the supplied key. @@ -816,12 +820,14 @@ where Q: Hash + Eq, { let hash = make_hash(&self.hash_builder, k); - self.table - .find(hash, |x| k.eq(x.0.borrow())) - .map(|item| unsafe { + // Avoid `Option::map` because it bloats LLVM IR. + match self.table.find(hash, |x| k.eq(x.0.borrow())) { + Some(item) => unsafe { let &(ref key, ref value) = item.as_ref(); - (key, value) - }) + Some((key, value)) + }, + None => None, + } } /// Returns the key-value pair corresponding to the supplied key, with a mutable reference to value. @@ -854,12 +860,14 @@ where Q: Hash + Eq, { let hash = make_hash(&self.hash_builder, k); - self.table - .find(hash, |x| k.eq(x.0.borrow())) - .map(|item| unsafe { + // Avoid `Option::map` because it bloats LLVM IR. + match self.table.find(hash, |x| k.eq(x.0.borrow())) { + Some(item) => unsafe { let &mut (ref key, ref mut value) = item.as_mut(); - (key, value) - }) + Some((key, value)) + }, + None => None, + } } /// Returns `true` if the map contains a value for the specified key. @@ -918,9 +926,11 @@ where Q: Hash + Eq, { let hash = make_hash(&self.hash_builder, k); - self.table - .find(hash, |x| k.eq(x.0.borrow())) - .map(|item| unsafe { &mut item.as_mut().1 }) + // Avoid `Option::map` because it bloats LLVM IR. + match self.table.find(hash, |x| k.eq(x.0.borrow())) { + Some(item) => Some(unsafe { &mut item.as_mut().1 }), + None => None, + } } /// Inserts a key-value pair into the map. @@ -995,7 +1005,11 @@ where K: Borrow, Q: Hash + Eq, { - self.remove_entry(k).map(|(_, v)| v) + // Avoid `Option::map` because it bloats LLVM IR. + match self.remove_entry(k) { + Some((_, v)) => Some(v), + None => None, + } } /// Removes a key from the map, returning the stored key and value if the @@ -1478,13 +1492,13 @@ impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> { where F: FnMut(&K) -> bool, { - self.map - .table - .find(hash, |(k, _)| is_match(k)) - .map(|item| unsafe { + match self.map.table.find(hash, |(k, _)| is_match(k)) { + Some(item) => unsafe { let &(ref key, ref value) = item.as_ref(); - (key, value) - }) + Some((key, value)) + }, + None => None, + } } /// Access an entry by hash. @@ -1947,10 +1961,14 @@ impl<'a, K, V> Iterator for Iter<'a, K, V> { #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option<(&'a K, &'a V)> { - self.inner.next().map(|x| unsafe { - let r = x.as_ref(); - (&r.0, &r.1) - }) + // Avoid `Option::map` because it bloats LLVM IR. + match self.inner.next() { + Some(x) => unsafe { + let r = x.as_ref(); + Some((&r.0, &r.1)) + }, + None => None, + } } #[cfg_attr(feature = "inline-more", inline)] fn size_hint(&self) -> (usize, Option) { @@ -1971,10 +1989,14 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> { #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option<(&'a K, &'a mut V)> { - self.inner.next().map(|x| unsafe { - let r = x.as_mut(); - (&r.0, &mut r.1) - }) + // Avoid `Option::map` because it bloats LLVM IR. + match self.inner.next() { + Some(x) => unsafe { + let r = x.as_mut(); + Some((&r.0, &mut r.1)) + }, + None => None, + } } #[cfg_attr(feature = "inline-more", inline)] fn size_hint(&self) -> (usize, Option) { @@ -2030,7 +2052,11 @@ impl<'a, K, V> Iterator for Keys<'a, K, V> { #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option<&'a K> { - self.inner.next().map(|(k, _)| k) + // Avoid `Option::map` because it bloats LLVM IR. + match self.inner.next() { + Some((k, _)) => Some(k), + None => None, + } } #[cfg_attr(feature = "inline-more", inline)] fn size_hint(&self) -> (usize, Option) { @@ -2050,7 +2076,11 @@ impl<'a, K, V> Iterator for Values<'a, K, V> { #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option<&'a V> { - self.inner.next().map(|(_, v)| v) + // Avoid `Option::map` because it bloats LLVM IR. + match self.inner.next() { + Some((_, v)) => Some(v), + None => None, + } } #[cfg_attr(feature = "inline-more", inline)] fn size_hint(&self) -> (usize, Option) { @@ -2070,7 +2100,11 @@ impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option<&'a mut V> { - self.inner.next().map(|(_, v)| v) + // Avoid `Option::map` because it bloats LLVM IR. + match self.inner.next() { + Some((_, v)) => Some(v), + None => None, + } } #[cfg_attr(feature = "inline-more", inline)] fn size_hint(&self) -> (usize, Option) { diff --git a/src/set.rs b/src/set.rs index 744ad5d..02c6c33 100644 --- a/src/set.rs +++ b/src/set.rs @@ -628,7 +628,11 @@ where T: Borrow, Q: Hash + Eq, { - self.map.get_key_value(value).map(|(k, _)| k) + // Avoid `Option::map` because it bloats LLVM IR. + match self.map.get_key_value(value) { + Some((k, _)) => Some(k), + None => None, + } } /// Returns `true` if `self` has no elements in common with `other`. @@ -800,7 +804,11 @@ where T: Borrow, Q: Hash + Eq, { - self.map.remove_entry(value).map(|(k, _)| k) + // Avoid `Option::map` because it bloats LLVM IR. + match self.map.remove_entry(value) { + Some((k, _)) => Some(k), + None => None, + } } } @@ -1188,7 +1196,11 @@ impl Iterator for IntoIter { #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option { - self.iter.next().map(|(k, _)| k) + // Avoid `Option::map` because it bloats LLVM IR. + match self.iter.next() { + Some((k, _)) => Some(k), + None => None, + } } #[cfg_attr(feature = "inline-more", inline)] fn size_hint(&self) -> (usize, Option) {