-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdict.go
More file actions
315 lines (292 loc) · 5.65 KB
/
dict.go
File metadata and controls
315 lines (292 loc) · 5.65 KB
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
package main
import (
"errors"
"math"
"math/rand"
)
const (
INIT_SIZE int64 = 8
FORCE_RATIO int64 = 2
GROW_RATIO int64 = 2
DEFAULT_STEP int = 1
)
var (
EP_ERR = errors.New("expand error")
EX_ERR = errors.New("key exists error")
NK_ERR = errors.New("key doesn't exist error")
WO_ERR = errors.New("wrong type Operation against a key holding the wrong kind of value")
)
type Entry struct {
Key *Gobj
Val *Gobj
next *Entry
}
type htable struct {
table []*Entry
size int64
mask int64
used int64 // number of entry
}
type DictType struct {
HashFunc func(key *Gobj) int64
EqualFunc func(k1, k2 *Gobj) bool
}
type Dict struct {
DictType
hts [2]*htable
rehashidx int64
}
func DictCreate(dictType DictType) *Dict {
var dict Dict
dict.DictType = dictType
dict.rehashidx = -1
return &dict
}
func (dict *Dict) isRehashing() bool {
return dict.rehashidx != -1
}
func (dict *Dict) rehashStep() {
dict.rehash(DEFAULT_STEP)
}
func (dict *Dict) rehash(step int) {
for step > 0 {
if dict.hts[0].used == 0 {
// rehash is done
dict.hts[0] = dict.hts[1]
dict.hts[1] = nil
dict.rehashidx = -1
return
}
// find a no-null slot
for dict.hts[0].table[dict.rehashidx] == nil {
dict.rehashidx++
}
// migrate all keys in this slot
entry := dict.hts[0].table[dict.rehashidx]
for entry != nil {
ne := entry.next
idx := dict.HashFunc(entry.Key) & dict.hts[1].mask
entry.next = dict.hts[1].table[idx]
dict.hts[1].table[idx] = entry
dict.hts[0].used--
dict.hts[1].used++
entry = ne
}
dict.hts[0].table[dict.rehashidx] = nil
dict.rehashidx++
step--
}
}
func nextPower(size int64) int64 {
for i := INIT_SIZE; i < math.MaxInt64; i *= 2 {
if i >= size {
return i
}
}
return -1
}
func (dict *Dict) expand(size int64) error {
sz := nextPower(size)
if dict.isRehashing() || (dict.hts[0] != nil && dict.hts[0].size >= sz) {
return EP_ERR
}
var ht htable
ht.size = sz
ht.mask = sz - 1
ht.table = make([]*Entry, sz)
ht.used = 0
// check for init
if dict.hts[0] == nil {
dict.hts[0] = &ht
return nil
}
// start rehashing
dict.hts[1] = &ht
dict.rehashidx = 0
return nil
}
func (dict *Dict) expandIfNeeded() error {
if dict.isRehashing() {
return nil
}
if dict.hts[0] == nil {
// init htable
return dict.expand(INIT_SIZE)
}
if (dict.hts[0].used > dict.hts[0].size) && (dict.hts[0].used/dict.hts[0].size > FORCE_RATIO) {
// expand htable
return dict.expand(dict.hts[0].size * GROW_RATIO)
}
return nil
}
// return the index of a free slot, return -1 if the key is exists or err
func (dict *Dict) keyIndex(key *Gobj) int64 {
err := dict.expandIfNeeded()
if err != nil {
return -1
}
h := dict.HashFunc(key)
var idx int64
for i := 0; i <= 1; i++ {
idx = h & dict.hts[i].mask
e := dict.hts[i].table[idx]
for e != nil {
if dict.EqualFunc(e.Key, key) {
return -1
}
e = e.next
}
// if rehashing, check the second htable
if !dict.isRehashing() {
break
}
}
return idx
}
func (dict *Dict) AddRaw(key *Gobj) *Entry {
if dict.isRehashing() {
dict.rehashStep()
}
idx := dict.keyIndex(key)
if idx == -1 {
return nil
}
// add key & return entry
var ht *htable
if dict.isRehashing() {
ht = dict.hts[1]
} else {
ht = dict.hts[0]
}
// head insert
var e Entry
e.Key = key
key.IncrRefCount()
e.next = ht.table[idx]
ht.table[idx] = &e
ht.used++
return &e
}
// Add add a new key-val pair, return err if key exists
func (dict *Dict) Add(key, val *Gobj) error {
entry := dict.AddRaw(key)
if entry == nil {
return EX_ERR
}
entry.Val = val
val.IncrRefCount()
return nil
}
func (dict *Dict) Set(key, val *Gobj) {
if err := dict.Add(key, val); err == nil {
return
}
// the key exists, find the entry
entry := dict.Find(key)
entry.Val.DecrRefCount()
entry.Val = val
val.IncrRefCount()
}
func freeEntry(e *Entry) {
e.Key.DecrRefCount()
e.Val.DecrRefCount()
}
func (dict *Dict) Delete(key *Gobj) error {
if dict.hts[0] == nil {
return NK_ERR
}
if dict.isRehashing() {
dict.rehashStep()
}
// find key & delete * decr refcount
h := dict.HashFunc(key)
for i := 0; i <= 1; i++ {
idx := h & dict.hts[i].mask
e := dict.hts[i].table[idx]
var prev *Entry
for e != nil {
if dict.EqualFunc(e.Key, key) {
if prev == nil {
dict.hts[i].table[idx] = e.next
} else {
prev.next = e.next
}
freeEntry(e)
return nil
}
prev = e
e = e.next
}
if !dict.isRehashing() {
break
}
}
return NK_ERR
}
func (dict *Dict) Find(key *Gobj) *Entry {
if dict.hts[0] == nil {
return nil
}
if dict.isRehashing() {
dict.rehashStep()
}
// find key in both ht
h := dict.HashFunc(key)
for i := 0; i <= 1; i++ {
idx := h & dict.hts[i].mask
e := dict.hts[i].table[idx]
for e != nil {
if dict.EqualFunc(e.Key, key) {
return e
}
e = e.next
}
if !dict.isRehashing() {
break
}
}
return nil
}
func (dict *Dict) Get(key *Gobj) *Gobj {
entry := dict.Find(key)
if entry == nil {
return nil
}
return entry.Val
}
func (dict *Dict) RandomGet() *Entry {
if dict.hts[0] == nil {
return nil
}
t := 0
if dict.isRehashing() {
dict.rehashStep()
// simplify the logic, random get in th bigger table
if dict.hts[1] != nil && dict.hts[1].used > dict.hts[0].used {
t = 1
}
}
// random slot
idx := rand.Int63n(dict.hts[t].size)
cnt := 0
for dict.hts[t].table[idx] == nil && cnt < 1000 {
idx = rand.Int63n(dict.hts[t].size)
cnt++
}
if dict.hts[t].table[idx] == nil {
return nil
}
// random entry
var listLen int64
p := dict.hts[t].table[idx]
for p != nil {
listLen++
p = p.next
}
listIdx := rand.Int63n(listLen)
p = dict.hts[t].table[idx]
for i := int64(0); i < listIdx; i++ {
p = p.next
}
return p
}