Skip to content

Reintroduces the 'mangle' abstraction that was removed in the switch to robinhood hashing. #13197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 77 additions & 10 deletions src/libcollections/hashmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1166,43 +1166,65 @@ impl<K: TotalEq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
fail!("Internal HashMap error: Out of space.");
}

/// Inserts an element, returning a reference to that element inside the
/// hashtable.
fn manual_insert_hashed<'a>(&'a mut self, hash: table::SafeHash, k: K, v: V) -> &'a mut V {
let potential_new_size = self.table.size() + 1;
self.make_some_room(potential_new_size);
self.manual_insert_hashed_nocheck(hash, k, v)
}

/// Inserts an element, returning a reference to that element inside the
/// hashtable.
fn manual_insert<'a>(&'a mut self, k: K, v: V) -> &'a mut V {
/// Modify and return the value corresponding to the key in the map, or
/// insert and return a new value if it doesn't exist.
///
/// This method allows for all insertion behaviours of a hashmap, see
/// methods like insert, find_or_insert and insert_or_update_with for less
/// general and more friendly variations of this.
pub fn mangle<'a, A>(&'a mut self, k: K, a: A,
not_found: |&K, A| -> V, found: |&K, &mut V, A|) -> &'a mut V {
let hash = self.make_hash(&k);
self.manual_insert_hashed(hash, k, v)

match self.search_hashed(&hash, &k) {
Some(idx) => {
let (k_ref, v_ref) = self.table.read_mut(&idx);
found(k_ref, v_ref, a);
v_ref
},
None => {
let v = not_found(&k, a);
self.manual_insert_hashed(hash, k, v)
}
}
}

/// Return the value corresponding to the key in the map, or insert
/// and return the value if it doesn't exist.
pub fn find_or_insert<'a>(&'a mut self, k: K, v: V) -> &'a mut V {
match self.search(&k) {
let hash = self.make_hash(&k);

match self.search_hashed(&hash, &k) {
Some(idx) => {
let (_, v_ref) = self.table.read_mut(&idx);
v_ref
},
None => self.manual_insert(k, v)
None => self.manual_insert_hashed(hash, k, v)
}
}

/// Return the value corresponding to the key in the map, or create,
/// insert, and return a new value if it doesn't exist.
pub fn find_or_insert_with<'a>(&'a mut self, k: K, f: |&K| -> V)
-> &'a mut V {
match self.search(&k) {
let hash = self.make_hash(&k);

match self.search_hashed(&hash, &k) {
Some(idx) => {
let (_, v_ref) = self.table.read_mut(&idx);
v_ref
},
None => {
let v = f(&k);
self.manual_insert(k, v)
self.manual_insert_hashed(hash, k, v)
}
}
}
Expand All @@ -1216,8 +1238,10 @@ impl<K: TotalEq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
v: V,
f: |&K, &mut V|)
-> &'a mut V {
match self.search(&k) {
None => self.manual_insert(k, v),
let hash = self.make_hash(&k);

match self.search_hashed(&hash, &k) {
None => self.manual_insert_hashed(hash, k, v),
Some(idx) => {
let (_, v_ref) = self.table.read_mut(&idx);
f(&k, v_ref);
Expand Down Expand Up @@ -1949,6 +1973,49 @@ mod test_map {
assert_eq!(map.find(&k), Some(&v));
}
}

#[test]
fn test_mangle_found() {
let mut was_found = false;

let mut map: HashMap<int, int> =
[(0, 1), (2, 3), (4, 5)].iter().map(|&x| x).collect();

map.mangle(4, (),
|k, ()| {
assert!(*k == 4);
assert!(false);
return 6;
},
|k, v, ()| {
assert!(*k == 4);
assert!(*v == 5);
was_found = true;
});

assert!(was_found == true);
}

#[test]
fn test_mangle_notfound() {
let mut was_found = true;

let mut map: HashMap<int, int> =
[(0, 1), (2, 3), (4, 5)].iter().map(|&x| x).collect();

map.mangle(5, (),
|k, ()| {
assert!(*k == 5);
was_found = false;
return 6;
},
|k, _, ()| {
assert!(*k == 5);
assert!(false);
});

assert!(was_found == false);
}
}

#[cfg(test)]
Expand Down