Skip to content

Commit 6ced454

Browse files
ericktnikomatsakis
authored andcommitted
libcore: add pop/swap/consume to SendMap
1 parent a477c5a commit 6ced454

File tree

1 file changed

+126
-29
lines changed

1 file changed

+126
-29
lines changed

src/libcore/send_map.rs

Lines changed: 126 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ pub trait SendMap<K:Eq Hash, V: Copy> {
1717

1818
fn insert(&mut self, k: K, +v: V) -> bool;
1919
fn remove(&mut self, k: &K) -> bool;
20+
fn pop(&mut self, k: &K) -> Option<V>;
21+
fn swap(&mut self, k: K, +v: V) -> Option<V>;
22+
fn consume(&mut self, f: fn(K, V));
2023
fn clear(&mut self);
2124
pure fn len(&const self) -> uint;
2225
pure fn is_empty(&const self) -> bool;
@@ -198,6 +201,52 @@ pub mod linear {
198201
}
199202
}
200203

204+
fn pop_internal(&mut self, hash: uint, k: &K) -> Option<V> {
205+
// Removing from an open-addressed hashtable
206+
// is, well, painful. The problem is that
207+
// the entry may lie on the probe path for other
208+
// entries, so removing it would make you think that
209+
// those probe paths are empty.
210+
//
211+
// To address this we basically have to keep walking,
212+
// re-inserting entries we find until we reach an empty
213+
// bucket. We know we will eventually reach one because
214+
// we insert one ourselves at the beginning (the removed
215+
// entry).
216+
//
217+
// I found this explanation elucidating:
218+
// http://www.maths.lse.ac.uk/Courses/MA407/del-hash.pdf
219+
let mut idx = match self.bucket_for_key_with_hash(self.buckets,
220+
hash, k) {
221+
TableFull | FoundHole(_) => return None,
222+
FoundEntry(idx) => idx
223+
};
224+
225+
let len_buckets = self.buckets.len();
226+
let mut bucket = None;
227+
self.buckets[idx] <-> bucket;
228+
229+
let value = match move bucket {
230+
None => None,
231+
Some(move bucket) => {
232+
let Bucket { value: move value, _ } = move bucket;
233+
Some(value)
234+
},
235+
};
236+
237+
idx = self.next_bucket(idx, len_buckets);
238+
while self.buckets[idx].is_some() {
239+
let mut bucket = None;
240+
bucket <-> self.buckets[idx];
241+
self.insert_opt_bucket(move bucket);
242+
idx = self.next_bucket(idx, len_buckets);
243+
}
244+
self.size -= 1;
245+
246+
value
247+
248+
}
249+
201250
fn search(&self,
202251
hash: uint,
203252
op: fn(x: &Option<Bucket<K,V>>) -> bool) {
@@ -222,37 +271,55 @@ pub mod linear {
222271
}
223272

224273
fn remove(&mut self, k: &K) -> bool {
225-
// Removing from an open-addressed hashtable
226-
// is, well, painful. The problem is that
227-
// the entry may lie on the probe path for other
228-
// entries, so removing it would make you think that
229-
// those probe paths are empty.
230-
//
231-
// To address this we basically have to keep walking,
232-
// re-inserting entries we find until we reach an empty
233-
// bucket. We know we will eventually reach one because
234-
// we insert one ourselves at the beginning (the removed
235-
// entry).
236-
//
237-
// I found this explanation elucidating:
238-
// http://www.maths.lse.ac.uk/Courses/MA407/del-hash.pdf
274+
match self.pop(k) {
275+
Some(_) => true,
276+
None => false,
277+
}
278+
}
239279

240-
let mut idx = match self.bucket_for_key(self.buckets, k) {
241-
TableFull | FoundHole(_) => return false,
242-
FoundEntry(idx) => idx
243-
};
280+
fn pop(&mut self, k: &K) -> Option<V> {
281+
let hash = k.hash_keyed(self.k0, self.k1) as uint;
282+
self.pop_internal(hash, k)
283+
}
244284

245-
let len_buckets = self.buckets.len();
246-
self.buckets[idx] = None;
247-
idx = self.next_bucket(idx, len_buckets);
248-
while self.buckets[idx].is_some() {
249-
let mut bucket = None;
250-
bucket <-> self.buckets[idx];
251-
self.insert_opt_bucket(move bucket);
252-
idx = self.next_bucket(idx, len_buckets);
285+
fn swap(&mut self, k: K, v: V) -> Option<V> {
286+
// this could be faster.
287+
let hash = k.hash_keyed(self.k0, self.k1) as uint;
288+
let old_value = self.pop_internal(hash, &k);
289+
290+
if self.size >= self.resize_at {
291+
// n.b.: We could also do this after searching, so
292+
// that we do not resize if this call to insert is
293+
// simply going to update a key in place. My sense
294+
// though is that it's worse to have to search through
295+
// buckets to find the right spot twice than to just
296+
// resize in this corner case.
297+
self.expand();
298+
}
299+
300+
self.insert_internal(hash, k, v);
301+
302+
old_value
303+
}
304+
305+
fn consume(&mut self, f: fn(K, V)) {
306+
let mut buckets = ~[];
307+
self.buckets <-> buckets;
308+
self.size = 0;
309+
310+
do vec::consume(buckets) |_i, bucket| {
311+
match move bucket {
312+
None => { },
313+
Some(move bucket) => {
314+
let Bucket {
315+
key: move key,
316+
value: move value,
317+
_
318+
} = move bucket;
319+
f(key, value)
320+
}
321+
}
253322
}
254-
self.size -= 1;
255-
return true;
256323
}
257324

258325
fn clear(&mut self) {
@@ -350,7 +417,6 @@ pub mod linear {
350417
}
351418
option::unwrap(move value)
352419
}
353-
354420
}
355421
}
356422

@@ -407,6 +473,37 @@ pub mod test {
407473
assert m.is_empty();
408474
}
409475

476+
#[test]
477+
pub fn pops() {
478+
let mut m = ~LinearMap();
479+
m.insert(1, 2);
480+
assert m.pop(&1) == Some(2);
481+
assert m.pop(&1) == None;
482+
}
483+
484+
#[test]
485+
pub fn swaps() {
486+
let mut m = ~LinearMap();
487+
assert m.swap(1, 2) == None;
488+
assert m.swap(1, 3) == Some(2);
489+
assert m.swap(1, 4) == Some(3);
490+
}
491+
492+
#[test]
493+
pub fn consumes() {
494+
let mut m = ~LinearMap();
495+
assert m.insert(1, 2);
496+
assert m.insert(2, 3);
497+
let mut m2 = ~LinearMap();
498+
do m.consume |k, v| {
499+
m2.insert(k, v);
500+
}
501+
assert m.len() == 0;
502+
assert m2.len() == 2;
503+
assert m2.find(&1) == Some(2);
504+
assert m2.find(&2) == Some(3);
505+
}
506+
410507
#[test]
411508
pub fn iterate() {
412509
let mut m = linear::linear_map_with_capacity(4);

0 commit comments

Comments
 (0)