From 5929813476bef1653cf761d197464ddc36668fae Mon Sep 17 00:00:00 2001 From: Michael J Ward Date: Wed, 1 Jul 2020 21:33:17 -0500 Subject: [PATCH 1/2] first pass at OccupiedEntry.replace_ implementations This is directly taken from `hashbrown` There is an existing warning in `src/map.rs` that the hashbrown implementation will not work, but I don't currently see why. --- src/map.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/set.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/src/map.rs b/src/map.rs index d3527c0..4ffdcd9 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1857,6 +1857,66 @@ impl<'a, K, V, S> OccupiedEntry<'a, K, V, S> { // restarting the iterator, though that would come at a performance penalty... // // when implemented, remove the #[allow(dead_code)] on OccupiedEntry::key + + /// Replaces the entry, returning the old key and value. The new key in the hash map will be + /// the key used to create this entry. + /// + /// # Examples + /// + /// ``` + /// use griddle::hash_map::{Entry, HashMap}; + /// use std::rc::Rc; + /// + /// let mut map: HashMap, u32> = HashMap::new(); + /// map.insert(Rc::new("Stringthing".to_string()), 15); + /// + /// let my_key = Rc::new("Stringthing".to_string()); + /// + /// if let Entry::Occupied(entry) = map.entry(my_key) { + /// // Also replace the key with a handle to our other key. + /// let (old_key, old_value): (Rc, u32) = entry.replace_entry(16); + /// } + /// + /// ``` + #[cfg_attr(feature = "inline-more", inline)] + pub fn replace_entry(self, value: V) -> (K, V) { + let entry = unsafe { self.elem.as_mut() }; + + let old_key = mem::replace(&mut entry.0, self.key.unwrap()); + let old_value = mem::replace(&mut entry.1, value); + + (old_key, old_value) + } + + /// Replaces the key in the hash map with the key used to create this entry. + /// + /// # Examples + /// + /// ``` + /// use griddle::hash_map::{Entry, HashMap}; + /// use std::rc::Rc; + /// + /// let mut map: HashMap, u32> = HashMap::new(); + /// let mut known_strings: Vec> = Vec::new(); + /// + /// // Initialise known strings, run program, etc. + /// + /// reclaim_memory(&mut map, &known_strings); + /// + /// fn reclaim_memory(map: &mut HashMap, u32>, known_strings: &[Rc] ) { + /// for s in known_strings { + /// if let Entry::Occupied(entry) = map.entry(s.clone()) { + /// // Replaces the entry's key with our version of it in `known_strings`. + /// entry.replace_key(); + /// } + /// } + /// } + /// ``` + #[cfg_attr(feature = "inline-more", inline)] + pub fn replace_key(self) -> K { + let entry = unsafe { self.elem.as_mut() }; + mem::replace(&mut entry.0, self.key.unwrap()) + } } impl<'a, K, V, S> VacantEntry<'a, K, V, S> { diff --git a/src/set.rs b/src/set.rs index 9619992..f3cc0cf 100644 --- a/src/set.rs +++ b/src/set.rs @@ -720,6 +720,32 @@ where self.map.insert(value, ()).is_none() } + /// Adds a value to the set, replacing the existing value, if any, that is equal to the given + /// one. Returns the replaced value. + /// + /// # Examples + /// + /// ``` + /// use griddle::HashSet; + /// + /// let mut set = HashSet::new(); + /// set.insert(Vec::::new()); + /// + /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 0); + /// set.replace(Vec::with_capacity(10)); + /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 10); + /// ``` + #[cfg_attr(feature = "inline-more", inline)] + pub fn replace(&mut self, value: T) -> Option { + match self.map.entry(value) { + map::Entry::Occupied(occupied) => Some(occupied.replace_key()), + map::Entry::Vacant(vacant) => { + vacant.insert(()); + None + } + } + } + /// Removes a value from the set. Returns whether the value was /// present in the set. /// @@ -1681,6 +1707,38 @@ mod test_set { assert_eq!(format!("{:?}", empty), "{}"); } + #[test] + fn test_replace() { + use core::hash; + + #[derive(Debug)] + struct Foo(&'static str, i32); + + impl PartialEq for Foo { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + + impl Eq for Foo {} + + impl hash::Hash for Foo { + fn hash(&self, h: &mut H) { + self.0.hash(h); + } + } + + let mut s = HashSet::new(); + assert_eq!(s.replace(Foo("a", 1)), None); + assert_eq!(s.len(), 1); + assert_eq!(s.replace(Foo("a", 2)), Some(Foo("a", 1))); + assert_eq!(s.len(), 1); + + let mut it = s.iter(); + assert_eq!(it.next(), Some(&Foo("a", 2))); + assert_eq!(it.next(), None); + } + #[test] fn test_extend_ref() { let mut a = HashSet::new(); From d25bc2304f85883b5982d216fbafbc3c8b4221b4 Mon Sep 17 00:00:00 2001 From: Michael J Ward Date: Thu, 2 Jul 2020 07:51:01 -0500 Subject: [PATCH 2/2] removes allow(dead_code) annotation from OccupiedEntry::key --- src/map.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map.rs b/src/map.rs index 4ffdcd9..b3f9a1f 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1236,7 +1236,6 @@ impl Debug for Entry<'_, K, V, S> { /// /// [`Entry`]: enum.Entry.html pub struct OccupiedEntry<'a, K, V, S> { - #[allow(dead_code)] // remove when replace_ methods are implemented key: Option, elem: Bucket<(K, V)>, table: &'a mut HashMap, @@ -1872,11 +1871,12 @@ impl<'a, K, V, S> OccupiedEntry<'a, K, V, S> { /// /// let my_key = Rc::new("Stringthing".to_string()); /// - /// if let Entry::Occupied(entry) = map.entry(my_key) { + /// if let Entry::Occupied(entry) = map.entry(my_key.clone()) { /// // Also replace the key with a handle to our other key. /// let (old_key, old_value): (Rc, u32) = entry.replace_entry(16); /// } /// + /// assert_eq!(map[&my_key], 16); /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn replace_entry(self, value: V) -> (K, V) {