Skip to content

Commit 4fb4a4b

Browse files
committed
core: add LinearMap::find_or_insert{,_with}
This allows for inserting a new value into the map only if it doesn't already exist in the map.
1 parent 849644b commit 4fb4a4b

File tree

1 file changed

+81
-13
lines changed

1 file changed

+81
-13
lines changed

src/libcore/hashmap.rs

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,14 @@ pub mod linear {
155155
}
156156
}
157157

158+
#[inline(always)]
159+
pure fn value_for_bucket(&self, idx: uint) -> &self/V {
160+
match self.buckets[idx] {
161+
Some(ref bkt) => &bkt.value,
162+
None => die!(~"LinearMap::find: internal logic error"),
163+
}
164+
}
165+
158166
/// Inserts the key value pair into the buckets.
159167
/// Assumes that there will be a bucket.
160168
/// True if there was no previous entry with that key
@@ -289,19 +297,8 @@ pub mod linear {
289297
/// Return the value corresponding to the key in the map
290298
pure fn find(&self, k: &K) -> Option<&self/V> {
291299
match self.bucket_for_key(k) {
292-
FoundEntry(idx) => {
293-
match self.buckets[idx] {
294-
Some(ref bkt) => {
295-
Some(&bkt.value)
296-
}
297-
None => {
298-
die!(~"LinearMap::find: internal logic error")
299-
}
300-
}
301-
}
302-
TableFull | FoundHole(_) => {
303-
None
304-
}
300+
FoundEntry(idx) => Some(self.value_for_bucket(idx)),
301+
TableFull | FoundHole(_) => None,
305302
}
306303
}
307304
@@ -361,6 +358,63 @@ pub mod linear {
361358
old_value
362359
}
363360
361+
/// Return the value corresponding to the key in the map, or insert
362+
/// and return the value if it doesn't exist.
363+
fn find_or_insert(&mut self, k: K, v: V) -> &self/V {
364+
if self.size >= self.resize_at {
365+
// n.b.: We could also do this after searching, so
366+
// that we do not resize if this call to insert is
367+
// simply going to update a key in place. My sense
368+
// though is that it's worse to have to search through
369+
// buckets to find the right spot twice than to just
370+
// resize in this corner case.
371+
self.expand();
372+
}
373+
374+
let hash = k.hash_keyed(self.k0, self.k1) as uint;
375+
let idx = match self.bucket_for_key_with_hash(hash, &k) {
376+
TableFull => die!(~"Internal logic error"),
377+
FoundEntry(idx) => idx,
378+
FoundHole(idx) => {
379+
self.buckets[idx] = Some(Bucket{hash: hash, key: k,
380+
value: v});
381+
self.size += 1;
382+
idx
383+
},
384+
};
385+
386+
self.value_for_bucket(idx)
387+
}
388+
389+
/// Return the value corresponding to the key in the map, or create,
390+
/// insert, and return a new value if it doesn't exist.
391+
fn find_or_insert_with(&mut self, k: K, f: fn(&K) -> V) -> &self/V {
392+
if self.size >= self.resize_at {
393+
// n.b.: We could also do this after searching, so
394+
// that we do not resize if this call to insert is
395+
// simply going to update a key in place. My sense
396+
// though is that it's worse to have to search through
397+
// buckets to find the right spot twice than to just
398+
// resize in this corner case.
399+
self.expand();
400+
}
401+
402+
let hash = k.hash_keyed(self.k0, self.k1) as uint;
403+
let idx = match self.bucket_for_key_with_hash(hash, &k) {
404+
TableFull => die!(~"Internal logic error"),
405+
FoundEntry(idx) => idx,
406+
FoundHole(idx) => {
407+
let v = f(&k);
408+
self.buckets[idx] = Some(Bucket{hash: hash, key: k,
409+
value: v});
410+
self.size += 1;
411+
idx
412+
},
413+
};
414+
415+
self.value_for_bucket(idx)
416+
}
417+
364418
fn consume(&mut self, f: fn(K, V)) {
365419
let mut buckets = ~[];
366420
self.buckets <-> buckets;
@@ -582,6 +636,20 @@ mod test_map {
582636
assert m.swap(1, 4) == Some(3);
583637
}
584638

639+
#[test]
640+
pub fn test_find_or_insert() {
641+
let mut m = LinearMap::new::<int, int>();
642+
assert m.find_or_insert(1, 2) == &2;
643+
assert m.find_or_insert(1, 3) == &2;
644+
}
645+
646+
#[test]
647+
pub fn test_find_or_insert_with() {
648+
let mut m = LinearMap::new::<int, int>();
649+
assert m.find_or_insert_with(1, |_| 2) == &2;
650+
assert m.find_or_insert_with(1, |_| 3) == &2;
651+
}
652+
585653
#[test]
586654
pub fn test_consume() {
587655
let mut m = LinearMap::new();

0 commit comments

Comments
 (0)