Skip to content

Commit fb68286

Browse files
committed
Add incomplete hashmap implementation to stdlib.
1 parent c906759 commit fb68286

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

src/lib/map.rs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/**
2+
* At the moment, this is a partial hashmap implementation, not yet fit for
3+
* use, but useful as a stress test for rustboot.
4+
*/
5+
6+
import std._int;
7+
import std.sys;
8+
import std.util;
9+
import std._vec;
10+
11+
12+
type hashfn[K] = fn(K) -> uint;
13+
type eqfn[K] = fn(K) -> bool;
14+
15+
type hashmap[K, V] = obj {
16+
fn insert(&K key, &V val);
17+
fn contains_key(&K key) -> bool;
18+
fn get(&K key) -> V;
19+
fn find(&K key) -> util.option[V];
20+
fn remove(&K key) -> util.option[V];
21+
fn rehash();
22+
};
23+
24+
fn mk_hashmap[K, V](&hashfn[K] hasher, &eqfn[K] eqer) -> hashmap[K, V] {
25+
26+
let uint initial_capacity = uint(32); // 2^5
27+
let util.rational load_factor = rec(num=3, den=4);
28+
29+
type bucket[V] = tag(nil(), deleted(), some(V));
30+
31+
// Derive two hash functions from the one given by taking the upper
32+
// half and lower half of the uint bits. Our bucket probing
33+
// sequence is then defined by
34+
//
35+
// hash(key, i) := hashl(key) + i * hashr(key) for i = 0, 1, 2, ...
36+
//
37+
// Tearing the hash function apart this way is kosher in practice
38+
// as, assuming 32-bit uints, the table would have to be at 2^32
39+
// buckets before the resulting pair of hash functions no longer
40+
// probes all buckets for a fixed key. Note that hashr is made to
41+
// output odd numbers (hence coprime to the number of nbkts, which
42+
// is always a power of 2), so that all buckets are probed for a
43+
// fixed key.
44+
45+
fn hashl[K](hashfn[K] hasher, uint nbkts, &K key) -> uint {
46+
ret (hasher(key) >>> (sys.rustrt.size_of[uint]() * uint(8) / uint(2)))
47+
% nbkts;
48+
}
49+
50+
fn hashr[K](hashfn[K] hasher, uint nbkts, &K key) -> uint {
51+
ret ((((~ uint(0)) >>> (sys.rustrt.size_of[uint]() * uint(8) / uint(2)))
52+
& hasher(key)) * uint(2) + uint(1))
53+
% nbkts;
54+
}
55+
56+
fn hash[K](hashfn[K] hasher, uint nbkts, &K key, uint i) -> uint {
57+
ret hashl[K](hasher, nbkts, key) + i * hashr[K](hasher, nbkts, key);
58+
}
59+
60+
fn find_common[K, V](hashfn[K] hasher,
61+
vec[mutable bucket[V]] bkts,
62+
uint nbkts,
63+
&K key)
64+
-> util.option[V]
65+
{
66+
let uint i = uint(0);
67+
while (i < nbkts) {
68+
// Pending fix to issue #94, remove uint coercion.
69+
let int j = int(hash[K](hasher, nbkts, key, i));
70+
alt (bkts.(j)) {
71+
case (some[V](val)) {
72+
ret util.some[V](val);
73+
}
74+
case (nil[V]()) {
75+
ret util.none[V]();
76+
}
77+
case (deleted[V]()) {
78+
i += uint(1);
79+
}
80+
}
81+
}
82+
ret util.none[V]();
83+
}
84+
85+
obj hashmap[K, V](hashfn[K] hasher,
86+
eqfn[K] eqer,
87+
mutable vec[mutable bucket[V]] bkts,
88+
mutable uint nbkts,
89+
mutable uint nelts,
90+
util.rational lf)
91+
{
92+
fn insert(&K key, &V val) {
93+
// FIXME grow the table and rehash if we ought to.
94+
let uint i = uint(0);
95+
while (i < nbkts) {
96+
// Issue #94, as in find_common()
97+
let int j = int(hash[K](hasher, nbkts, key, i));
98+
alt (bkts.(j)) {
99+
case (some[V](_)) {
100+
i += uint(1);
101+
}
102+
case (_) {
103+
bkts.(j) = some[V](val);
104+
nelts += uint(1);
105+
ret;
106+
}
107+
}
108+
}
109+
// full table, impossible unless growth is broken. remove after testing.
110+
fail;
111+
}
112+
113+
fn contains_key(&K key) -> bool {
114+
alt (find_common[K, V](hasher, bkts, nbkts, key)) {
115+
case (util.some[V](_)) { ret true; }
116+
case (_) { ret false; }
117+
}
118+
}
119+
120+
fn get(&K key) -> V {
121+
alt (find_common[K, V](hasher, bkts, nbkts, key)) {
122+
case (util.some[V](val)) { ret val; }
123+
case (_) { fail; }
124+
}
125+
}
126+
127+
fn find(&K key) -> util.option[V] {
128+
be find_common[K, V](hasher, bkts, nbkts, key);
129+
}
130+
131+
fn remove(&K key) -> util.option[V] {
132+
let uint i = uint(0);
133+
while (i < nbkts) {
134+
// Issue #94, as in find_common()
135+
let int j = int(hash[K](hasher, nbkts, key, i));
136+
alt (bkts.(j)) {
137+
case (some[V](val)) {
138+
bkts.(j) = deleted[V]();
139+
ret util.some[V](val);
140+
}
141+
case (deleted[V]()) {
142+
nelts += uint(1);
143+
}
144+
case (nil[V]()) {
145+
ret util.none[V]();
146+
}
147+
}
148+
}
149+
ret util.none[V]();
150+
}
151+
152+
fn rehash() {}
153+
}
154+
155+
let vec[mutable bucket[V]] bkts =
156+
_vec.init_elt[mutable bucket[V]](nil[V](),
157+
uint(initial_capacity));
158+
159+
ret hashmap[K, V](hasher, eqer, bkts, uint(0), uint(0), load_factor);
160+
}

src/lib/std.rc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ auth _io = unsafe;
2626
auth _str = unsafe;
2727
auth _vec = unsafe;
2828

29+
/**
30+
* FIXME for some reason 'auth sys = unsafe' isn't enough here to silence
31+
* the effect system about map.mk_hashmap.hashl and .hashr using
32+
* sys.rustrt.size_of and thereby being unsafe.
33+
*/
34+
auth map.mk_hashmap = unsafe;
35+
2936
// Target-OS module.
3037

3138
alt (target_os) {
@@ -37,3 +44,5 @@ alt (target_os) {
3744
mod os = "linux_os.rs";
3845
}
3946
}
47+
48+
mod map;

0 commit comments

Comments
 (0)