libcore: add pop/swap/consume to SendMap
This commit is contained in:
parent
a477c5af20
commit
6ced454b96
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue