From fed5df8f2edbdd78573a54bb5baec71c0056fc9b Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 10 Feb 2013 13:56:03 -0800 Subject: [PATCH 1/3] libcore: LinearMap doesn't need to pass around the bucket vec --- src/libcore/hashmap.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/libcore/hashmap.rs b/src/libcore/hashmap.rs index a69cf4611bb7f..81cfe2702398d 100644 --- a/src/libcore/hashmap.rs +++ b/src/libcore/hashmap.rs @@ -108,19 +108,17 @@ pub mod linear { } #[inline(always)] - pure fn bucket_for_key(&self, buckets: &[Option>], - k: &K) -> SearchResult { + pure fn bucket_for_key(&self, k: &K) -> SearchResult { let hash = k.hash_keyed(self.k0, self.k1) as uint; - self.bucket_for_key_with_hash(buckets, hash, k) + self.bucket_for_key_with_hash(hash, k) } #[inline(always)] pure fn bucket_for_key_with_hash(&self, - buckets: &[Option>], hash: uint, k: &K) -> SearchResult { let _ = for self.bucket_sequence(hash) |i| { - match buckets[i] { + match self.buckets[i] { Some(ref bkt) => if bkt.hash == hash && *k == bkt.key { return FoundEntry(i); }, @@ -161,7 +159,7 @@ pub mod linear { /// Assumes that there will be a bucket. /// True if there was no previous entry with that key fn insert_internal(&mut self, hash: uint, k: K, v: V) -> bool { - match self.bucket_for_key_with_hash(self.buckets, hash, &k) { + match self.bucket_for_key_with_hash(hash, &k) { TableFull => { die!(~"Internal logic error"); } FoundHole(idx) => { debug!("insert fresh (%?->%?) at idx %?, hash %?", @@ -196,8 +194,7 @@ pub mod linear { // // 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) { + let mut idx = match self.bucket_for_key_with_hash(hash, k) { TableFull | FoundHole(_) => return None, FoundEntry(idx) => idx }; @@ -273,7 +270,7 @@ pub mod linear { impl LinearMap: Map { /// Return true if the map contains a value for the specified key pure fn contains_key(&self, k: &K) -> bool { - match self.bucket_for_key(self.buckets, k) { + match self.bucket_for_key(k) { FoundEntry(_) => {true} TableFull | FoundHole(_) => {false} } @@ -291,7 +288,7 @@ pub mod linear { /// Return the value corresponding to the key in the map pure fn find(&self, k: &K) -> Option<&self/V> { - match self.bucket_for_key(self.buckets, k) { + match self.bucket_for_key(k) { FoundEntry(idx) => { match self.buckets[idx] { Some(ref bkt) => { From 849644b5bc6b1fe836d96bed61b55cba92e5b5e1 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 10 Feb 2013 14:39:38 -0800 Subject: [PATCH 2/3] core: rename hashmap test functions --- src/libcore/hashmap.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libcore/hashmap.rs b/src/libcore/hashmap.rs index 81cfe2702398d..b8906e126a8d8 100644 --- a/src/libcore/hashmap.rs +++ b/src/libcore/hashmap.rs @@ -518,7 +518,7 @@ mod test_map { use uint; #[test] - pub fn inserts() { + pub fn test_insert() { let mut m = LinearMap::new(); assert m.insert(1, 2); assert m.insert(2, 4); @@ -527,7 +527,7 @@ mod test_map { } #[test] - pub fn overwrite() { + pub fn test_insert_overwrite() { let mut m = LinearMap::new(); assert m.insert(1, 2); assert *m.get(&1) == 2; @@ -536,7 +536,7 @@ mod test_map { } #[test] - pub fn conflicts() { + pub fn test_insert_conflicts() { let mut m = linear::linear_map_with_capacity(4); assert m.insert(1, 2); assert m.insert(5, 3); @@ -547,7 +547,7 @@ mod test_map { } #[test] - pub fn conflict_remove() { + pub fn test_conflict_remove() { let mut m = linear::linear_map_with_capacity(4); assert m.insert(1, 2); assert m.insert(5, 3); @@ -558,7 +558,7 @@ mod test_map { } #[test] - pub fn empty() { + pub fn test_is_empty() { let mut m = linear::linear_map_with_capacity(4); assert m.insert(1, 2); assert !m.is_empty(); @@ -567,7 +567,7 @@ mod test_map { } #[test] - pub fn pops() { + pub fn test_pop() { let mut m = LinearMap::new(); m.insert(1, 2); assert m.pop(&1) == Some(2); @@ -575,7 +575,7 @@ mod test_map { } #[test] - pub fn swaps() { + pub fn test_swap() { let mut m = LinearMap::new(); assert m.swap(1, 2) == None; assert m.swap(1, 3) == Some(2); @@ -583,7 +583,7 @@ mod test_map { } #[test] - pub fn consumes() { + pub fn test_consume() { let mut m = LinearMap::new(); assert m.insert(1, 2); assert m.insert(2, 3); @@ -598,7 +598,7 @@ mod test_map { } #[test] - pub fn iterate() { + pub fn test_iterate() { let mut m = linear::linear_map_with_capacity(4); for uint::range(0, 32) |i| { assert m.insert(i, i*2); @@ -612,7 +612,7 @@ mod test_map { } #[test] - pub fn find() { + pub fn test_find() { let mut m = LinearMap::new(); assert m.find(&1).is_none(); m.insert(1, 2); From 4fb4a4b66d5c988d79f77b081dabd8f62b880dfe Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 10 Feb 2013 15:30:44 -0800 Subject: [PATCH 3/3] 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. --- src/libcore/hashmap.rs | 94 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 13 deletions(-) diff --git a/src/libcore/hashmap.rs b/src/libcore/hashmap.rs index b8906e126a8d8..70358bab46874 100644 --- a/src/libcore/hashmap.rs +++ b/src/libcore/hashmap.rs @@ -155,6 +155,14 @@ pub mod linear { } } + #[inline(always)] + pure fn value_for_bucket(&self, idx: uint) -> &self/V { + match self.buckets[idx] { + Some(ref bkt) => &bkt.value, + None => die!(~"LinearMap::find: internal logic error"), + } + } + /// Inserts the key value pair into the buckets. /// Assumes that there will be a bucket. /// True if there was no previous entry with that key @@ -289,19 +297,8 @@ pub mod linear { /// Return the value corresponding to the key in the map pure fn find(&self, k: &K) -> Option<&self/V> { match self.bucket_for_key(k) { - FoundEntry(idx) => { - match self.buckets[idx] { - Some(ref bkt) => { - Some(&bkt.value) - } - None => { - die!(~"LinearMap::find: internal logic error") - } - } - } - TableFull | FoundHole(_) => { - None - } + FoundEntry(idx) => Some(self.value_for_bucket(idx)), + TableFull | FoundHole(_) => None, } } @@ -361,6 +358,63 @@ pub mod linear { old_value } + /// Return the value corresponding to the key in the map, or insert + /// and return the value if it doesn't exist. + fn find_or_insert(&mut self, k: K, v: V) -> &self/V { + if self.size >= self.resize_at { + // 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 + // though is that it's worse to have to search through + // buckets to find the right spot twice than to just + // resize in this corner case. + self.expand(); + } + + let hash = k.hash_keyed(self.k0, self.k1) as uint; + let idx = match self.bucket_for_key_with_hash(hash, &k) { + TableFull => die!(~"Internal logic error"), + FoundEntry(idx) => idx, + FoundHole(idx) => { + self.buckets[idx] = Some(Bucket{hash: hash, key: k, + value: v}); + self.size += 1; + idx + }, + }; + + self.value_for_bucket(idx) + } + + /// Return the value corresponding to the key in the map, or create, + /// insert, and return a new value if it doesn't exist. + fn find_or_insert_with(&mut self, k: K, f: fn(&K) -> V) -> &self/V { + if self.size >= self.resize_at { + // 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 + // though is that it's worse to have to search through + // buckets to find the right spot twice than to just + // resize in this corner case. + self.expand(); + } + + let hash = k.hash_keyed(self.k0, self.k1) as uint; + let idx = match self.bucket_for_key_with_hash(hash, &k) { + TableFull => die!(~"Internal logic error"), + FoundEntry(idx) => idx, + FoundHole(idx) => { + let v = f(&k); + self.buckets[idx] = Some(Bucket{hash: hash, key: k, + value: v}); + self.size += 1; + idx + }, + }; + + self.value_for_bucket(idx) + } + fn consume(&mut self, f: fn(K, V)) { let mut buckets = ~[]; self.buckets <-> buckets; @@ -582,6 +636,20 @@ mod test_map { assert m.swap(1, 4) == Some(3); } + #[test] + pub fn test_find_or_insert() { + let mut m = LinearMap::new::(); + assert m.find_or_insert(1, 2) == &2; + assert m.find_or_insert(1, 3) == &2; + } + + #[test] + pub fn test_find_or_insert_with() { + let mut m = LinearMap::new::(); + assert m.find_or_insert_with(1, |_| 2) == &2; + assert m.find_or_insert_with(1, |_| 3) == &2; + } + #[test] pub fn test_consume() { let mut m = LinearMap::new();