Skip to content

Commit

Permalink
Add pop() and swap() to the Map trait
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcrichton committed May 7, 2013
1 parent 4b6864f commit 393a409
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 83 deletions.
8 changes: 8 additions & 0 deletions src/libcore/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ pub trait Map<K, V>: Mutable {
/// Remove a key-value pair from the map. Return true if the key
/// was present in the map, otherwise false.
fn remove(&mut self, key: &K) -> bool;

/// Insert a key-value pair from the map. If the key already had a value
/// present in the map, that value is returned. Otherwise None is returned.
fn swap(&mut self, k: K, v: V) -> Option<V>;

/// Removes a key from the map, returning the value at the key if the key
/// was previously in the map.
fn pop(&mut self, k: &K) -> Option<V>;
}

pub trait Set<T>: Mutable {
Expand Down
65 changes: 30 additions & 35 deletions src/libcore/hashmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ use rand::RngUtil;
use rand;
use uint;
use vec;
use util::unreachable;
use kinds::Copy;
use util::{replace, unreachable};

static INITIAL_CAPACITY: uint = 32u; // 2^5

Expand Down Expand Up @@ -204,7 +204,7 @@ priv impl<K:Hash + Eq,V> HashMap<K, V> {
/// 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
fn insert_internal(&mut self, hash: uint, k: K, v: V) -> bool {
fn insert_internal(&mut self, hash: uint, k: K, v: V) -> Option<V> {
match self.bucket_for_key_with_hash(hash, &k) {
TableFull => { fail!(~"Internal logic error"); }
FoundHole(idx) => {
Expand All @@ -213,14 +213,19 @@ priv impl<K:Hash + Eq,V> HashMap<K, V> {
self.buckets[idx] = Some(Bucket{hash: hash, key: k,
value: v});
self.size += 1;
true
None
}
FoundEntry(idx) => {
debug!("insert overwrite (%?->%?) at idx %?, hash %?",
k, v, idx, hash);
self.buckets[idx] = Some(Bucket{hash: hash, key: k,
value: v});
false
match self.buckets[idx] {
None => { fail!(~"insert_internal: Internal logic error") }
Some(ref mut b) => {
b.hash = hash;
b.key = k;
Some(replace(&mut b.value, v))
}
}
}
}
}
Expand Down Expand Up @@ -361,6 +366,20 @@ impl<K:Hash + Eq,V> Map<K, V> for HashMap<K, V> {
/// key is replaced by the new value. Return true if the key did
/// not already exist in the map.
fn insert(&mut self, k: K, v: V) -> bool {
self.swap(k, v).is_none()
}

/// Remove a key-value pair from the map. Return true if the key
/// was present in the map, otherwise false.
fn remove(&mut self, k: &K) -> bool {
self.pop(k).is_some()
}

/// Insert a key-value pair from the map. If the key already had a value
/// present in the map, that value is returned. Otherwise None is returned.
fn swap(&mut self, k: K, v: V) -> Option<V> {
// this could be faster.

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
Expand All @@ -375,10 +394,11 @@ impl<K:Hash + Eq,V> Map<K, V> for HashMap<K, V> {
self.insert_internal(hash, k, v)
}

/// Remove a key-value pair from the map. Return true if the key
/// was present in the map, otherwise false.
fn remove(&mut self, k: &K) -> bool {
self.pop(k).is_some()
/// Removes a key from the map, returning the value at the key if the key
/// was previously in the map.
fn pop(&mut self, k: &K) -> Option<V> {
let hash = k.hash_keyed(self.k0, self.k1) as uint;
self.pop_internal(hash, k)
}
}

Expand All @@ -402,31 +422,6 @@ pub impl<K: Hash + Eq, V> HashMap<K, V> {
}
}

fn pop(&mut self, k: &K) -> Option<V> {
let hash = k.hash_keyed(self.k0, self.k1) as uint;
self.pop_internal(hash, k)
}

fn swap(&mut self, k: K, v: V) -> Option<V> {
// this could be faster.
let hash = k.hash_keyed(self.k0, self.k1) as uint;
let old_value = self.pop_internal(hash, &k);

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();
}

self.insert_internal(hash, k, v);

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<'a>(&'a mut self, k: K, v: V) -> &'a V {
Expand Down
75 changes: 54 additions & 21 deletions src/libcore/trie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//! An ordered map and set for integer keys implemented as a radix trie

use prelude::*;
use util::{swap, replace};

// FIXME: #5244: need to manually update the TrieNode constructor
static SHIFT: uint = 4;
Expand Down Expand Up @@ -110,21 +111,33 @@ impl<T> Map<uint, T> for TrieMap<T> {
/// not already exist in the map.
#[inline(always)]
fn insert(&mut self, key: uint, value: T) -> bool {
let ret = insert(&mut self.root.count,
&mut self.root.children[chunk(key, 0)],
key, value, 1);
if ret { self.length += 1 }
ret
self.swap(key, value).is_none()
}

/// Remove a key-value pair from the map. Return true if the key
/// was present in the map, otherwise false.
#[inline(always)]
fn remove(&mut self, key: &uint) -> bool {
self.pop(key).is_some()
}

/// Insert a key-value pair from the map. If the key already had a value
/// present in the map, that value is returned. Otherwise None is returned.
fn swap(&mut self, key: uint, value: T) -> Option<T> {
let ret = insert(&mut self.root.count,
&mut self.root.children[chunk(key, 0)],
key, value, 1);
if ret.is_none() { self.length += 1 }
ret
}

/// Removes a key from the map, returning the value at the key if the key
/// was previously in the map.
fn pop(&mut self, key: &uint) -> Option<T> {
let ret = remove(&mut self.root.count,
&mut self.root.children[chunk(*key, 0)],
*key, 1);
if ret { self.length -= 1 }
if ret.is_some() { self.length -= 1 }
ret
}
}
Expand Down Expand Up @@ -289,61 +302,65 @@ fn find_mut<'r, T>(child: &'r mut Child<T>, key: uint, idx: uint)
}

fn insert<T>(count: &mut uint, child: &mut Child<T>, key: uint, value: T,
idx: uint) -> bool {
idx: uint) -> Option<T> {
let mut tmp = Nothing;
tmp <-> *child;
let mut added = false;
let ret;
swap(&mut tmp, child);

*child = match tmp {
External(stored_key, stored_value) => {
if stored_key == key {
ret = Some(stored_value);
External(stored_key, value)
} else {
// conflict - split the node
let mut new = ~TrieNode::new();
insert(&mut new.count,
&mut new.children[chunk(stored_key, idx)],
stored_key, stored_value, idx + 1);
insert(&mut new.count, &mut new.children[chunk(key, idx)], key,
value, idx + 1);
added = true;
ret = insert(&mut new.count, &mut new.children[chunk(key, idx)],
key, value, idx + 1);
Internal(new)
}
}
Internal(x) => {
let mut x = x;
added = insert(&mut x.count, &mut x.children[chunk(key, idx)], key,
value, idx + 1);
ret = insert(&mut x.count, &mut x.children[chunk(key, idx)], key,
value, idx + 1);
Internal(x)
}
Nothing => {
*count += 1;
added = true;
ret = None;
External(key, value)
}
};
added
return ret;
}

fn remove<T>(count: &mut uint, child: &mut Child<T>, key: uint,
idx: uint) -> bool {
idx: uint) -> Option<T> {
let (ret, this) = match *child {
External(stored, _) => {
if stored == key { (true, true) } else { (false, false) }
External(stored, _) if stored == key => {
match replace(child, Nothing) {
External(_, value) => (Some(value), true),
_ => fail!()
}
}
External(*) => (None, false),
Internal(ref mut x) => {
let ret = remove(&mut x.count, &mut x.children[chunk(key, idx)],
key, idx + 1);
(ret, x.count == 0)
}
Nothing => (false, false)
Nothing => (None, false)
};

if this {
*child = Nothing;
*count -= 1;
}
ret
return ret;
}

#[cfg(test)]
Expand Down Expand Up @@ -516,4 +533,20 @@ mod tests {
i += 1;
}
}

#[test]
fn test_swap() {
let mut m = TrieMap::new();
assert!(m.swap(1, 2) == None);
assert!(m.swap(1, 3) == Some(2));
assert!(m.swap(1, 4) == Some(3));
}

#[test]
fn test_pop() {
let mut m = TrieMap::new();
m.insert(1, 2);
assert!(m.pop(&1) == Some(2));
assert!(m.pop(&1) == None);
}
}
40 changes: 36 additions & 4 deletions src/libstd/smallintmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use core::container::{Container, Mutable, Map, Set};
use core::old_iter::{BaseIter};
use core::option::{Some, None};
use core::util::replace;

pub struct SmallIntMap<T> {
priv v: ~[Option<T>],
Expand Down Expand Up @@ -119,12 +120,27 @@ impl<V> Map<uint, V> for SmallIntMap<V> {
/// Remove a key-value pair from the map. Return true if the key
/// was present in the map, otherwise false.
fn remove(&mut self, key: &uint) -> bool {
self.pop(key).is_some()
}

/// Insert a key-value pair from the map. If the key already had a value
/// present in the map, that value is returned. Otherwise None is returned.
fn swap(&mut self, key: uint, value: V) -> Option<V> {
match self.find_mut(&key) {
Some(loc) => { return Some(replace(loc, value)); }
None => ()
}
self.insert(key, value);
return None;
}

/// Removes a key from the map, returning the value at the key if the key
/// was previously in the map.
fn pop(&mut self, key: &uint) -> Option<V> {
if *key >= self.v.len() {
return false;
return None;
}
let removed = self.v[*key].is_some();
self.v[*key] = None;
removed
replace(&mut self.v[*key], None)
}
}

Expand Down Expand Up @@ -237,4 +253,20 @@ mod tests {
// sadly, no sevens were counted
assert!(map.find(&7).is_none());
}

#[test]
fn test_swap() {
let mut m = SmallIntMap::new();
assert!(m.swap(1, 2) == None);
assert!(m.swap(1, 3) == Some(2));
assert!(m.swap(1, 4) == Some(3));
}

#[test]
fn test_pop() {
let mut m = SmallIntMap::new();
m.insert(1, 2);
assert!(m.pop(&1) == Some(2));
assert!(m.pop(&1) == None);
}
}
Loading

1 comment on commit 393a409

@thestinger
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r+

Please sign in to comment.