Skip to content

Commit

Permalink
OccupiedEntry.replace_* (#11)
Browse files Browse the repository at this point in the history
This is directly taken from `hashbrown`
  • Loading branch information
Michael-J-Ward authored Jul 2, 2020
1 parent 13509e4 commit 78787e6
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 1 deletion.
62 changes: 61 additions & 1 deletion src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1242,7 +1242,6 @@ impl<K: Debug, V: Debug, S> 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<K>,
elem: Bucket<(K, V)>,
table: &'a mut HashMap<K, V, S>,
Expand Down Expand Up @@ -1863,6 +1862,67 @@ 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<Rc<String>, u32> = HashMap::new();
/// map.insert(Rc::new("Stringthing".to_string()), 15);
///
/// let my_key = Rc::new("Stringthing".to_string());
///
/// if let Entry::Occupied(entry) = map.entry(my_key.clone()) {
/// // Also replace the key with a handle to our other key.
/// let (old_key, old_value): (Rc<String>, 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) {
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<Rc<String>, u32> = HashMap::new();
/// let mut known_strings: Vec<Rc<String>> = Vec::new();
///
/// // Initialise known strings, run program, etc.
///
/// reclaim_memory(&mut map, &known_strings);
///
/// fn reclaim_memory(map: &mut HashMap<Rc<String>, u32>, known_strings: &[Rc<String>] ) {
/// 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> {
Expand Down
58 changes: 58 additions & 0 deletions src/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<i32>::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<T> {
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.
///
Expand Down Expand Up @@ -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<H: hash::Hasher>(&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();
Expand Down

0 comments on commit 78787e6

Please sign in to comment.