rust/src/libstd/map.rs

341 lines
9.2 KiB
Rust
Raw Normal View History

2011-10-26 13:28:23 -05:00
/*
Module: map
A hashmap
*/
/* Section: Types */
/*
Type: hashfn
A function that returns a hash of a value
*/
type hashfn<K> = fn(K) -> uint;
2011-10-26 13:28:23 -05:00
/*
Type: eqfn
Equality
*/
type eqfn<K> = fn(K, K) -> bool;
2011-10-26 13:28:23 -05:00
/*
Type: hashset
A convenience type to treat a hashmap as a set
*/
type hashset<K> = hashmap<K, ()>;
2011-10-26 13:28:23 -05:00
/*
Obj: hashmap
*/
type hashmap<K, V> = obj {
/*
Method: size
Return the number of elements in the map
*/
fn size() -> uint;
/*
Method: insert
Add a value to the map. If the map already contains a value for
the specified key then the original value is replaced.
Returns:
True if the key did not already exist in the map
*/
fn insert(K, V) -> bool;
/*
Method: contains_key
Returns true if the map contains a value for the specified key
*/
fn contains_key(K) -> bool;
/*
Method: get
Get the value for the specified key
Failure:
If the key does not exist in the map
*/
fn get(K) -> V;
/*
Method: find
2011-10-26 13:28:23 -05:00
Get the value for the specified key. If the key does not exist
in the map then returns none.
*/
fn find(K) -> option::t<V>;
/*
Method: remove
Remove and return a value from the map. If the key does not exist
in the map then returns none.
*/
fn remove(K) -> option::t<V>;
/*
Method: rehash
Force map growth and rehashing
*/
fn rehash();
/*
Method: items
Iterate over all the key/value pairs in the map
*/
fn items(block(K, V));
/*
Method: keys
Iterate over all the keys in the map
*/
fn keys(block(K));
/*
Iterate over all the values in the map
*/
fn values(block(V));
};
/* Section: Operations */
/*
Function: mk_hashmap
Construct a hashmap
Parameters:
hasher - The hash function for key type K
eqer - The equality function for key type K
*/
fn mk_hashmap<copy K, copy V>(hasher: hashfn<K>, eqer: eqfn<K>)
-> hashmap<K, V> {
2011-07-27 07:19:39 -05:00
let initial_capacity: uint = 32u; // 2^5
2011-07-27 07:19:39 -05:00
let load_factor: util::rational = {num: 3, den: 4};
tag bucket<copy K, copy V> { nil; deleted; some(K, V); }
fn make_buckets<copy K, copy V>(nbkts: uint) -> [mutable bucket<K, V>] {
ret vec::init_elt_mut::<bucket<K, V>>(nil::<K, V>, nbkts);
}
// Derive two hash functions from the one given by taking the upper
// half and lower half of the uint bits. Our bucket probing
// sequence is then defined by
//
// hash(key, i) := hashl(key) * i + hashr(key) for i = 0, 1, 2, ...
//
// Tearing the hash function apart this way is kosher in practice
// as, assuming 32-bit uints, the table would have to be at 2^32
// buckets before the resulting pair of hash functions no longer
// probes all buckets for a fixed key. Note that hashl is made to
// output odd numbers (hence coprime to the number of nbkts, which
// is always a power of 2), so that all buckets are probed for a
// fixed key.
fn hashl(n: u32) -> u32 { ret (n >>> 16u32) * 2u32 + 1u32; }
fn hashr(n: u32) -> u32 { ret 0x0000_ffff_u32 & n; }
fn hash(h: u32, nbkts: uint, i: uint) -> uint {
ret ((hashl(h) as uint) * i + (hashr(h) as uint)) % nbkts;
}
fn to_u64(h: uint) -> u32 {
ret (h as u32) ^ ((h >>> 16u) as u32);
}
/**
* We attempt to never call this with a full table. If we do, it
* will fail.
*/
fn insert_common<copy K, copy V>(hasher: hashfn<K>, eqer: eqfn<K>,
bkts: [mutable bucket<K, V>],
nbkts: uint, key: K, val: V) -> bool {
2011-07-27 07:19:39 -05:00
let i: uint = 0u;
let h = to_u64(hasher(key));
2011-07-27 07:19:39 -05:00
while i < nbkts {
let j: uint = hash(h, nbkts, i);
alt bkts[j] {
2011-07-27 07:19:39 -05:00
some(k, _) {
// Copy key to please alias analysis.
2011-07-27 07:19:39 -05:00
let k_ = k;
if eqer(key, k_) { bkts[j] = some(k_, val); ret false; }
2011-07-27 07:19:39 -05:00
i += 1u;
}
_ { bkts[j] = some(key, val); ret true; }
}
}
fail; // full table
}
fn find_common<copy K, copy V>(hasher: hashfn<K>, eqer: eqfn<K>,
bkts: [mutable bucket<K, V>],
nbkts: uint, key: K) -> option::t<V> {
2011-07-27 07:19:39 -05:00
let i: uint = 0u;
let h = to_u64(hasher(key));
2011-07-27 07:19:39 -05:00
while i < nbkts {
let j: uint = hash(h, nbkts, i);
alt bkts[j] {
2011-07-27 07:19:39 -05:00
some(k, v) {
// Copy to please alias analysis.
let k_ = k;
let v_ = v;
2011-08-09 19:36:07 -05:00
if eqer(key, k_) { ret option::some(v_); }
2011-07-27 07:19:39 -05:00
}
2011-08-09 19:36:07 -05:00
nil. { ret option::none; }
deleted. { }
}
i += 1u;
}
2011-08-09 19:36:07 -05:00
ret option::none;
}
fn rehash<copy K, copy V>(hasher: hashfn<K>, eqer: eqfn<K>,
oldbkts: [mutable bucket<K, V>],
_noldbkts: uint,
newbkts: [mutable bucket<K, V>],
nnewbkts: uint) {
for b: bucket<K, V> in oldbkts {
2011-07-27 07:19:39 -05:00
alt b {
some(k_, v_) {
let k = k_;
let v = v_;
2011-08-09 19:36:07 -05:00
insert_common(hasher, eqer, newbkts, nnewbkts, k, v);
2011-07-27 07:19:39 -05:00
}
_ { }
}
}
}
obj hashmap<copy K, copy V>(hasher: hashfn<K>,
eqer: eqfn<K>,
mutable bkts: [mutable bucket<K, V>],
mutable nbkts: uint,
mutable nelts: uint,
lf: util::rational) {
fn size() -> uint { ret nelts; }
fn insert(key: K, val: V) -> bool {
2011-07-27 07:19:39 -05:00
let load: util::rational =
{num: nelts + 1u as int, den: nbkts as int};
if !util::rational_leq(load, lf) {
let nnewbkts: uint = uint::next_power_of_two(nbkts + 1u);
2011-08-09 19:36:07 -05:00
let newbkts = make_buckets(nnewbkts);
rehash(hasher, eqer, bkts, nbkts, newbkts, nnewbkts);
bkts = newbkts;
nbkts = nnewbkts;
}
2011-08-09 19:36:07 -05:00
if insert_common(hasher, eqer, bkts, nbkts, key, val) {
nelts += 1u;
ret true;
}
ret false;
}
fn contains_key(key: K) -> bool {
2011-08-09 19:36:07 -05:00
ret alt find_common(hasher, eqer, bkts, nbkts, key) {
2011-07-27 07:19:39 -05:00
option::some(_) { true }
_ { false }
};
}
fn get(key: K) -> V {
2011-08-09 19:36:07 -05:00
ret alt find_common(hasher, eqer, bkts, nbkts, key) {
2011-07-27 07:19:39 -05:00
option::some(val) { val }
_ { fail }
};
}
fn find(key: K) -> option::t<V> {
2011-08-09 19:36:07 -05:00
be find_common(hasher, eqer, bkts, nbkts, key);
}
fn remove(key: K) -> option::t<V> {
2011-07-27 07:19:39 -05:00
let i: uint = 0u;
let h = to_u64(hasher(key));
2011-07-27 07:19:39 -05:00
while i < nbkts {
let j: uint = hash(h, nbkts, i);
alt bkts[j] {
2011-07-27 07:19:39 -05:00
some(k, v) {
let k_ = k;
let vo = option::some(v);
if eqer(key, k_) {
bkts[j] = deleted;
2011-07-27 07:19:39 -05:00
nelts -= 1u;
ret vo;
}
2011-07-27 07:19:39 -05:00
}
deleted. { }
2011-08-09 19:36:07 -05:00
nil. { ret option::none; }
}
i += 1u;
}
2011-08-09 19:36:07 -05:00
ret option::none;
}
fn rehash() {
2011-08-09 19:36:07 -05:00
let newbkts = make_buckets(nbkts);
rehash(hasher, eqer, bkts, nbkts, newbkts, nbkts);
bkts = newbkts;
}
fn items(it: block(K, V)) {
for b in bkts {
alt b { some(k, v) { it(copy k, copy v); } _ { } }
2010-09-22 19:04:08 -05:00
}
}
fn keys(it: block(K)) {
for b in bkts {
alt b { some(k, _) { it(copy k); } _ { } }
}
}
fn values(it: block(V)) {
for b in bkts {
alt b { some(_, v) { it(copy v); } _ { } }
}
}
}
2011-08-09 19:36:07 -05:00
let bkts = make_buckets(initial_capacity);
ret hashmap(hasher, eqer, bkts, initial_capacity, 0u, load_factor);
}
2011-10-26 13:28:23 -05:00
/*
Function: new_str_hash
2011-10-26 13:28:23 -05:00
Construct a hashmap for string keys
*/
fn new_str_hash<copy V>() -> hashmap<str, V> {
ret mk_hashmap(str::hash, str::eq);
}
2011-10-26 13:28:23 -05:00
/*
Function: new_int_hash
Construct a hashmap for int keys
*/
fn new_int_hash<copy V>() -> hashmap<int, V> {
fn hash_int(&&x: int) -> uint { ret x as uint; }
fn eq_int(&&a: int, &&b: int) -> bool { ret a == b; }
2011-08-09 19:36:07 -05:00
ret mk_hashmap(hash_int, eq_int);
}
2011-10-26 13:28:23 -05:00
/*
Function: new_uint_hash
Construct a hashmap for uint keys
*/
fn new_uint_hash<copy V>() -> hashmap<uint, V> {
fn hash_uint(&&x: uint) -> uint { ret x; }
fn eq_uint(&&a: uint, &&b: uint) -> bool { ret a == b; }
2011-08-09 19:36:07 -05:00
ret mk_hashmap(hash_uint, eq_uint);
}
2011-10-26 13:28:23 -05:00
/*
Function: set_add
Convenience function for adding keys to a hashmap with nil type keys
*/
fn set_add<K>(set: hashset<K>, key: K) -> bool { ret set.insert(key, ()); }
// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End: