From 6af55a7c61be881bfb56c79dcea9a56b21dad413 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Thu, 17 May 2018 00:27:19 -0400 Subject: [PATCH 01/10] WIP: add raw_entry API to HashMap --- src/libstd/collections/hash/map.rs | 740 +++++++++++++++++++++++++++-- src/libstd/lib.rs | 1 + 2 files changed, 704 insertions(+), 37 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 91912e5f241..82bd2c695b1 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -20,7 +20,7 @@ use fmt::{self, Debug}; use hash::{Hash, Hasher, BuildHasher, SipHasher13}; use iter::{FromIterator, FusedIterator}; use mem::{self, replace}; -use ops::{Deref, Index}; +use ops::{Deref, DerefMut, Index}; use sys; use super::table::{self, Bucket, EmptyBucket, Fallibility, FullBucket, FullBucketMut, RawTable, @@ -438,6 +438,23 @@ fn search_hashed(table: M, hash: SafeHash, is_match: F) -> InternalE search_hashed_nonempty(table, hash, is_match) } +/// Search for a pre-hashed key. +/// If you don't already know the hash, use search or search_mut instead +#[inline] +fn search_hashed_mut(table: M, hash: SafeHash, is_match: F) -> InternalEntry + where M: DerefMut>, + F: FnMut(&mut K) -> bool +{ + // This is the only function where capacity can be zero. To avoid + // undefined behavior when Bucket::new gets the raw bucket in this + // case, immediately return the appropriate search result. + if table.capacity() == 0 { + return InternalEntry::TableIsEmpty; + } + + search_hashed_nonempty_mut(table, hash, is_match) +} + /// Search for a pre-hashed key when the hash map is known to be non-empty. #[inline] fn search_hashed_nonempty(table: M, hash: SafeHash, mut is_match: F) @@ -488,6 +505,56 @@ fn search_hashed_nonempty(table: M, hash: SafeHash, mut is_match: F) } } +/// Search for a pre-hashed key when the hash map is known to be non-empty. +#[inline] +fn search_hashed_nonempty_mut(table: M, hash: SafeHash, mut is_match: F) + -> InternalEntry + where M: DerefMut>, + F: FnMut(&mut K) -> bool +{ + // Do not check the capacity as an extra branch could slow the lookup. + + let size = table.size(); + let mut probe = Bucket::new(table, hash); + let mut displacement = 0; + + loop { + let mut full = match probe.peek() { + Empty(bucket) => { + // Found a hole! + return InternalEntry::Vacant { + hash, + elem: NoElem(bucket, displacement), + }; + } + Full(bucket) => bucket, + }; + + let probe_displacement = full.displacement(); + + if probe_displacement < displacement { + // Found a luckier bucket than me. + // We can finish the search early if we hit any bucket + // with a lower distance to initial bucket than we've probed. + return InternalEntry::Vacant { + hash, + elem: NeqElem(full, probe_displacement), + }; + } + + // If the hash doesn't match, it can't be this one.. + if hash == full.hash() { + // If the key doesn't match, it can't be this one.. + if is_match(full.read_mut().0) { + return InternalEntry::Occupied { elem: full }; + } + } + displacement += 1; + probe = full.next(); + debug_assert!(displacement <= size); + } +} + fn pop_internal(starting_bucket: FullBucketMut) -> (K, V, &mut RawTable) { @@ -1484,6 +1551,47 @@ impl HashMap } } +impl HashMap + where K: Eq + Hash, + S: BuildHasher +{ + /// 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 hash and + /// then manually searched. After this, insertions into the entry also + /// 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 HashMap where the key type can't or shouldn't be hashed and/or compared + /// + /// Unless you are in such a situation, higher-level and more foolproof APIs like + /// `entry` should be preferred. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn raw_entry(&mut self) -> RawEntryBuilder { + self.reserve(1); + RawEntryBuilder { map: self } + } + + /// Creates a raw immutable entry builder for the HashMap. + /// + /// This is useful for + /// * Hash memoization + /// * Querying a HashMap where the key type can't or shouldn't be hashed and/or compared + /// + /// Unless you are in such a situation, higher-level and more foolproof APIs like + /// `entry` should be preferred. + /// + /// Immutable raw entries have very limited use; you might instead want `raw_entry`. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn raw_entry_immut(&self) -> RawImmutableEntryBuilder { + RawImmutableEntryBuilder { map: self } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for HashMap where K: Eq + Hash, @@ -1709,14 +1817,13 @@ impl<'a, K, V> InternalEntry> { InternalEntry::Occupied { elem } => { Some(Occupied(OccupiedEntry { key: Some(key), - elem, + entry: RawOccupiedEntry { elem }, })) } InternalEntry::Vacant { hash, elem } => { Some(Vacant(VacantEntry { - hash, key, - elem, + entry: RawVacantEntry { hash, elem } })) } InternalEntry::TableIsEmpty => None, @@ -1724,6 +1831,584 @@ impl<'a, K, V> InternalEntry> { } } +/// WIP +#[unstable(feature = "raw_entry", issue = "42069")] +pub struct RawEntryBuilder<'a, K: 'a, V: 'a, S: 'a> { + map: &'a mut HashMap, +} + +/// WIP +#[unstable(feature = "raw_entry", issue = "42069")] +pub struct RawEntryBuilderHashed<'a, K: 'a, V: 'a> { + map: &'a mut RawTable, + hash: SafeHash, +} + +/// WIP +#[unstable(feature = "raw_entry", issue = "42069")] +pub enum RawEntry<'a, K: 'a, V: 'a> { + /// WIP + Occupied(RawOccupiedEntry<'a, K, V>), + /// WIP + Vacant(RawVacantEntry<'a, K, V>), +} + +/// WIP +#[unstable(feature = "raw_entry", issue = "42069")] +pub struct RawOccupiedEntry<'a, K: 'a, V: 'a> { + elem: FullBucket>, +} + +/// WIP +#[unstable(feature = "raw_entry", issue = "42069")] +pub struct RawVacantEntry<'a, K: 'a, V: 'a> { + hash: SafeHash, + elem: VacantEntryState>, +} + +/// WIP +#[unstable(feature = "raw_entry", issue = "42069")] +pub struct RawImmutableEntryBuilder<'a, K: 'a, V: 'a, S: 'a> { + map: &'a HashMap, +} + +/// WIP +#[unstable(feature = "raw_entry", issue = "42069")] +pub struct RawImmutableEntryBuilderHashed<'a, K: 'a, V: 'a> { + map: &'a RawTable, + hash: SafeHash, +} + +impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> + where S: BuildHasher, +{ + /// Initializes the raw entry builder with the hash of the given query value. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn hash_by(self, k: &Q) -> RawEntryBuilderHashed<'a, K, V> + where Q: Hash + { + self.hash_with(|mut hasher| { + k.hash(&mut hasher); + hasher.finish() + }) + } + + /// Initializes the raw entry builder with the hash yielded by the given function. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn hash_with(self, func: F) -> RawEntryBuilderHashed<'a, K, V> + where F: FnOnce(S::Hasher) -> u64 + { + let hasher = self.map.hash_builder.build_hasher(); + let hash = SafeHash::new(func(hasher)); + + RawEntryBuilderHashed { map: &mut self.map.table, hash } + } + + /// Searches for the location of the raw entry with the given query value. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn search_by(self, k: &Q) -> RawEntry<'a, K, V> + where K: Borrow, + Q: Eq + Hash + { + self.hash_by(k).search_by(k) + } +} + +impl<'a, K, V> RawEntryBuilderHashed<'a, K, V> +{ + /// Searches for the location of the raw entry with the given query value. + /// + /// Note that it isn't required that the query value be hashable, as the + /// builder's hash is used. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn search_by(self, k: &Q) -> RawEntry<'a, K, V> + where K: Borrow, + Q: Eq, + { + // I don't know why we need this `&mut -> &` transform to resolve Borrow, but we do + self.search_with(|key| (&*key).borrow() == k) + } + + /// Searches for the location of the raw entry with the given comparison function. + /// + /// Note that mutable access is given to each key that is visited, because + /// this land is truly godless, and *someone* might have a use for this. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn search_with(self, func: F) -> RawEntry<'a, K, V> + where F: FnMut(&mut K) -> bool, + { + match search_hashed_mut(self.map, self.hash, func) { + InternalEntry::Occupied { elem } => { + RawEntry::Occupied(RawOccupiedEntry { elem }) + } + InternalEntry::Vacant { hash, elem } => { + RawEntry::Vacant(RawVacantEntry { hash, elem }) + } + InternalEntry::TableIsEmpty => { + unreachable!() + } + } + } +} + +impl<'a, K, V, S> RawImmutableEntryBuilder<'a, K, V, S> + where S: BuildHasher, +{ + /// Initializes the raw entry builder with the hash of the given query value. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn hash_by(self, k: &Q) -> RawImmutableEntryBuilderHashed<'a, K, V> + where Q: Hash + { + self.hash_with(|mut hasher| { + k.hash(&mut hasher); + hasher.finish() + }) + } + + /// Initializes the raw entry builder with the hash yielded by the given function. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn hash_with(self, func: F) -> RawImmutableEntryBuilderHashed<'a, K, V> + where F: FnOnce(S::Hasher) -> u64 + { + let hasher = self.map.hash_builder.build_hasher(); + let hash = SafeHash::new(func(hasher)); + + RawImmutableEntryBuilderHashed { map: &self.map.table, hash } + } + + /// Searches for the location of the raw entry with the given query value. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn search_by(self, k: &Q) -> Option<(&'a K, &'a V)> + where K: Borrow, + Q: Eq + Hash + { + self.hash_by(k).search_by(k) + } +} + +impl<'a, K, V> RawImmutableEntryBuilderHashed<'a, K, V> +{ + /// Searches for the location of the raw entry with the given query value. + /// + /// Note that it isn't required that the query value be hashable, as the + /// builder's hash is used. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn search_by(self, k: &Q) -> Option<(&'a K, &'a V)> + where K: Borrow, + Q: Eq, + { + self.search_with(|key| key.borrow() == k) + } + + /// Searches for the location of the raw entry with the given comparison function. + /// + /// Note that mutable access is given to each key that is visited, because + /// this land is truly godless, and *someone* might have a use for this. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn search_with(self, func: F) -> Option<(&'a K, &'a V)> + where F: FnMut(&K) -> bool, + { + match search_hashed(self.map, self.hash, func) { + InternalEntry::Occupied { elem } => { + Some(elem.into_refs()) + } + InternalEntry::Vacant { .. } | InternalEntry::TableIsEmpty => { + None + } + } + } +} + +impl<'a, K, V> RawEntry<'a, K, V> { + /// 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 std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// + /// *map.entry("poneyland").or_insert(12) += 10; + /// assert_eq!(map["poneyland"], 22); + /// ``` + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn or_insert(self, default_key: K, default_val: V) -> (&'a mut K, &'a mut V) { + match self { + RawEntry::Occupied(entry) => entry.into_kv(), + RawEntry::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. + /// + /// # Examples + /// + /// ``` + /// use std::collections::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()); + /// ``` + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn or_insert_with(self, default: F) -> (&'a mut K, &'a mut V) + where F: FnOnce() -> (K, V), + { + match self { + RawEntry::Occupied(entry) => entry.into_kv(), + RawEntry::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 std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// 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); + /// ``` + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn and_modify(self, f: F) -> Self + where F: FnOnce(&mut K, &mut V) + { + match self { + RawEntry::Occupied(mut entry) => { + { + let (k, v) = entry.kv_mut(); + f(k, v); + } + RawEntry::Occupied(entry) + }, + RawEntry::Vacant(entry) => RawEntry::Vacant(entry), + } + } +} + +impl<'a, K, V> RawOccupiedEntry<'a, K, V> { + /// Gets a reference to the key in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn key(&self) -> &K { + self.elem.read().0 + } + + /// Gets a mutable reference to the key in the entry. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn key_mut(&mut self) -> &mut K { + self.elem.read_mut().0 + } + + /// Converts the entry into a mutable reference to the key in the entry + /// with a lifetime bound to the map itself. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn into_key(self) -> &'a mut K { + self.elem.into_mut_refs().0 + } + + /// Gets a reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::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); + /// } + /// ``` + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn get(&self) -> &V { + self.elem.read().1 + } + + /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// with a lifetime bound to the map itself. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::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); + /// ``` + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn into_mut(self) -> &'a mut V { + self.elem.into_mut_refs().1 + } + + /// Gets a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::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!(map["poneyland"], 22); + /// ``` + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn get_mut(&mut self) -> &mut V { + self.elem.read_mut().1 + } + + /// Gets a reference to the key and value in the entry. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn kv(&mut self) -> (&K, &V) { + self.elem.read() + } + + /// Gets a mutable reference to the key and value in the entry. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn kv_mut(&mut self) -> (&mut K, &mut V) { + self.elem.read_mut() + } + + /// Converts the OccupiedEntry into a mutable reference to the key and value in the entry + /// with a lifetime bound to the map itself. + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn into_kv(self) -> (&'a mut K, &'a mut V) { + self.elem.into_mut_refs() + } + + /// Sets the value of the entry, and returns the entry's old value. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::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); + /// ``` + #[unstable(feature = "raw_entry", issue = "42069")] + 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. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::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); + /// ``` + #[unstable(feature = "raw_entry", issue = "42069")] + 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. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::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); + /// ``` + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn remove(self) -> V { + pop_internal(self.elem).1 + } + + + /// Take the ownership of the key and value from the map. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::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); + /// ``` + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn remove_entry(self) -> (K, V) { + let (k, v, _) = pop_internal(self.elem); + (k, v) + } +} + +impl<'a, K, V> RawVacantEntry<'a, K, V> { + /// Sets the value of the entry with the VacantEntry's key, + /// and returns a mutable reference to it. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::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); + /// ``` + #[unstable(feature = "raw_entry", issue = "42069")] + pub fn insert(self, key: K, value: V) -> (&'a mut K, &'a mut V) { + let b = match self.elem { + NeqElem(mut bucket, disp) => { + if disp >= DISPLACEMENT_THRESHOLD { + bucket.table_mut().set_tag(true); + } + robin_hood(bucket, disp, self.hash, key, value) + }, + NoElem(mut bucket, disp) => { + if disp >= DISPLACEMENT_THRESHOLD { + bucket.table_mut().set_tag(true); + } + bucket.put(self.hash, key, value) + }, + }; + b.into_mut_refs() + } +} + +#[unstable(feature = "raw_entry", issue = "42069")] +impl<'a, K, V, S> Debug for RawEntryBuilder<'a, K, V, S> { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } +} + +/// WIP +#[unstable(feature = "raw_entry", issue = "42069")] +impl<'a, K, V> Debug for RawEntryBuilderHashed<'a, K, V> { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } +} + +/// WIP +#[unstable(feature = "raw_entry", issue = "42069")] +impl<'a, K, V> Debug for RawEntry<'a, K, V> { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } +} + +/// WIP +#[unstable(feature = "raw_entry", issue = "42069")] +impl<'a, K, V> Debug for RawOccupiedEntry<'a, K, V> { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } +} + +/// WIP +#[unstable(feature = "raw_entry", issue = "42069")] +impl<'a, K, V> Debug for RawVacantEntry<'a, K, V> { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } +} + +/// WIP +#[unstable(feature = "raw_entry", issue = "42069")] +impl<'a, K, V, S> Debug for RawImmutableEntryBuilder<'a, K, V, S> { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } +} + +/// WIP +#[unstable(feature = "raw_entry", issue = "42069")] +impl<'a, K, V> Debug for RawImmutableEntryBuilderHashed<'a, K, V> { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } +} + /// 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`]. @@ -1768,7 +2453,7 @@ impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for Entry<'a, K, V> { #[stable(feature = "rust1", since = "1.0.0")] pub struct OccupiedEntry<'a, K: 'a, V: 'a> { key: Option, - elem: FullBucket>, + entry: RawOccupiedEntry<'a, K, V>, } #[stable(feature= "debug_hash_map", since = "1.12.0")] @@ -1787,9 +2472,8 @@ impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for OccupiedEntry<'a, K, V> { /// [`Entry`]: enum.Entry.html #[stable(feature = "rust1", since = "1.0.0")] pub struct VacantEntry<'a, K: 'a, V: 'a> { - hash: SafeHash, key: K, - elem: VacantEntryState>, + entry: RawVacantEntry<'a, K, V>, } #[stable(feature= "debug_hash_map", since = "1.12.0")] @@ -2213,7 +2897,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "map_entry_keys", since = "1.10.0")] pub fn key(&self) -> &K { - self.elem.read().0 + self.entry.key() } /// Take the ownership of the key and value from the map. @@ -2236,8 +2920,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] pub fn remove_entry(self) -> (K, V) { - let (k, v, _) = pop_internal(self.elem); - (k, v) + self.entry.remove_entry() } /// Gets a reference to the value in the entry. @@ -2257,7 +2940,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn get(&self) -> &V { - self.elem.read().1 + self.entry.get() } /// Gets a mutable reference to the value in the entry. @@ -2289,7 +2972,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn get_mut(&mut self) -> &mut V { - self.elem.read_mut().1 + self.entry.get_mut() } /// Converts the OccupiedEntry into a mutable reference to the value in the entry @@ -2317,7 +3000,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn into_mut(self) -> &'a mut V { - self.elem.into_mut_refs().1 + self.entry.into_mut() } /// Sets the value of the entry, and returns the entry's old value. @@ -2338,10 +3021,8 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// assert_eq!(map["poneyland"], 15); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - 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 { + self.entry.insert(value) } /// Takes the value out of the entry, and returns it. @@ -2363,7 +3044,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn remove(self) -> V { - pop_internal(self.elem).1 + self.entry.remove() } /// Returns a key that was used for search. @@ -2396,7 +3077,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[unstable(feature = "map_entry_replace", issue = "44286")] pub fn replace_entry(mut self, value: V) -> (K, V) { - let (old_key, old_value) = self.elem.read_mut(); + let (old_key, old_value) = self.entry.kv_mut(); let old_key = mem::replace(old_key, self.key.unwrap()); let old_value = mem::replace(old_value, value); @@ -2431,8 +3112,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[unstable(feature = "map_entry_replace", issue = "44286")] pub fn replace_key(mut self) -> K { - let (old_key, _) = self.elem.read_mut(); - mem::replace(old_key, self.key.unwrap()) + mem::replace(self.entry.key_mut(), self.key.unwrap()) } } @@ -2490,21 +3170,7 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(self, value: V) -> &'a mut V { - let b = match self.elem { - NeqElem(mut bucket, disp) => { - if disp >= DISPLACEMENT_THRESHOLD { - bucket.table_mut().set_tag(true); - } - robin_hood(bucket, disp, self.hash, self.key, value) - }, - NoElem(mut bucket, disp) => { - if disp >= DISPLACEMENT_THRESHOLD { - bucket.table_mut().set_tag(true); - } - bucket.put(self.hash, self.key, value) - }, - }; - b.into_mut_refs().1 + self.entry.insert(self.key, value).1 } } @@ -2715,7 +3381,7 @@ impl super::Recover for HashMap match self.entry(key) { Occupied(mut occupied) => { let key = occupied.take_key().unwrap(); - Some(mem::replace(occupied.elem.read_mut().0, key)) + Some(mem::replace(occupied.entry.key_mut(), key)) } Vacant(vacant) => { vacant.insert(()); diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index e7195b3e21e..591d51184cf 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -284,6 +284,7 @@ #![feature(prelude_import)] #![feature(ptr_internals)] #![feature(raw)] +#![feature(raw_entry)] #![feature(rustc_attrs)] #![feature(rustc_const_unstable)] #![feature(std_internals)] From 9c566e64faf66603a76b3f17793c7dad8e64fdab Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Fri, 25 May 2018 14:56:21 -0400 Subject: [PATCH 02/10] progress on raw_entry --- src/libstd/collections/hash/map.rs | 460 +++++++++++++++-------------- src/libstd/lib.rs | 3 +- 2 files changed, 233 insertions(+), 230 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 82bd2c695b1..328962af33e 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -1558,19 +1558,39 @@ 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 hash and - /// then manually searched. After this, insertions into the entry also + /// 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 HashMap where the key type can't or shouldn't be hashed and/or compared + /// * 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 - /// `entry` should be preferred. - #[unstable(feature = "raw_entry", issue = "42069")] + /// 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 eratically, with two keys randomly masking eachother. Implementations + /// are free to assume this doesn't happen (within the limits of memory-safety). + /// + /// # Examples + /// + /// + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn raw_entry(&mut self) -> RawEntryBuilder { self.reserve(1); RawEntryBuilder { map: self } @@ -1578,15 +1598,20 @@ impl HashMap /// 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 - /// * Querying a HashMap where the key type can't or shouldn't be hashed and/or compared + /// * 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 - /// `entry` should be preferred. + /// `get` should be preferred. /// /// Immutable raw entries have very limited use; you might instead want `raw_entry`. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry_immut", issue = "42069")] pub fn raw_entry_immut(&self) -> RawImmutableEntryBuilder { RawImmutableEntryBuilder { map: self } } @@ -1831,49 +1856,73 @@ impl<'a, K, V> InternalEntry> { } } -/// WIP -#[unstable(feature = "raw_entry", issue = "42069")] +/// A builder for computing where in a HashMap a key-value pair would be stored. +/// +/// See the [`HashMap::raw_entry`][] docs for usage examples. +#[unstable(feature = "hash_raw_entry", issue = "42069")] pub struct RawEntryBuilder<'a, K: 'a, V: 'a, S: 'a> { map: &'a mut HashMap, } -/// WIP -#[unstable(feature = "raw_entry", issue = "42069")] +/// A builder for computing where in a HashMap a key-value pair would be stored, +/// where the hash has already been specified. +/// +/// See the [`HashMap::raw_entry`][] docs for usage examples. +#[unstable(feature = "hash_raw_entry", issue = "42069")] pub struct RawEntryBuilderHashed<'a, K: 'a, V: 'a> { map: &'a mut RawTable, hash: SafeHash, } -/// WIP -#[unstable(feature = "raw_entry", issue = "42069")] +/// 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 from the [`raw_entry`] method on [`HashMap`]. +/// +/// [`HashMap`]: struct.HashMap.html +/// [`Entry`]: struct.Entry.html +/// [`raw_entry`]: struct.HashMap.html#method.raw_entry +#[unstable(feature = "hash_raw_entry", issue = "42069")] pub enum RawEntry<'a, K: 'a, V: 'a> { - /// WIP + /// An occupied entry. Occupied(RawOccupiedEntry<'a, K, V>), - /// WIP + /// A vacant entry. Vacant(RawVacantEntry<'a, K, V>), } -/// WIP -#[unstable(feature = "raw_entry", issue = "42069")] +/// A view into an occupied entry in a `HashMap`. +/// It is part of the [`RawEntry`] enum. +/// +/// [`RawEntry`]: enum.RawEntry.html +#[unstable(feature = "hash_raw_entry", issue = "42069")] pub struct RawOccupiedEntry<'a, K: 'a, V: 'a> { elem: FullBucket>, } -/// WIP -#[unstable(feature = "raw_entry", issue = "42069")] +/// A view into a vacant entry in a `HashMap`. +/// It is part of the [`RawEntry`] enum. +/// +/// [`RawEntry`]: enum.RawEntry.html +#[unstable(feature = "hash_raw_entry", issue = "42069")] pub struct RawVacantEntry<'a, K: 'a, V: 'a> { hash: SafeHash, elem: VacantEntryState>, } -/// WIP -#[unstable(feature = "raw_entry", issue = "42069")] +/// A builder for computing where in a HashMap a key-value pair would be stored. +/// +/// See the [`HashMap::raw_entry_immut`][] docs for usage examples. +#[unstable(feature = "hash_raw_entry_immut", issue = "42069")] pub struct RawImmutableEntryBuilder<'a, K: 'a, V: 'a, S: 'a> { map: &'a HashMap, } -/// WIP -#[unstable(feature = "raw_entry", issue = "42069")] +/// A builder for computing where in a HashMap a key-value pair would be stored, +/// where the hash has already been specified. +/// +/// See the [`HashMap::raw_entry_immut`][] docs for usage examples. +#[unstable(feature = "hash_raw_entry_immut", issue = "42069")] pub struct RawImmutableEntryBuilderHashed<'a, K: 'a, V: 'a> { map: &'a RawTable, hash: SafeHash, @@ -1883,7 +1932,7 @@ impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> where S: BuildHasher, { /// Initializes the raw entry builder with the hash of the given query value. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn hash_by(self, k: &Q) -> RawEntryBuilderHashed<'a, K, V> where Q: Hash { @@ -1894,7 +1943,7 @@ impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> } /// Initializes the raw entry builder with the hash yielded by the given function. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn hash_with(self, func: F) -> RawEntryBuilderHashed<'a, K, V> where F: FnOnce(S::Hasher) -> u64 { @@ -1905,7 +1954,7 @@ impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> } /// Searches for the location of the raw entry with the given query value. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn search_by(self, k: &Q) -> RawEntry<'a, K, V> where K: Borrow, Q: Eq + Hash @@ -1920,7 +1969,7 @@ impl<'a, K, V> RawEntryBuilderHashed<'a, K, V> /// /// Note that it isn't required that the query value be hashable, as the /// builder's hash is used. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn search_by(self, k: &Q) -> RawEntry<'a, K, V> where K: Borrow, Q: Eq, @@ -1933,7 +1982,7 @@ impl<'a, K, V> RawEntryBuilderHashed<'a, K, V> /// /// Note that mutable access is given to each key that is visited, because /// this land is truly godless, and *someone* might have a use for this. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn search_with(self, func: F) -> RawEntry<'a, K, V> where F: FnMut(&mut K) -> bool, { @@ -1955,7 +2004,7 @@ impl<'a, K, V, S> RawImmutableEntryBuilder<'a, K, V, S> where S: BuildHasher, { /// Initializes the raw entry builder with the hash of the given query value. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry_immut", issue = "42069")] pub fn hash_by(self, k: &Q) -> RawImmutableEntryBuilderHashed<'a, K, V> where Q: Hash { @@ -1966,7 +2015,7 @@ impl<'a, K, V, S> RawImmutableEntryBuilder<'a, K, V, S> } /// Initializes the raw entry builder with the hash yielded by the given function. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry_immut", issue = "42069")] pub fn hash_with(self, func: F) -> RawImmutableEntryBuilderHashed<'a, K, V> where F: FnOnce(S::Hasher) -> u64 { @@ -1977,7 +2026,7 @@ impl<'a, K, V, S> RawImmutableEntryBuilder<'a, K, V, S> } /// Searches for the location of the raw entry with the given query value. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry_immut", issue = "42069")] pub fn search_by(self, k: &Q) -> Option<(&'a K, &'a V)> where K: Borrow, Q: Eq + Hash @@ -1992,7 +2041,7 @@ impl<'a, K, V> RawImmutableEntryBuilderHashed<'a, K, V> /// /// Note that it isn't required that the query value be hashable, as the /// builder's hash is used. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry_immut", issue = "42069")] pub fn search_by(self, k: &Q) -> Option<(&'a K, &'a V)> where K: Borrow, Q: Eq, @@ -2004,7 +2053,7 @@ impl<'a, K, V> RawImmutableEntryBuilderHashed<'a, K, V> /// /// Note that mutable access is given to each key that is visited, because /// this land is truly godless, and *someone* might have a use for this. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry_immut", issue = "42069")] pub fn search_with(self, func: F) -> Option<(&'a K, &'a V)> where F: FnMut(&K) -> bool, { @@ -2021,7 +2070,7 @@ impl<'a, K, V> RawImmutableEntryBuilderHashed<'a, K, V> impl<'a, K, V> RawEntry<'a, K, V> { /// 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 /// @@ -2029,14 +2078,14 @@ impl<'a, K, V> RawEntry<'a, K, V> { /// use std::collections::HashMap; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); + /// map.raw_entry().search_by("poneyland").or_insert("poneyland", 12); /// /// assert_eq!(map["poneyland"], 12); /// - /// *map.entry("poneyland").or_insert(12) += 10; + /// *map.raw_entry().search_by("poneyland").or_insert("poneyland", 12).1 += 10; /// assert_eq!(map["poneyland"], 22); /// ``` - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn or_insert(self, default_key: K, default_val: V) -> (&'a mut K, &'a mut V) { match self { RawEntry::Occupied(entry) => entry.into_kv(), @@ -2045,7 +2094,7 @@ impl<'a, K, V> RawEntry<'a, K, V> { } /// 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 /// @@ -2053,13 +2102,14 @@ impl<'a, K, V> RawEntry<'a, K, V> { /// use std::collections::HashMap; /// /// let mut map: HashMap<&str, String> = HashMap::new(); - /// let s = "hoho".to_string(); /// - /// map.entry("poneyland").or_insert_with(|| s); + /// map.raw_entry().search_by("poneyland").or_insert_with(|| { + /// ("poneyland".to_string(), "hoho".to_string()) + /// }); /// /// assert_eq!(map["poneyland"], "hoho".to_string()); /// ``` - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn or_insert_with(self, default: F) -> (&'a mut K, &'a mut V) where F: FnOnce() -> (K, V), { @@ -2082,17 +2132,19 @@ impl<'a, K, V> RawEntry<'a, K, V> { /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// - /// map.entry("poneyland") - /// .and_modify(|e| { *e += 1 }) - /// .or_insert(42); + /// map.raw_entry() + /// .search_by("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() + /// .search_by("poneyland") + /// .and_modify(|_k, v| { *v += 1 }) + /// .or_insert("poneyland", 42); /// assert_eq!(map["poneyland"], 43); /// ``` - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn and_modify(self, f: F) -> Self where F: FnOnce(&mut K, &mut V) { @@ -2111,206 +2163,83 @@ impl<'a, K, V> RawEntry<'a, K, V> { impl<'a, K, V> RawOccupiedEntry<'a, K, V> { /// Gets a reference to the key in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn key(&self) -> &K { self.elem.read().0 } /// Gets a mutable reference to the key in the entry. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn key_mut(&mut self) -> &mut K { self.elem.read_mut().0 } /// Converts the entry into a mutable reference to the key in the entry /// with a lifetime bound to the map itself. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn into_key(self) -> &'a mut K { self.elem.into_mut_refs().0 } /// Gets a reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::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); - /// } - /// ``` - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn get(&self) -> &V { self.elem.read().1 } /// Converts the OccupiedEntry into a mutable reference to the value in the entry /// with a lifetime bound to the map itself. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::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); - /// ``` - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn into_mut(self) -> &'a mut V { self.elem.into_mut_refs().1 } /// Gets a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::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!(map["poneyland"], 22); - /// ``` - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn get_mut(&mut self) -> &mut V { self.elem.read_mut().1 } /// Gets a reference to the key and value in the entry. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn kv(&mut self) -> (&K, &V) { self.elem.read() } /// Gets a mutable reference to the key and value in the entry. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn kv_mut(&mut self) -> (&mut K, &mut V) { self.elem.read_mut() } /// Converts the OccupiedEntry into a mutable reference to the key and value in the entry /// with a lifetime bound to the map itself. - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn into_kv(self) -> (&'a mut K, &'a mut V) { self.elem.into_mut_refs() } /// Sets the value of the entry, and returns the entry's old value. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::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); - /// ``` - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] 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. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::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); - /// ``` - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] 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. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::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); - /// ``` - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn remove(self) -> V { pop_internal(self.elem).1 } /// Take the ownership of the key and value from the map. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::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); - /// ``` - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn remove_entry(self) -> (K, V) { let (k, v, _) = pop_internal(self.elem); (k, v) @@ -2320,21 +2249,7 @@ impl<'a, K, V> RawOccupiedEntry<'a, K, V> { impl<'a, K, V> RawVacantEntry<'a, K, V> { /// Sets the value of the entry with the VacantEntry's key, /// and returns a mutable reference to it. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::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); - /// ``` - #[unstable(feature = "raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn insert(self, key: K, value: V) -> (&'a mut K, &'a mut V) { let b = match self.elem { NeqElem(mut bucket, disp) => { @@ -2354,58 +2269,74 @@ impl<'a, K, V> RawVacantEntry<'a, K, V> { } } -#[unstable(feature = "raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "42069")] impl<'a, K, V, S> Debug for RawEntryBuilder<'a, K, V, S> { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { - unimplemented!() + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("RawEntryBuilder") + .finish() } } -/// WIP -#[unstable(feature = "raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "42069")] impl<'a, K, V> Debug for RawEntryBuilderHashed<'a, K, V> { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { - unimplemented!() + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("RawEntryBuilderHashed") + .field("hash", &self.hash.inspect()) + .finish() } } -/// WIP -#[unstable(feature = "raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "42069")] impl<'a, K, V> Debug for RawEntry<'a, K, V> { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { - unimplemented!() + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + RawEntry::Vacant(ref v) => { + f.debug_tuple("RawEntry") + .field(v) + .finish() + } + RawEntry::Occupied(ref o) => { + f.debug_tuple("RawEntry") + .field(o) + .finish() + } + } } } -/// WIP -#[unstable(feature = "raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "42069")] impl<'a, K, V> Debug for RawOccupiedEntry<'a, K, V> { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { - unimplemented!() + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("RawOccupiedEntry") + .field("key", self.key()) + .field("value", self.get()) + .finish() } } -/// WIP -#[unstable(feature = "raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "42069")] impl<'a, K, V> Debug for RawVacantEntry<'a, K, V> { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { - unimplemented!() + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("RawVacantEntry") + .field("hash", &self.hash.inspect()) + .finish() } } -/// WIP -#[unstable(feature = "raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry_immut", issue = "42069")] impl<'a, K, V, S> Debug for RawImmutableEntryBuilder<'a, K, V, S> { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { - unimplemented!() + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("RawImmutableEntryBuilder") + .finish() } } -/// WIP -#[unstable(feature = "raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry_immut", issue = "42069")] impl<'a, K, V> Debug for RawImmutableEntryBuilderHashed<'a, K, V> { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { - unimplemented!() + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("RawImmutableEntryBuilderHashed") + .field("hash", &self.hash.inspect()) + .finish() } } @@ -3432,7 +3363,6 @@ fn assert_covariance() { #[cfg(test)] mod test_map { use super::HashMap; - use super::Entry::{Occupied, Vacant}; use super::RandomState; use cell::RefCell; use rand::{thread_rng, Rng}; @@ -3671,6 +3601,8 @@ mod test_map { #[test] fn test_empty_entry() { + use super::Entry::{Occupied, Vacant}; + let mut m: HashMap = HashMap::new(); match m.entry(0) { Occupied(_) => panic!(), @@ -4129,6 +4061,8 @@ mod test_map { #[test] fn test_entry() { + use super::Entry::{Occupied, Vacant}; + let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; let mut map: HashMap<_, _> = xs.iter().cloned().collect(); @@ -4182,6 +4116,9 @@ mod test_map { #[test] fn test_entry_take_doesnt_corrupt() { #![allow(deprecated)] //rand + + use super::Entry::{Occupied, Vacant}; + // Test for #19292 fn check(m: &HashMap) { for k in m.keys() { @@ -4256,6 +4193,8 @@ mod test_map { #[test] fn test_occupied_entry_key() { + use super::Entry::{Occupied, Vacant}; + let mut a = HashMap::new(); let key = "hello there"; let value = "value goes here"; @@ -4274,6 +4213,8 @@ mod test_map { #[test] fn test_vacant_entry_key() { + use super::Entry::{Occupied, Vacant}; + let mut a = HashMap::new(); let key = "hello there"; let value = "value goes here"; @@ -4349,4 +4290,65 @@ mod test_map { } } + #[test] + fn test_raw_entry() { + use super::RawEntry::{Occupied, Vacant}; + + let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; + + let mut map: HashMap<_, _> = xs.iter().cloned().collect(); + + // Existing key (insert) + match map.raw_entry().search_by(&1) { + Vacant(_) => unreachable!(), + Occupied(mut view) => { + assert_eq!(view.get(), &10); + assert_eq!(view.insert(100), 10); + } + } + assert_eq!(map.raw_entry_immut().hash_with(|mut h| { + 1.hash(&mut h); + h.finish() + }).search_with(|k| *k == 1) + .unwrap(), (&10, &100)); + assert_eq!(map.len(), 6); + + + // Existing key (update) + match map.raw_entry().hash_by(&2).search_by(&2) { + Vacant(_) => unreachable!(), + Occupied(mut view) => { + let v = view.get_mut(); + let new_v = (*v) * 10; + *v = new_v; + } + } + assert_eq!(map.raw_entry_immut().search_by(&2).unwrap(), (&2, &200)); + assert_eq!(map.len(), 6); + + // Existing key (take) + match map.raw_entry().hash_with(|mut h| { + 3.hash(&mut h); + h.finish() + }).search_with(|k| *k == 3) { + Vacant(_) => unreachable!(), + Occupied(view) => { + assert_eq!(view.remove_kv(), (3, 30)); + } + } + assert_eq!(map.raw_entry_immut().search_by(&3), None); + assert_eq!(map.len(), 5); + + + // Inexistent key (insert) + match map.raw_entry().search_by(&10) { + Occupied(_) => unreachable!(), + Vacant(view) => { + assert_eq!(view.insert(10, 1000), (&mut 10, &mut 1000)); + } + } + assert_eq!(map.raw_entry_immut().hash_by(&10).search_by(&10).unwrap(), (&10, &1000)); + assert_eq!(map.len(), 6); + } + } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 591d51184cf..01f5ef44540 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -284,7 +284,8 @@ #![feature(prelude_import)] #![feature(ptr_internals)] #![feature(raw)] -#![feature(raw_entry)] +#![feature(hash_raw_entry)] +#![feature(hash_raw_entry_immut)] #![feature(rustc_attrs)] #![feature(rustc_const_unstable)] #![feature(std_internals)] From 14ee4e468d3aba03ff42e90e47d6def6b6d40edd Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Fri, 25 May 2018 16:03:50 -0400 Subject: [PATCH 03/10] fixup Debug bounds --- src/libstd/collections/hash/map.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 328962af33e..04ffcfc0c27 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -2287,7 +2287,7 @@ impl<'a, K, V> Debug for RawEntryBuilderHashed<'a, K, V> { } #[unstable(feature = "hash_raw_entry", issue = "42069")] -impl<'a, K, V> Debug for RawEntry<'a, K, V> { +impl<'a, K: Debug, V: Debug> Debug for RawEntry<'a, K, V> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { RawEntry::Vacant(ref v) => { @@ -2305,7 +2305,7 @@ impl<'a, K, V> Debug for RawEntry<'a, K, V> { } #[unstable(feature = "hash_raw_entry", issue = "42069")] -impl<'a, K, V> Debug for RawOccupiedEntry<'a, K, V> { +impl<'a, K: Debug, V: Debug> Debug for RawOccupiedEntry<'a, K, V> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("RawOccupiedEntry") .field("key", self.key()) From 6089af72c0060ee0f88db77a142762abf194a326 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Fri, 25 May 2018 19:10:25 -0400 Subject: [PATCH 04/10] disambiguate hashes --- src/libstd/collections/hash/map.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 04ffcfc0c27..caa149bb9a5 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -4294,7 +4294,7 @@ mod test_map { fn test_raw_entry() { use super::RawEntry::{Occupied, Vacant}; - let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; + let xs = [(1i32, 10i32), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; let mut map: HashMap<_, _> = xs.iter().cloned().collect(); @@ -4307,7 +4307,7 @@ mod test_map { } } assert_eq!(map.raw_entry_immut().hash_with(|mut h| { - 1.hash(&mut h); + 1i32.hash(&mut h); h.finish() }).search_with(|k| *k == 1) .unwrap(), (&10, &100)); @@ -4328,7 +4328,7 @@ mod test_map { // Existing key (take) match map.raw_entry().hash_with(|mut h| { - 3.hash(&mut h); + 3i32.hash(&mut h); h.finish() }).search_with(|k| *k == 3) { Vacant(_) => unreachable!(), From ad6c7a9a3152245021778140fa57f8d1a8ad8fb6 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Fri, 7 Sep 2018 16:47:19 -0400 Subject: [PATCH 05/10] Cleanup API somewhat --- src/libstd/collections/hash/map.rs | 426 ++++++++++++++--------------- src/libstd/lib.rs | 1 - 2 files changed, 202 insertions(+), 225 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index caa149bb9a5..1a432e5bd3d 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -443,7 +443,7 @@ fn search_hashed(table: M, hash: SafeHash, is_match: F) -> InternalE #[inline] fn search_hashed_mut(table: M, hash: SafeHash, is_match: F) -> InternalEntry where M: DerefMut>, - F: FnMut(&mut K) -> bool + F: FnMut(&K) -> bool { // This is the only function where capacity can be zero. To avoid // undefined behavior when Bucket::new gets the raw bucket in this @@ -510,7 +510,7 @@ fn search_hashed_nonempty(table: M, hash: SafeHash, mut is_match: F) fn search_hashed_nonempty_mut(table: M, hash: SafeHash, mut is_match: F) -> InternalEntry where M: DerefMut>, - F: FnMut(&mut K) -> bool + F: FnMut(&K) -> bool { // Do not check the capacity as an extra branch could slow the lookup. @@ -1591,9 +1591,9 @@ impl HashMap /// /// #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn raw_entry(&mut self) -> RawEntryBuilder { + pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut { self.reserve(1); - RawEntryBuilder { map: self } + RawEntryBuilderMut { map: self } } /// Creates a raw immutable entry builder for the HashMap. @@ -1611,9 +1611,9 @@ impl HashMap /// `get` should be preferred. /// /// Immutable raw entries have very limited use; you might instead want `raw_entry`. - #[unstable(feature = "hash_raw_entry_immut", issue = "42069")] - pub fn raw_entry_immut(&self) -> RawImmutableEntryBuilder { - RawImmutableEntryBuilder { map: self } + #[unstable(feature = "hash_raw_entry", issue = "42069")] + pub fn raw_entry(&self) -> RawEntryBuilder { + RawEntryBuilder { map: self } } } @@ -1842,13 +1842,14 @@ impl<'a, K, V> InternalEntry> { InternalEntry::Occupied { elem } => { Some(Occupied(OccupiedEntry { key: Some(key), - entry: RawOccupiedEntry { elem }, + elem, })) } InternalEntry::Vacant { hash, elem } => { Some(Vacant(VacantEntry { + hash, key, - entry: RawVacantEntry { hash, elem } + elem, })) } InternalEntry::TableIsEmpty => None, @@ -1858,22 +1859,12 @@ impl<'a, K, V> InternalEntry> { /// A builder for computing where in a HashMap a key-value pair would be stored. /// -/// See the [`HashMap::raw_entry`][] docs for usage examples. +/// See the [`HashMap::raw_entry_mut`][] docs for usage examples. #[unstable(feature = "hash_raw_entry", issue = "42069")] -pub struct RawEntryBuilder<'a, K: 'a, V: 'a, S: 'a> { +pub struct RawEntryBuilderMut<'a, K: 'a, V: 'a, S: 'a> { map: &'a mut HashMap, } -/// A builder for computing where in a HashMap a key-value pair would be stored, -/// where the hash has already been specified. -/// -/// See the [`HashMap::raw_entry`][] docs for usage examples. -#[unstable(feature = "hash_raw_entry", issue = "42069")] -pub struct RawEntryBuilderHashed<'a, K: 'a, V: 'a> { - map: &'a mut RawTable, - hash: SafeHash, -} - /// A view into a single entry in a map, which may either be vacant or occupied. /// /// This is a lower-level version of [`Entry`]. @@ -1884,191 +1875,170 @@ pub struct RawEntryBuilderHashed<'a, K: 'a, V: 'a> { /// [`Entry`]: struct.Entry.html /// [`raw_entry`]: struct.HashMap.html#method.raw_entry #[unstable(feature = "hash_raw_entry", issue = "42069")] -pub enum RawEntry<'a, K: 'a, V: 'a> { +pub enum RawEntryMut<'a, K: 'a, V: 'a, S: 'a> { /// An occupied entry. - Occupied(RawOccupiedEntry<'a, K, V>), + Occupied(RawOccupiedEntryMut<'a, K, V>), /// A vacant entry. - Vacant(RawVacantEntry<'a, K, V>), + Vacant(RawVacantEntryMut<'a, K, V, S>), } /// A view into an occupied entry in a `HashMap`. -/// It is part of the [`RawEntry`] enum. +/// It is part of the [`RawEntryMut`] enum. /// -/// [`RawEntry`]: enum.RawEntry.html +/// [`RawEntryMut`]: enum.RawEntryMut.html #[unstable(feature = "hash_raw_entry", issue = "42069")] -pub struct RawOccupiedEntry<'a, K: 'a, V: 'a> { +pub struct RawOccupiedEntryMut<'a, K: 'a, V: 'a> { elem: FullBucket>, } /// A view into a vacant entry in a `HashMap`. -/// It is part of the [`RawEntry`] enum. +/// It is part of the [`RawEntryMut`] enum. /// -/// [`RawEntry`]: enum.RawEntry.html +/// [`RawEntryMut`]: enum.RawEntryMut.html #[unstable(feature = "hash_raw_entry", issue = "42069")] -pub struct RawVacantEntry<'a, K: 'a, V: 'a> { - hash: SafeHash, +pub struct RawVacantEntryMut<'a, K: 'a, V: 'a, S: 'a> { elem: VacantEntryState>, + hash_builder: &'a S, } /// A builder for computing where in a HashMap a key-value pair would be stored. /// -/// See the [`HashMap::raw_entry_immut`][] docs for usage examples. -#[unstable(feature = "hash_raw_entry_immut", issue = "42069")] -pub struct RawImmutableEntryBuilder<'a, K: 'a, V: 'a, S: 'a> { +/// See the [`HashMap::raw_entry`][] docs for usage examples. +#[unstable(feature = "hash_raw_entry", issue = "42069")] +pub struct RawEntryBuilder<'a, K: 'a, V: 'a, S: 'a> { map: &'a HashMap, } -/// A builder for computing where in a HashMap a key-value pair would be stored, -/// where the hash has already been specified. -/// -/// See the [`HashMap::raw_entry_immut`][] docs for usage examples. -#[unstable(feature = "hash_raw_entry_immut", issue = "42069")] -pub struct RawImmutableEntryBuilderHashed<'a, K: 'a, V: 'a> { - map: &'a RawTable, - hash: SafeHash, -} - -impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> +impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> where S: BuildHasher, + K: Eq + Hash, { - /// Initializes the raw entry builder with the hash of the given query value. + /// Create a `RawEntryMut` from the given key. #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn hash_by(self, k: &Q) -> RawEntryBuilderHashed<'a, K, V> - where Q: Hash - { - self.hash_with(|mut hasher| { - k.hash(&mut hasher); - hasher.finish() - }) - } - - /// Initializes the raw entry builder with the hash yielded by the given function. - #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn hash_with(self, func: F) -> RawEntryBuilderHashed<'a, K, V> - where F: FnOnce(S::Hasher) -> u64 - { - let hasher = self.map.hash_builder.build_hasher(); - let hash = SafeHash::new(func(hasher)); - - RawEntryBuilderHashed { map: &mut self.map.table, hash } - } - - /// Searches for the location of the raw entry with the given query value. - #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn search_by(self, k: &Q) -> RawEntry<'a, K, V> + pub fn from_key(self, k: &Q) -> RawEntryMut<'a, K, V, S> where K: Borrow, - Q: Eq + Hash + Q: Hash + Eq { - self.hash_by(k).search_by(k) + let mut hasher = self.map.hash_builder.build_hasher(); + k.hash(&mut hasher); + self.from_key_hashed_nocheck(hasher.finish(), k) } -} -impl<'a, K, V> RawEntryBuilderHashed<'a, K, V> -{ - /// Searches for the location of the raw entry with the given query value. - /// - /// Note that it isn't required that the query value be hashable, as the - /// builder's hash is used. + /// Create a `RawEntryMut` from the given key and its hash. #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn search_by(self, k: &Q) -> RawEntry<'a, K, V> + pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> RawEntryMut<'a, K, V, S> where K: Borrow, - Q: Eq, + Q: Eq { - // I don't know why we need this `&mut -> &` transform to resolve Borrow, but we do - self.search_with(|key| (&*key).borrow() == k) + self.from_hash(hash, |q| q.borrow().eq(k)) } - /// Searches for the location of the raw entry with the given comparison function. - /// - /// Note that mutable access is given to each key that is visited, because - /// this land is truly godless, and *someone* might have a use for this. + /// Create a `RawEntryMut` from the given hash. #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn search_with(self, func: F) -> RawEntry<'a, K, V> - where F: FnMut(&mut K) -> bool, + pub fn from_hash(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S> + where for<'b> F: FnMut(&'b K) -> bool, { - match search_hashed_mut(self.map, self.hash, func) { + match search_hashed_mut(&mut self.map.table, SafeHash::new(hash), is_match) { InternalEntry::Occupied { elem } => { - RawEntry::Occupied(RawOccupiedEntry { elem }) + RawEntryMut::Occupied(RawOccupiedEntryMut { elem }) } - InternalEntry::Vacant { hash, elem } => { - RawEntry::Vacant(RawVacantEntry { hash, elem }) + InternalEntry::Vacant { elem, .. } => { + RawEntryMut::Vacant(RawVacantEntryMut { + elem, + hash_builder: &self.map.hash_builder, + }) } InternalEntry::TableIsEmpty => { unreachable!() } } } -} -impl<'a, K, V, S> RawImmutableEntryBuilder<'a, K, V, S> - where S: BuildHasher, -{ - /// Initializes the raw entry builder with the hash of the given query value. - #[unstable(feature = "hash_raw_entry_immut", issue = "42069")] - pub fn hash_by(self, k: &Q) -> RawImmutableEntryBuilderHashed<'a, K, V> - where Q: Hash + /// Create a `RawEntryMut` by examining the elements of a hash bucket until `is_match` returns true for one of them. + #[unstable(feature = "hash_raw_entry", issue = "42069")] + pub fn from_bucket(self, hash_bucket: u64, mut is_match: F) -> RawEntryMut<'a, K, V, S> + where for<'b> F: FnMut(&'b K) -> bool, { - self.hash_with(|mut hasher| { - k.hash(&mut hasher); - hasher.finish() - }) - } + let hash = SafeHash::new(hash_bucket); - /// Initializes the raw entry builder with the hash yielded by the given function. - #[unstable(feature = "hash_raw_entry_immut", issue = "42069")] - pub fn hash_with(self, func: F) -> RawImmutableEntryBuilderHashed<'a, K, V> - where F: FnOnce(S::Hasher) -> u64 - { - let hasher = self.map.hash_builder.build_hasher(); - let hash = SafeHash::new(func(hasher)); + let size = self.map.table.size(); + let mut probe = Bucket::new(&mut self.map.table, hash); + let mut displacement = 0; - RawImmutableEntryBuilderHashed { map: &self.map.table, hash } - } + loop { + let full = match probe.peek() { + Empty(bucket) => { + // Found a hole! + return RawEntryMut::Vacant(RawVacantEntryMut { + elem: NoElem(bucket, displacement), + hash_builder: &self.map.hash_builder, + }); + } + Full(bucket) => bucket, + }; - /// Searches for the location of the raw entry with the given query value. - #[unstable(feature = "hash_raw_entry_immut", issue = "42069")] - pub fn search_by(self, k: &Q) -> Option<(&'a K, &'a V)> - where K: Borrow, - Q: Eq + Hash - { - self.hash_by(k).search_by(k) - } -} + let probe_displacement = full.displacement(); -impl<'a, K, V> RawImmutableEntryBuilderHashed<'a, K, V> -{ - /// Searches for the location of the raw entry with the given query value. - /// - /// Note that it isn't required that the query value be hashable, as the - /// builder's hash is used. - #[unstable(feature = "hash_raw_entry_immut", issue = "42069")] - pub fn search_by(self, k: &Q) -> Option<(&'a K, &'a V)> - where K: Borrow, - Q: Eq, - { - self.search_with(|key| key.borrow() == k) - } - - /// Searches for the location of the raw entry with the given comparison function. - /// - /// Note that mutable access is given to each key that is visited, because - /// this land is truly godless, and *someone* might have a use for this. - #[unstable(feature = "hash_raw_entry_immut", issue = "42069")] - pub fn search_with(self, func: F) -> Option<(&'a K, &'a V)> - where F: FnMut(&K) -> bool, - { - match search_hashed(self.map, self.hash, func) { - InternalEntry::Occupied { elem } => { - Some(elem.into_refs()) + if probe_displacement < displacement { + // Found a luckier bucket than me. + // We can finish the search early if we hit any bucket + // with a lower distance to initial bucket than we've probed. + return RawEntryMut::Vacant(RawVacantEntryMut { + elem: NeqElem(full, probe_displacement), + hash_builder: &self.map.hash_builder, + }) } - InternalEntry::Vacant { .. } | InternalEntry::TableIsEmpty => { - None + + // Call is_match even if hash doesn't match hash_bucket. + if is_match(full.read().0) { + return RawEntryMut::Occupied(RawOccupiedEntryMut { elem: full }); } + + displacement += 1; + probe = full.next(); + debug_assert!(displacement <= size); } } } -impl<'a, K, V> RawEntry<'a, K, V> { +impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> + where S: BuildHasher, +{ + /// Access an entry by key. + #[unstable(feature = "hash_raw_entry", issue = "42069")] + pub fn from_key(self, k: &Q) -> Option<(&'a K, &'a V)> + where 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. + #[unstable(feature = "hash_raw_entry", issue = "42069")] + 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)) + } + + /// Access an entry by hash. + #[unstable(feature = "hash_raw_entry", issue = "42069")] + pub fn from_hash(self, hash: u64, is_match: F) -> Option<(&'a K, &'a V)> + where F: FnMut(&K) -> bool + { + match search_hashed(&self.map.table, SafeHash::new(hash), is_match) { + InternalEntry::Occupied { elem } => Some(elem.into_refs()), + InternalEntry::Vacant { .. } => None, + InternalEntry::TableIsEmpty => unreachable!(), + } + } +} + +impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { /// 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. /// @@ -2086,10 +2056,13 @@ impl<'a, K, V> RawEntry<'a, K, V> { /// assert_eq!(map["poneyland"], 22); /// ``` #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn or_insert(self, default_key: K, default_val: V) -> (&'a mut K, &'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 { - RawEntry::Occupied(entry) => entry.into_kv(), - RawEntry::Vacant(entry) => entry.insert(default_key, default_val), + RawEntryMut::Occupied(entry) => entry.into_key_value(), + RawEntryMut::Vacant(entry) => entry.insert(default_key, default_val), } } @@ -2112,10 +2085,12 @@ impl<'a, K, V> RawEntry<'a, K, V> { #[unstable(feature = "hash_raw_entry", issue = "42069")] 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 { - RawEntry::Occupied(entry) => entry.into_kv(), - RawEntry::Vacant(entry) => { + RawEntryMut::Occupied(entry) => entry.into_key_value(), + RawEntryMut::Vacant(entry) => { let (k, v) = default(); entry.insert(k, v) } @@ -2149,19 +2124,19 @@ impl<'a, K, V> RawEntry<'a, K, V> { where F: FnOnce(&mut K, &mut V) { match self { - RawEntry::Occupied(mut entry) => { + RawEntryMut::Occupied(mut entry) => { { - let (k, v) = entry.kv_mut(); + let (k, v) = entry.get_key_value_mut(); f(k, v); } - RawEntry::Occupied(entry) + RawEntryMut::Occupied(entry) }, - RawEntry::Vacant(entry) => RawEntry::Vacant(entry), + RawEntryMut::Vacant(entry) => RawEntryMut::Vacant(entry), } } } -impl<'a, K, V> RawOccupiedEntry<'a, K, V> { +impl<'a, K, V> RawOccupiedEntryMut<'a, K, V> { /// Gets a reference to the key in the entry. #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn key(&self) -> &K { @@ -2202,20 +2177,20 @@ impl<'a, K, V> RawOccupiedEntry<'a, K, V> { /// Gets a reference to the key and value in the entry. #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn kv(&mut self) -> (&K, &V) { + pub fn get_key_value(&mut self) -> (&K, &V) { self.elem.read() } /// Gets a mutable reference to the key and value in the entry. #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn kv_mut(&mut self) -> (&mut K, &mut V) { + pub fn get_key_value_mut(&mut self) -> (&mut K, &mut V) { self.elem.read_mut() } /// Converts the OccupiedEntry into a mutable reference to the key and value in the entry /// with a lifetime bound to the map itself. #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn into_kv(self) -> (&'a mut K, &'a mut V) { + pub fn into_key_value(self) -> (&'a mut K, &'a mut V) { self.elem.into_mut_refs() } @@ -2246,23 +2221,36 @@ impl<'a, K, V> RawOccupiedEntry<'a, K, V> { } } -impl<'a, K, V> RawVacantEntry<'a, K, V> { +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. #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn insert(self, key: K, value: V) -> (&'a mut K, &'a mut V) { + 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. + #[unstable(feature = "hash_raw_entry", issue = "42069")] + pub fn insert_hashed_nocheck(self, hash: u64, key: K, value: V) -> (&'a mut K, &'a mut V) { + let hash = SafeHash::new(hash); let b = match self.elem { NeqElem(mut bucket, disp) => { if disp >= DISPLACEMENT_THRESHOLD { bucket.table_mut().set_tag(true); } - robin_hood(bucket, disp, self.hash, key, value) + robin_hood(bucket, disp, hash, key, value) }, NoElem(mut bucket, disp) => { if disp >= DISPLACEMENT_THRESHOLD { bucket.table_mut().set_tag(true); } - bucket.put(self.hash, key, value) + bucket.put(hash, key, value) }, }; b.into_mut_refs() @@ -2270,7 +2258,7 @@ impl<'a, K, V> RawVacantEntry<'a, K, V> { } #[unstable(feature = "hash_raw_entry", issue = "42069")] -impl<'a, K, V, S> Debug for RawEntryBuilder<'a, K, V, S> { +impl<'a, K, V, S> Debug for RawEntryBuilderMut<'a, K, V, S> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("RawEntryBuilder") .finish() @@ -2278,24 +2266,15 @@ impl<'a, K, V, S> Debug for RawEntryBuilder<'a, K, V, S> { } #[unstable(feature = "hash_raw_entry", issue = "42069")] -impl<'a, K, V> Debug for RawEntryBuilderHashed<'a, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("RawEntryBuilderHashed") - .field("hash", &self.hash.inspect()) - .finish() - } -} - -#[unstable(feature = "hash_raw_entry", issue = "42069")] -impl<'a, K: Debug, V: Debug> Debug for RawEntry<'a, K, V> { +impl<'a, K: Debug, V: Debug, S> Debug for RawEntryMut<'a, K, V, S> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - RawEntry::Vacant(ref v) => { + RawEntryMut::Vacant(ref v) => { f.debug_tuple("RawEntry") .field(v) .finish() } - RawEntry::Occupied(ref o) => { + RawEntryMut::Occupied(ref o) => { f.debug_tuple("RawEntry") .field(o) .finish() @@ -2305,9 +2284,9 @@ impl<'a, K: Debug, V: Debug> Debug for RawEntry<'a, K, V> { } #[unstable(feature = "hash_raw_entry", issue = "42069")] -impl<'a, K: Debug, V: Debug> Debug for RawOccupiedEntry<'a, K, V> { +impl<'a, K: Debug, V: Debug> Debug for RawOccupiedEntryMut<'a, K, V> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("RawOccupiedEntry") + f.debug_struct("RawOccupiedEntryMut") .field("key", self.key()) .field("value", self.get()) .finish() @@ -2315,27 +2294,17 @@ impl<'a, K: Debug, V: Debug> Debug for RawOccupiedEntry<'a, K, V> { } #[unstable(feature = "hash_raw_entry", issue = "42069")] -impl<'a, K, V> Debug for RawVacantEntry<'a, K, V> { +impl<'a, K, V, S> Debug for RawVacantEntryMut<'a, K, V, S> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("RawVacantEntry") - .field("hash", &self.hash.inspect()) + f.debug_struct("RawVacantEntryMut") .finish() } } -#[unstable(feature = "hash_raw_entry_immut", issue = "42069")] -impl<'a, K, V, S> Debug for RawImmutableEntryBuilder<'a, K, V, S> { +#[unstable(feature = "hash_raw_entry", issue = "42069")] +impl<'a, K, V, S> Debug for RawEntryBuilder<'a, K, V, S> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("RawImmutableEntryBuilder") - .finish() - } -} - -#[unstable(feature = "hash_raw_entry_immut", issue = "42069")] -impl<'a, K, V> Debug for RawImmutableEntryBuilderHashed<'a, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("RawImmutableEntryBuilderHashed") - .field("hash", &self.hash.inspect()) + f.debug_struct("RawEntryBuilder") .finish() } } @@ -2384,7 +2353,7 @@ impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for Entry<'a, K, V> { #[stable(feature = "rust1", since = "1.0.0")] pub struct OccupiedEntry<'a, K: 'a, V: 'a> { key: Option, - entry: RawOccupiedEntry<'a, K, V>, + elem: FullBucket>, } #[stable(feature= "debug_hash_map", since = "1.12.0")] @@ -2403,8 +2372,9 @@ impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for OccupiedEntry<'a, K, V> { /// [`Entry`]: enum.Entry.html #[stable(feature = "rust1", since = "1.0.0")] pub struct VacantEntry<'a, K: 'a, V: 'a> { + hash: SafeHash, key: K, - entry: RawVacantEntry<'a, K, V>, + elem: VacantEntryState>, } #[stable(feature= "debug_hash_map", since = "1.12.0")] @@ -2828,7 +2798,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "map_entry_keys", since = "1.10.0")] pub fn key(&self) -> &K { - self.entry.key() + self.elem.read().0 } /// Take the ownership of the key and value from the map. @@ -2851,7 +2821,8 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] pub fn remove_entry(self) -> (K, V) { - self.entry.remove_entry() + let (k, v, _) = pop_internal(self.elem); + (k, v) } /// Gets a reference to the value in the entry. @@ -2871,7 +2842,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn get(&self) -> &V { - self.entry.get() + self.elem.read().1 } /// Gets a mutable reference to the value in the entry. @@ -2903,7 +2874,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn get_mut(&mut self) -> &mut V { - self.entry.get_mut() + self.elem.read_mut().1 } /// Converts the OccupiedEntry into a mutable reference to the value in the entry @@ -2931,7 +2902,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn into_mut(self) -> &'a mut V { - self.entry.into_mut() + self.elem.into_mut_refs().1 } /// Sets the value of the entry, and returns the entry's old value. @@ -2952,8 +2923,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// assert_eq!(map["poneyland"], 15); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, value: V) -> V { - self.entry.insert(value) + pub fn insert(&mut self, mut value: V) -> V { + let old_value = self.get_mut(); + mem::swap(&mut value, old_value); + value } /// Takes the value out of the entry, and returns it. @@ -2975,7 +2948,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn remove(self) -> V { - self.entry.remove() + pop_internal(self.elem).1 } /// Returns a key that was used for search. @@ -3008,7 +2981,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[unstable(feature = "map_entry_replace", issue = "44286")] pub fn replace_entry(mut self, value: V) -> (K, V) { - let (old_key, old_value) = self.entry.kv_mut(); + let (old_key, old_value) = self.elem.read_mut(); let old_key = mem::replace(old_key, self.key.unwrap()); let old_value = mem::replace(old_value, value); @@ -3043,7 +3016,8 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[unstable(feature = "map_entry_replace", issue = "44286")] pub fn replace_key(mut self) -> K { - mem::replace(self.entry.key_mut(), self.key.unwrap()) + let (old_key, _) = self.elem.read_mut(); + mem::replace(old_key, self.key.unwrap()) } } @@ -3101,7 +3075,21 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(self, value: V) -> &'a mut V { - self.entry.insert(self.key, value).1 + let b = match self.elem { + NeqElem(mut bucket, disp) => { + if disp >= DISPLACEMENT_THRESHOLD { + bucket.table_mut().set_tag(true); + } + robin_hood(bucket, disp, self.hash, self.key, value) + }, + NoElem(mut bucket, disp) => { + if disp >= DISPLACEMENT_THRESHOLD { + bucket.table_mut().set_tag(true); + } + bucket.put(self.hash, self.key, value) + }, + }; + b.into_mut_refs().1 } } @@ -3312,7 +3300,7 @@ impl super::Recover for HashMap match self.entry(key) { Occupied(mut occupied) => { let key = occupied.take_key().unwrap(); - Some(mem::replace(occupied.entry.key_mut(), key)) + Some(mem::replace(occupied.elem.read_mut().0, key)) } Vacant(vacant) => { vacant.insert(()); @@ -3363,6 +3351,7 @@ fn assert_covariance() { #[cfg(test)] mod test_map { use super::HashMap; + use super::Entry::{Occupied, Vacant}; use super::RandomState; use cell::RefCell; use rand::{thread_rng, Rng}; @@ -3601,8 +3590,6 @@ mod test_map { #[test] fn test_empty_entry() { - use super::Entry::{Occupied, Vacant}; - let mut m: HashMap = HashMap::new(); match m.entry(0) { Occupied(_) => panic!(), @@ -4061,8 +4048,6 @@ mod test_map { #[test] fn test_entry() { - use super::Entry::{Occupied, Vacant}; - let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; let mut map: HashMap<_, _> = xs.iter().cloned().collect(); @@ -4116,9 +4101,6 @@ mod test_map { #[test] fn test_entry_take_doesnt_corrupt() { #![allow(deprecated)] //rand - - use super::Entry::{Occupied, Vacant}; - // Test for #19292 fn check(m: &HashMap) { for k in m.keys() { @@ -4193,8 +4175,6 @@ mod test_map { #[test] fn test_occupied_entry_key() { - use super::Entry::{Occupied, Vacant}; - let mut a = HashMap::new(); let key = "hello there"; let value = "value goes here"; @@ -4213,8 +4193,6 @@ mod test_map { #[test] fn test_vacant_entry_key() { - use super::Entry::{Occupied, Vacant}; - let mut a = HashMap::new(); let key = "hello there"; let value = "value goes here"; @@ -4333,7 +4311,7 @@ mod test_map { }).search_with(|k| *k == 3) { Vacant(_) => unreachable!(), Occupied(view) => { - assert_eq!(view.remove_kv(), (3, 30)); + assert_eq!(view.remove_key_value(), (3, 30)); } } assert_eq!(map.raw_entry_immut().search_by(&3), None); diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 01f5ef44540..a3721942b96 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -285,7 +285,6 @@ #![feature(ptr_internals)] #![feature(raw)] #![feature(hash_raw_entry)] -#![feature(hash_raw_entry_immut)] #![feature(rustc_attrs)] #![feature(rustc_const_unstable)] #![feature(std_internals)] From 2c0f385d0c16c6797788ef5a5f42ac02ced50da1 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Wed, 12 Sep 2018 14:59:12 -0400 Subject: [PATCH 06/10] Fix formatting --- src/libstd/collections/hash/map.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 1a432e5bd3d..bb635a26c31 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -1954,7 +1954,8 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> } } - /// Create a `RawEntryMut` by examining the elements of a hash bucket until `is_match` returns true for one of them. + /// Create a `RawEntryMut` by examining the elements of a hash bucket until `is_match` returns + /// true for one of them. #[unstable(feature = "hash_raw_entry", issue = "42069")] pub fn from_bucket(self, hash_bucket: u64, mut is_match: F) -> RawEntryMut<'a, K, V, S> where for<'b> F: FnMut(&'b K) -> bool, From dc41726de3a0088fd1f31b018bd31d56be7cfd9c Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Thu, 13 Sep 2018 16:19:40 -0400 Subject: [PATCH 07/10] Fix tests and update issue number --- src/libstd/collections/hash/map.rs | 294 +++++++++++++++-------------- 1 file changed, 156 insertions(+), 138 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index bb635a26c31..62e5cdcf90e 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -435,29 +435,13 @@ fn search_hashed(table: M, hash: SafeHash, is_match: F) -> InternalE return InternalEntry::TableIsEmpty; } - search_hashed_nonempty(table, hash, is_match) -} - -/// Search for a pre-hashed key. -/// If you don't already know the hash, use search or search_mut instead -#[inline] -fn search_hashed_mut(table: M, hash: SafeHash, is_match: F) -> InternalEntry - where M: DerefMut>, - F: FnMut(&K) -> bool -{ - // This is the only function where capacity can be zero. To avoid - // undefined behavior when Bucket::new gets the raw bucket in this - // case, immediately return the appropriate search result. - if table.capacity() == 0 { - return InternalEntry::TableIsEmpty; - } - - search_hashed_nonempty_mut(table, hash, is_match) + search_hashed_nonempty(table, hash, is_match, true) } /// Search for a pre-hashed key when the hash map is known to be non-empty. #[inline] -fn search_hashed_nonempty(table: M, hash: SafeHash, mut is_match: F) +fn search_hashed_nonempty(table: M, hash: SafeHash, mut is_match: F, + compare_hashes: bool) -> InternalEntry where M: Deref>, F: FnMut(&K) -> bool @@ -493,7 +477,7 @@ fn search_hashed_nonempty(table: M, hash: SafeHash, mut is_match: F) } // If the hash doesn't match, it can't be this one.. - if hash == full.hash() { + if hash == full.hash() || !compare_hashes { // If the key doesn't match, it can't be this one.. if is_match(full.read().0) { return InternalEntry::Occupied { elem: full }; @@ -507,7 +491,8 @@ fn search_hashed_nonempty(table: M, hash: SafeHash, mut is_match: F) /// Search for a pre-hashed key when the hash map is known to be non-empty. #[inline] -fn search_hashed_nonempty_mut(table: M, hash: SafeHash, mut is_match: F) +fn search_hashed_nonempty_mut(table: M, hash: SafeHash, mut is_match: F, + compare_hashes: bool) -> InternalEntry where M: DerefMut>, F: FnMut(&K) -> bool @@ -543,7 +528,7 @@ fn search_hashed_nonempty_mut(table: M, hash: SafeHash, mut is_match } // If the hash doesn't match, it can't be this one.. - if hash == full.hash() { + if hash == full.hash() || !compare_hashes { // If the key doesn't match, it can't be this one.. if is_match(full.read_mut().0) { return InternalEntry::Occupied { elem: full }; @@ -660,7 +645,7 @@ impl HashMap } let hash = self.make_hash(q); - search_hashed_nonempty(&self.table, hash, |k| q.eq(k.borrow())) + search_hashed_nonempty(&self.table, hash, |k| q.eq(k.borrow()), true) .into_occupied_bucket() } @@ -675,7 +660,7 @@ impl HashMap } let hash = self.make_hash(q); - search_hashed_nonempty(&mut self.table, hash, |k| q.eq(k.borrow())) + search_hashed_nonempty(&mut self.table, hash, |k| q.eq(k.borrow()), true) .into_occupied_bucket() } @@ -1590,7 +1575,7 @@ impl HashMap /// # Examples /// /// - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut { self.reserve(1); RawEntryBuilderMut { map: self } @@ -1611,7 +1596,7 @@ impl HashMap /// `get` should be preferred. /// /// Immutable raw entries have very limited use; you might instead want `raw_entry`. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn raw_entry(&self) -> RawEntryBuilder { RawEntryBuilder { map: self } } @@ -1860,7 +1845,7 @@ impl<'a, K, V> InternalEntry> { /// 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. -#[unstable(feature = "hash_raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "54043")] pub struct RawEntryBuilderMut<'a, K: 'a, V: 'a, S: 'a> { map: &'a mut HashMap, } @@ -1874,7 +1859,7 @@ pub struct RawEntryBuilderMut<'a, K: 'a, V: 'a, S: 'a> { /// [`HashMap`]: struct.HashMap.html /// [`Entry`]: struct.Entry.html /// [`raw_entry`]: struct.HashMap.html#method.raw_entry -#[unstable(feature = "hash_raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "54043")] pub enum RawEntryMut<'a, K: 'a, V: 'a, S: 'a> { /// An occupied entry. Occupied(RawOccupiedEntryMut<'a, K, V>), @@ -1886,7 +1871,7 @@ pub enum RawEntryMut<'a, K: 'a, V: 'a, S: 'a> { /// It is part of the [`RawEntryMut`] enum. /// /// [`RawEntryMut`]: enum.RawEntryMut.html -#[unstable(feature = "hash_raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "54043")] pub struct RawOccupiedEntryMut<'a, K: 'a, V: 'a> { elem: FullBucket>, } @@ -1895,7 +1880,7 @@ pub struct RawOccupiedEntryMut<'a, K: 'a, V: 'a> { /// It is part of the [`RawEntryMut`] enum. /// /// [`RawEntryMut`]: enum.RawEntryMut.html -#[unstable(feature = "hash_raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "54043")] pub struct RawVacantEntryMut<'a, K: 'a, V: 'a, S: 'a> { elem: VacantEntryState>, hash_builder: &'a S, @@ -1904,7 +1889,7 @@ pub struct RawVacantEntryMut<'a, K: 'a, V: 'a, S: 'a> { /// A builder for computing where in a HashMap a key-value pair would be stored. /// /// See the [`HashMap::raw_entry`][] docs for usage examples. -#[unstable(feature = "hash_raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "54043")] pub struct RawEntryBuilder<'a, K: 'a, V: 'a, S: 'a> { map: &'a HashMap, } @@ -1914,7 +1899,7 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> K: Eq + Hash, { /// Create a `RawEntryMut` from the given key. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn from_key(self, k: &Q) -> RawEntryMut<'a, K, V, S> where K: Borrow, Q: Hash + Eq @@ -1925,7 +1910,7 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> } /// Create a `RawEntryMut` from the given key and its hash. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> RawEntryMut<'a, K, V, S> where K: Borrow, Q: Eq @@ -1933,12 +1918,13 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> self.from_hash(hash, |q| q.borrow().eq(k)) } - /// Create a `RawEntryMut` from the given hash. - #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn from_hash(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S> + fn search(self, hash: u64, is_match: F, compare_hashes: bool) -> RawEntryMut<'a, K, V, S> where for<'b> F: FnMut(&'b K) -> bool, { - match search_hashed_mut(&mut self.map.table, SafeHash::new(hash), is_match) { + match search_hashed_nonempty_mut(&mut self.map.table, + SafeHash::new(hash), + is_match, + compare_hashes) { InternalEntry::Occupied { elem } => { RawEntryMut::Occupied(RawOccupiedEntryMut { elem }) } @@ -1953,52 +1939,22 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> } } } - - /// Create a `RawEntryMut` by examining the elements of a hash bucket until `is_match` returns - /// true for one of them. - #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn from_bucket(self, hash_bucket: u64, mut is_match: F) -> RawEntryMut<'a, K, V, S> + /// Create a `RawEntryMut` from the given hash. + #[unstable(feature = "hash_raw_entry", issue = "54043")] + pub fn from_hash(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S> where for<'b> F: FnMut(&'b K) -> bool, { - let hash = SafeHash::new(hash_bucket); + self.search(hash, is_match, true) + } - let size = self.map.table.size(); - let mut probe = Bucket::new(&mut self.map.table, hash); - let mut displacement = 0; - - loop { - let full = match probe.peek() { - Empty(bucket) => { - // Found a hole! - return RawEntryMut::Vacant(RawVacantEntryMut { - elem: NoElem(bucket, displacement), - hash_builder: &self.map.hash_builder, - }); - } - Full(bucket) => bucket, - }; - - let probe_displacement = full.displacement(); - - if probe_displacement < displacement { - // Found a luckier bucket than me. - // We can finish the search early if we hit any bucket - // with a lower distance to initial bucket than we've probed. - return RawEntryMut::Vacant(RawVacantEntryMut { - elem: NeqElem(full, probe_displacement), - hash_builder: &self.map.hash_builder, - }) - } - - // Call is_match even if hash doesn't match hash_bucket. - if is_match(full.read().0) { - return RawEntryMut::Occupied(RawOccupiedEntryMut { elem: full }); - } - - displacement += 1; - probe = full.next(); - debug_assert!(displacement <= size); - } + /// Search possible locations for an element with hash `hash` until `is_match` returns true for + /// one of them. There is no guarantee that all keys passed to `is_match` will have the provided + /// hash. + #[unstable(feature = "hash_raw_entry", issue = "54043")] + pub fn search_bucket(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S> + where for<'b> F: FnMut(&'b K) -> bool, + { + self.search(hash, is_match, false) } } @@ -2006,7 +1962,7 @@ impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> where S: BuildHasher, { /// Access an entry by key. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn from_key(self, k: &Q) -> Option<(&'a K, &'a V)> where K: Borrow, Q: Hash + Eq @@ -2017,7 +1973,7 @@ impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> } /// Access an entry by a key and its hash. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> Option<(&'a K, &'a V)> where K: Borrow, Q: Hash + Eq @@ -2026,17 +1982,36 @@ impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> self.from_hash(hash, |q| q.borrow().eq(k)) } - /// Access an entry by hash. - #[unstable(feature = "hash_raw_entry", issue = "42069")] - pub fn from_hash(self, hash: u64, is_match: F) -> Option<(&'a K, &'a V)> + fn search(self, hash: u64, is_match: F, compare_hashes: bool) -> Option<(&'a K, &'a V)> where F: FnMut(&K) -> bool { - match search_hashed(&self.map.table, SafeHash::new(hash), is_match) { + match search_hashed_nonempty(&self.map.table, + SafeHash::new(hash), + is_match, + compare_hashes) { InternalEntry::Occupied { elem } => Some(elem.into_refs()), InternalEntry::Vacant { .. } => None, InternalEntry::TableIsEmpty => unreachable!(), } } + + /// Access an entry by hash. + #[unstable(feature = "hash_raw_entry", issue = "54043")] + 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, true) + } + + /// Search possible locations for an element with hash `hash` until `is_match` returns true for + /// one of them. There is no guarantee that all keys passed to `is_match` will have the provided + /// hash. + #[unstable(feature = "hash_raw_entry", issue = "54043")] + pub fn search_bucket(self, hash: u64, is_match: F) -> Option<(&'a K, &'a V)> + where F: FnMut(&K) -> bool + { + self.search(hash, is_match, false) + } } impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { @@ -2046,17 +2021,18 @@ impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { /// # Examples /// /// ``` + /// #![feature(hash_raw_entry)] /// use std::collections::HashMap; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.raw_entry().search_by("poneyland").or_insert("poneyland", 12); + /// map.raw_entry_mut().from_key("poneyland").or_insert("poneyland", 12); /// /// assert_eq!(map["poneyland"], 12); /// - /// *map.raw_entry().search_by("poneyland").or_insert("poneyland", 12).1 += 10; + /// *map.raw_entry_mut().from_key("poneyland").or_insert("poneyland", 12).1 += 10; /// assert_eq!(map["poneyland"], 22); /// ``` - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn or_insert(self, default_key: K, default_val: V) -> (&'a mut K, &'a mut V) where K: Hash, S: BuildHasher, @@ -2073,17 +2049,18 @@ impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { /// # Examples /// /// ``` + /// #![feature(hash_raw_entry)] /// use std::collections::HashMap; /// /// let mut map: HashMap<&str, String> = HashMap::new(); /// - /// map.raw_entry().search_by("poneyland").or_insert_with(|| { - /// ("poneyland".to_string(), "hoho".to_string()) + /// map.raw_entry_mut().from_key("poneyland").or_insert_with(|| { + /// ("poneyland", "hoho".to_string()) /// }); /// /// assert_eq!(map["poneyland"], "hoho".to_string()); /// ``` - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn or_insert_with(self, default: F) -> (&'a mut K, &'a mut V) where F: FnOnce() -> (K, V), K: Hash, @@ -2104,23 +2081,24 @@ impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { /// # Examples /// /// ``` + /// #![feature(hash_raw_entry)] /// use std::collections::HashMap; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); /// - /// map.raw_entry() - /// .search_by("poneyland") + /// 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() - /// .search_by("poneyland") + /// map.raw_entry_mut() + /// .from_key("poneyland") /// .and_modify(|_k, v| { *v += 1 }) - /// .or_insert("poneyland", 42); + /// .or_insert("poneyland", 0); /// assert_eq!(map["poneyland"], 43); /// ``` - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn and_modify(self, f: F) -> Self where F: FnOnce(&mut K, &mut V) { @@ -2139,83 +2117,82 @@ impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { impl<'a, K, V> RawOccupiedEntryMut<'a, K, V> { /// Gets a reference to the key in the entry. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn key(&self) -> &K { self.elem.read().0 } /// Gets a mutable reference to the key in the entry. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn key_mut(&mut self) -> &mut K { self.elem.read_mut().0 } /// Converts the entry into a mutable reference to the key in the entry /// with a lifetime bound to the map itself. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn into_key(self) -> &'a mut K { self.elem.into_mut_refs().0 } /// Gets a reference to the value in the entry. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn get(&self) -> &V { self.elem.read().1 } /// Converts the OccupiedEntry into a mutable reference to the value in the entry /// with a lifetime bound to the map itself. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn into_mut(self) -> &'a mut V { self.elem.into_mut_refs().1 } /// Gets a mutable reference to the value in the entry. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn get_mut(&mut self) -> &mut V { self.elem.read_mut().1 } /// Gets a reference to the key and value in the entry. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn get_key_value(&mut self) -> (&K, &V) { self.elem.read() } /// Gets a mutable reference to the key and value in the entry. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn get_key_value_mut(&mut self) -> (&mut K, &mut V) { self.elem.read_mut() } /// Converts the OccupiedEntry into a mutable reference to the key and value in the entry /// with a lifetime bound to the map itself. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn into_key_value(self) -> (&'a mut K, &'a mut V) { self.elem.into_mut_refs() } /// Sets the value of the entry, and returns the entry's old value. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] 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. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] 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. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn remove(self) -> V { pop_internal(self.elem).1 } - /// Take the ownership of the key and value from the map. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn remove_entry(self) -> (K, V) { let (k, v, _) = pop_internal(self.elem); (k, v) @@ -2225,7 +2202,7 @@ impl<'a, K, V> RawOccupiedEntryMut<'a, K, V> { 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. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn insert(self, key: K, value: V) -> (&'a mut K, &'a mut V) where K: Hash, S: BuildHasher, @@ -2237,7 +2214,7 @@ 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. - #[unstable(feature = "hash_raw_entry", issue = "42069")] + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn insert_hashed_nocheck(self, hash: u64, key: K, value: V) -> (&'a mut K, &'a mut V) { let hash = SafeHash::new(hash); let b = match self.elem { @@ -2258,7 +2235,7 @@ impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> { } } -#[unstable(feature = "hash_raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "54043")] impl<'a, K, V, S> Debug for RawEntryBuilderMut<'a, K, V, S> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("RawEntryBuilder") @@ -2266,7 +2243,7 @@ impl<'a, K, V, S> Debug for RawEntryBuilderMut<'a, K, V, S> { } } -#[unstable(feature = "hash_raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "54043")] impl<'a, K: Debug, V: Debug, S> Debug for RawEntryMut<'a, K, V, S> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -2284,7 +2261,7 @@ impl<'a, K: Debug, V: Debug, S> Debug for RawEntryMut<'a, K, V, S> { } } -#[unstable(feature = "hash_raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "54043")] impl<'a, K: Debug, V: Debug> Debug for RawOccupiedEntryMut<'a, K, V> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("RawOccupiedEntryMut") @@ -2294,7 +2271,7 @@ impl<'a, K: Debug, V: Debug> Debug for RawOccupiedEntryMut<'a, K, V> { } } -#[unstable(feature = "hash_raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "54043")] impl<'a, K, V, S> Debug for RawVacantEntryMut<'a, K, V, S> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("RawVacantEntryMut") @@ -2302,7 +2279,7 @@ impl<'a, K, V, S> Debug for RawVacantEntryMut<'a, K, V, S> { } } -#[unstable(feature = "hash_raw_entry", issue = "42069")] +#[unstable(feature = "hash_raw_entry", issue = "54043")] impl<'a, K, V, S> Debug for RawEntryBuilder<'a, K, V, S> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("RawEntryBuilder") @@ -4271,30 +4248,37 @@ mod test_map { #[test] fn test_raw_entry() { - use super::RawEntry::{Occupied, Vacant}; + 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().search_by(&1) { + match map.raw_entry_mut().from_key(&1) { Vacant(_) => unreachable!(), Occupied(mut view) => { assert_eq!(view.get(), &10); assert_eq!(view.insert(100), 10); } } - assert_eq!(map.raw_entry_immut().hash_with(|mut h| { - 1i32.hash(&mut h); - h.finish() - }).search_with(|k| *k == 1) - .unwrap(), (&10, &100)); + 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.raw_entry().search_bucket(hash1, |k| *k == 1).unwrap(), (&1, &100)); assert_eq!(map.len(), 6); - // Existing key (update) - match map.raw_entry().hash_by(&2).search_by(&2) { + match map.raw_entry_mut().from_key(&2) { Vacant(_) => unreachable!(), Occupied(mut view) => { let v = view.get_mut(); @@ -4302,32 +4286,66 @@ mod test_map { *v = new_v; } } - assert_eq!(map.raw_entry_immut().search_by(&2).unwrap(), (&2, &200)); + 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.raw_entry().search_bucket(hash2, |k| *k == 2).unwrap(), (&2, &200)); assert_eq!(map.len(), 6); // Existing key (take) - match map.raw_entry().hash_with(|mut h| { - 3i32.hash(&mut h); - h.finish() - }).search_with(|k| *k == 3) { + 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_key_value(), (3, 30)); + assert_eq!(view.remove_entry(), (3, 30)); } } - assert_eq!(map.raw_entry_immut().search_by(&3), None); + 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.raw_entry().search_bucket(hash3, |k| *k == 3), None); assert_eq!(map.len(), 5); - // Inexistent key (insert) - match map.raw_entry().search_by(&10) { + // 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_immut().hash_by(&10).search_by(&10).unwrap(), (&10, &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); + assert_eq!(map.raw_entry().search_bucket(hash, |q| *q == 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), + } + match map.raw_entry_mut().search_bucket(hash, |q| *q == k) { + Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv), + Vacant(_) => assert_eq!(v, None), + } + } } } From 7220fbb713e7669772775d1c2b27a93e19eda051 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Thu, 13 Sep 2018 19:05:41 -0400 Subject: [PATCH 08/10] Fix links in docs --- src/libstd/collections/hash/map.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 62e5cdcf90e..8c44245d1f4 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -1844,7 +1844,10 @@ impl<'a, K, V> InternalEntry> { /// 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. +/// See the [`HashMap::raw_entry_mut`] docs for usage examples. +/// +/// [`HashMap::raw_entry_mut`]: struct.HashMap.html#method.raw_entry_mut + #[unstable(feature = "hash_raw_entry", issue = "54043")] pub struct RawEntryBuilderMut<'a, K: 'a, V: 'a, S: 'a> { map: &'a mut HashMap, @@ -1888,7 +1891,9 @@ pub struct RawVacantEntryMut<'a, K: 'a, V: 'a, S: 'a> { /// A builder for computing where in a HashMap a key-value pair would be stored. /// -/// See the [`HashMap::raw_entry`][] docs for usage examples. +/// See the [`HashMap::raw_entry`] docs for usage examples. +/// +/// [`HashMap::raw_entry`]: struct.HashMap.html#method.raw_entry #[unstable(feature = "hash_raw_entry", issue = "54043")] pub struct RawEntryBuilder<'a, K: 'a, V: 'a, S: 'a> { map: &'a HashMap, From a92594dfacd6d6323f920a9d64557174db813904 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Thu, 13 Sep 2018 21:21:53 -0400 Subject: [PATCH 09/10] Entry is an enum not a struct --- src/libstd/collections/hash/map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 8c44245d1f4..8b5957abe59 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -1860,7 +1860,7 @@ pub struct RawEntryBuilderMut<'a, K: 'a, V: 'a, S: 'a> { /// This `enum` is constructed from the [`raw_entry`] method on [`HashMap`]. /// /// [`HashMap`]: struct.HashMap.html -/// [`Entry`]: struct.Entry.html +/// [`Entry`]: enum.Entry.html /// [`raw_entry`]: struct.HashMap.html#method.raw_entry #[unstable(feature = "hash_raw_entry", issue = "54043")] pub enum RawEntryMut<'a, K: 'a, V: 'a, S: 'a> { From daf5bd564a0eb8ff7784f85a015cbe4f06ac162e Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Wed, 31 Oct 2018 15:15:20 -0400 Subject: [PATCH 10/10] A couple suggested edits --- src/libstd/collections/hash/map.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 8b5957abe59..4a62649434c 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -477,7 +477,7 @@ fn search_hashed_nonempty(table: M, hash: SafeHash, mut is_match: F, } // If the hash doesn't match, it can't be this one.. - if hash == full.hash() || !compare_hashes { + if !compare_hashes || hash == full.hash() { // If the key doesn't match, it can't be this one.. if is_match(full.read().0) { return InternalEntry::Occupied { elem: full }; @@ -489,7 +489,7 @@ fn search_hashed_nonempty(table: M, hash: SafeHash, mut is_match: F, } } -/// Search for a pre-hashed key when the hash map is known to be non-empty. +/// Same as `search_hashed_nonempty` but for mutable access. #[inline] fn search_hashed_nonempty_mut(table: M, hash: SafeHash, mut is_match: F, compare_hashes: bool) @@ -1571,10 +1571,6 @@ impl HashMap /// so that the map now contains keys which compare equal, search may start /// acting eratically, with two keys randomly masking eachother. Implementations /// are free to assume this doesn't happen (within the limits of memory-safety). - /// - /// # Examples - /// - /// #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut { self.reserve(1); @@ -1595,7 +1591,7 @@ impl HashMap /// 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`. + /// Immutable raw entries have very limited use; you might instead want `raw_entry_mut`. #[unstable(feature = "hash_raw_entry", issue = "54043")] pub fn raw_entry(&self) -> RawEntryBuilder { RawEntryBuilder { map: self }