libcore: add pop/swap/consume to SendMap

This commit is contained in:
Erick Tryzelaar 2012-10-08 07:05:01 -07:00 committed by Niko Matsakis
parent a477c5af20
commit 6ced454b96
1 changed files with 128 additions and 31 deletions

View File

@ -17,6 +17,9 @@ pub trait SendMap<K:Eq Hash, V: Copy> {
fn insert(&mut self, k: K, +v: V) -> bool; fn insert(&mut self, k: K, +v: V) -> bool;
fn remove(&mut self, k: &K) -> bool; fn remove(&mut self, k: &K) -> bool;
fn pop(&mut self, k: &K) -> Option<V>;
fn swap(&mut self, k: K, +v: V) -> Option<V>;
fn consume(&mut self, f: fn(K, V));
fn clear(&mut self); fn clear(&mut self);
pure fn len(&const self) -> uint; pure fn len(&const self) -> uint;
pure fn is_empty(&const self) -> bool; pure fn is_empty(&const self) -> bool;
@ -198,6 +201,52 @@ pub mod linear {
} }
} }
fn pop_internal(&mut self, hash: uint, k: &K) -> Option<V> {
// Removing from an open-addressed hashtable
// is, well, painful. The problem is that
// the entry may lie on the probe path for other
// entries, so removing it would make you think that
// those probe paths are empty.
//
// To address this we basically have to keep walking,
// re-inserting entries we find until we reach an empty
// bucket. We know we will eventually reach one because
// we insert one ourselves at the beginning (the removed
// entry).
//
// I found this explanation elucidating:
// http://www.maths.lse.ac.uk/Courses/MA407/del-hash.pdf
let mut idx = match self.bucket_for_key_with_hash(self.buckets,
hash, k) {
TableFull | FoundHole(_) => return None,
FoundEntry(idx) => idx
};
let len_buckets = self.buckets.len();
let mut bucket = None;
self.buckets[idx] <-> bucket;
let value = match move bucket {
None => None,
Some(move bucket) => {
let Bucket { value: move value, _ } = move bucket;
Some(value)
},
};
idx = self.next_bucket(idx, len_buckets);
while self.buckets[idx].is_some() {
let mut bucket = None;
bucket <-> self.buckets[idx];
self.insert_opt_bucket(move bucket);
idx = self.next_bucket(idx, len_buckets);
}
self.size -= 1;
value
}
fn search(&self, fn search(&self,
hash: uint, hash: uint,
op: fn(x: &Option<Bucket<K,V>>) -> bool) { op: fn(x: &Option<Bucket<K,V>>) -> bool) {
@ -222,37 +271,55 @@ pub mod linear {
} }
fn remove(&mut self, k: &K) -> bool { fn remove(&mut self, k: &K) -> bool {
// Removing from an open-addressed hashtable match self.pop(k) {
// is, well, painful. The problem is that Some(_) => true,
// the entry may lie on the probe path for other None => false,
// entries, so removing it would make you think that }
// those probe paths are empty. }
//
// To address this we basically have to keep walking, fn pop(&mut self, k: &K) -> Option<V> {
// re-inserting entries we find until we reach an empty let hash = k.hash_keyed(self.k0, self.k1) as uint;
// bucket. We know we will eventually reach one because self.pop_internal(hash, k)
// we insert one ourselves at the beginning (the removed }
// entry).
// fn swap(&mut self, k: K, v: V) -> Option<V> {
// I found this explanation elucidating: // this could be faster.
// http://www.maths.lse.ac.uk/Courses/MA407/del-hash.pdf let hash = k.hash_keyed(self.k0, self.k1) as uint;
let old_value = self.pop_internal(hash, &k);
let mut idx = match self.bucket_for_key(self.buckets, k) {
TableFull | FoundHole(_) => return false, if self.size >= self.resize_at {
FoundEntry(idx) => idx // n.b.: We could also do this after searching, so
}; // that we do not resize if this call to insert is
// simply going to update a key in place. My sense
let len_buckets = self.buckets.len(); // though is that it's worse to have to search through
self.buckets[idx] = None; // buckets to find the right spot twice than to just
idx = self.next_bucket(idx, len_buckets); // resize in this corner case.
while self.buckets[idx].is_some() { self.expand();
let mut bucket = None; }
bucket <-> self.buckets[idx];
self.insert_opt_bucket(move bucket); self.insert_internal(hash, k, v);
idx = self.next_bucket(idx, len_buckets);
old_value
}
fn consume(&mut self, f: fn(K, V)) {
let mut buckets = ~[];
self.buckets <-> buckets;
self.size = 0;
do vec::consume(buckets) |_i, bucket| {
match move bucket {
None => { },
Some(move bucket) => {
let Bucket {
key: move key,
value: move value,
_
} = move bucket;
f(key, value)
}
}
} }
self.size -= 1;
return true;
} }
fn clear(&mut self) { fn clear(&mut self) {
@ -350,7 +417,6 @@ pub mod linear {
} }
option::unwrap(move value) option::unwrap(move value)
} }
} }
} }
@ -407,6 +473,37 @@ pub mod test {
assert m.is_empty(); assert m.is_empty();
} }
#[test]
pub fn pops() {
let mut m = ~LinearMap();
m.insert(1, 2);
assert m.pop(&1) == Some(2);
assert m.pop(&1) == None;
}
#[test]
pub fn swaps() {
let mut m = ~LinearMap();
assert m.swap(1, 2) == None;
assert m.swap(1, 3) == Some(2);
assert m.swap(1, 4) == Some(3);
}
#[test]
pub fn consumes() {
let mut m = ~LinearMap();
assert m.insert(1, 2);
assert m.insert(2, 3);
let mut m2 = ~LinearMap();
do m.consume |k, v| {
m2.insert(k, v);
}
assert m.len() == 0;
assert m2.len() == 2;
assert m2.find(&1) == Some(2);
assert m2.find(&2) == Some(3);
}
#[test] #[test]
pub fn iterate() { pub fn iterate() {
let mut m = linear::linear_map_with_capacity(4); let mut m = linear::linear_map_with_capacity(4);