-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathStableHashMap.mo
157 lines (141 loc) · 4.4 KB
/
StableHashMap.mo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import Prim "mo:⛔";
import P "mo:base/Prelude";
import A "mo:base/Array";
import Hash "mo:base/Hash";
import Iter "mo:base/Iter";
import AssocList "mo:base/AssocList";
module {
// key-val list type
type KVs<K, V> = AssocList.AssocList<K, V>;
public type HashMap<K, V> = {
var table : [var KVs<K, V>];
var _count : Nat;
};
public func size<K, V>(hm: HashMap<K, V>) : Nat = hm._count;
public func defaults<K, V>() : HashMap<K, V> {
return {
var table : [var KVs<K, V>] = [var];
var _count : Nat = 0;
};
};
public func delete<K, V>(
hm: HashMap<K, V>,
k: K,
keyHash: K -> Hash.Hash,
keyEq : (K, K) -> Bool) : HashMap<K, V> = remove(hm, k, keyHash, keyEq).1;
public func remove<K, V>(
hm: HashMap<K, V>,
k: K,
keyHash: K -> Hash.Hash,
keyEq : (K, K) -> Bool) : (?V, HashMap<K, V>) {
let m = hm.table.size();
if (m > 0) {
let h = Prim.nat32ToNat(keyHash(k));
let pos = h % m;
let (kvs2, ov) = AssocList.replace<K, V>(hm.table[pos], k, keyEq, null);
hm.table[pos] := kvs2;
switch(ov){
case null { };
case _ { hm._count -= 1; }
};
(ov, hm)
} else {
(null, hm)
};
};
public func get<K, V>(
hm: HashMap<K, V>,
k : K,
keyHash: K -> Hash.Hash,
keyEq : (K, K) -> Bool) : ?V {
let h = Prim.nat32ToNat(keyHash(k));
let m = hm.table.size();
let v = if (m > 0) {
AssocList.find<K, V>(hm.table[h % m], k, keyEq)
} else {
null
};
};
public func put<K, V>(
hm: HashMap<K, V>,
k : K,
v : V,
keyHash: K -> Hash.Hash,
keyEq : (K, K) -> Bool) : HashMap<K, V> = replace(hm, k, v, keyHash, keyEq).1;
/// Insert the value `v` at key `k` and returns the previous value stored at
/// `k` or `null` if it didn't exist.
public func replace<K, V>(
hm: HashMap<K, V>,
k : K,
v : V,
keyHash: K -> Hash.Hash,
keyEq : (K, K) -> Bool) : (?V, HashMap<K, V>) {
if (hm._count >= hm.table.size()) {
let size =
if (hm._count == 0) {
1
} else {
hm.table.size() * 2;
};
let table2 = A.init<KVs<K, V>>(size, null);
for (i in hm.table.keys()) {
var kvs = hm.table[i];
label moveKeyVals : ()
loop {
switch kvs {
case null { break moveKeyVals };
case (?((k, v), kvsTail)) {
let h = Prim.nat32ToNat(keyHash(k));
let pos2 = h % table2.size();
table2[pos2] := ?((k,v), table2[pos2]);
kvs := kvsTail;
};
}
};
};
hm.table := table2;
};
let h = Prim.nat32ToNat(keyHash(k));
let pos = h % hm.table.size();
let (kvs2, ov) = AssocList.replace<K, V>(hm.table[pos], k, keyEq, ?v);
hm.table[pos] := kvs2;
switch(ov){
case null { hm._count += 1 };
case _ {}
};
(ov, hm)
};
/// Returns an iterator over the key value pairs in this
/// `HashMap`. Does _not_ modify the `HashMap`.
public func entries<K,V>(
hm: HashMap<K, V>,
) : Iter.Iter<(K, V)> {
var table = hm.table;
if (table.size() == 0) {
object { public func next() : ?(K, V) { null } }
}
else {
object {
var kvs = table[0];
var nextTablePos = 1;
public func next () : ?(K, V) {
switch kvs {
case (?(kv, kvs2)) {
kvs := kvs2;
?kv
};
case null {
if (nextTablePos < table.size()) {
kvs := table[nextTablePos];
nextTablePos += 1;
next()
} else {
null
}
}
}
}
}
}
};
};