-
Notifications
You must be signed in to change notification settings - Fork 0
/
hashmap.t
318 lines (269 loc) · 6.58 KB
/
hashmap.t
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
316
local m = require("mem")
local templatize = require("templatize")
local util = require("util")
local hash = require("hash")
local cstdlib = terralib.includec("stdlib.h")
local cstdio = terralib.includec("stdio.h")
local defaultInitialCapacity = 8
local expandFactor = 2
local loadFactor = 2.0
local HM = templatize(function(K, V)
local hashfn = hash.gethashfn(K)
local struct HashCell
{
key: K,
val: V,
next: &HashCell
}
terra HashCell:__construct(k: K, v: V)
self.key = m.copy(k)
self.val = m.copy(v)
self.next = nil
end
terra HashCell:__construct(k: K)
self.key = m.copy(k)
m.init(self.val)
self.next = nil
end
terra HashCell:__destruct() : {}
m.destruct(self.key)
m.destruct(self.val)
if self.next ~= nil then
m.delete(self.next)
end
end
m.addConstructors(HashCell)
-----
local struct HashMap
{
__cells: &&HashCell,
__capacity: uint,
size: uint
}
terra HashMap:__construct(initialCapacity: uint) : {}
self.__capacity = initialCapacity
self.__cells = [&&HashCell](cstdlib.malloc(initialCapacity*sizeof([&HashCell])))
for i=0,self.__capacity do
self.__cells[i] = nil
end
self.size = 0
end
terra HashMap:__construct() : {}
self:__construct(defaultInitialCapacity)
end
terra HashMap:clear()
for i=0,self.__capacity do
if self.__cells[i] ~= nil then
m.delete(self.__cells[i])
self.__cells[i] = nil
end
end
self.size = 0
end
terra HashMap:__destruct()
self:clear()
cstdlib.free(self.__cells)
end
terra HashMap:hash(key: K)
return hashfn(key) % self.__capacity
end
util.inline(HashMap.methods.hash)
terra HashMap:getPointer(key: K)
var cell = self.__cells[self:hash(key)]
while cell ~= nil do
if cell.key == key then
return &cell.val
end
cell = cell.next
end
return nil
end
local C = terralib.includec("stdio.h")
-- Searches for a value matching 'key', and
-- will create an entry if it doesn't find one.
-- (Similar to the std::hash_map's [] operator)
-- NOTE: This will attempt initialize the new entry's
-- value field by calling a no-argument constructor,
-- which will be a compile-time error if no such
-- constructor exists.
terra HashMap:getOrCreatePointer(key: K)
var index = self:hash(key)
var cell = self.__cells[index]
while cell ~= nil do
if cell.key == key then
return &cell.val, true
end
cell = cell.next
end
-- Didn't find it; need to create
-- (insert at head of list)
cell = HashCell.heapAlloc(key)
cell.next = self.__cells[index]
self.__cells[index] = cell
self.size = self.size + 1
self:__checkExpand()
return &cell.val, false
end
terra HashMap:get(key: K, outval: &V)
var vptr = self:getPointer(key)
if vptr == nil then
return false
else
@outval = m.copy(@vptr)
return true
end
end
-- Dies if the key does not exist
-- Does not copy the return value
HashMap.metamethods.__apply = terra(self: &HashMap, key: K)
var vptr = self:getPointer(key)
if vptr == nil then
util.fatalError("HashMap:__apply - key does not exist!\n")
else
return @vptr
end
end
-- Expand and rehash
terra HashMap:__expand()
var oldcap = self.__capacity
var oldcells = self.__cells
var oldsize = self.size
self:__construct(2*oldcap)
self.size = oldsize
for i=0,oldcap do
var cell = oldcells[i]
while cell ~= nil do
var index = self:hash(cell.key)
var nextCellToProcess = cell.next
cell.next = self.__cells[index]
self.__cells[index] = cell
cell = nextCellToProcess
end
end
cstdlib.free(oldcells)
end
terra HashMap:__checkExpand()
if [float](self.size)/self.__capacity > loadFactor then
self:__expand()
end
end
util.inline(HashMap.methods.__checkExpand)
terra HashMap:put(key: K, val: V) : {}
var index = self:hash(key)
var cell = self.__cells[index]
if cell == nil then
cell = HashCell.heapAlloc(key, val)
self.__cells[index] = cell
else
-- Check if this key is already present, and if so, replace
-- its value
var origcell = cell
while cell ~= nil do
if cell.key == key then
m.destruct(cell.val)
cell.val = m.copy(val)
return
end
cell = cell.next
end
cell = origcell
-- Otherwise, insert new cell at head of linked list
var newcell = HashCell.heapAlloc(key, val)
newcell.next = cell
self.__cells[index] = newcell
end
self.size = self.size + 1
self:__checkExpand()
end
terra HashMap:remove(key: K)
var index = self:hash(key)
var cell = self.__cells[index]
var prevcell : &HashCell = nil
while cell ~= nil do
if cell.key == key then
-- CASE: Found it in the first cell
if prevcell == nil then
self.__cells[index] = cell.next
return
-- CASE: Found it in a cell further along
else
prevcell.next = cell.next
end
self.size = self.size - 1
m.delete(cell)
return
end
prevcell = cell
cell = cell.next
end
end
m.addConstructors(HashMap)
-----
local struct Iterator
{
srcmap: &HashMap,
currblock: uint,
currcell: &HashCell
}
terra Iterator:__construct(map: &HashMap)
self.srcmap = map
self.currblock = 0
self.currcell = map.__cells[0]
if self.currcell == nil then
self:next()
end
end
terra Iterator:next()
-- First, just try to move to the next cell in the
-- current chain
if self.currcell ~= nil then
self.currcell = self.currcell.next
end
-- Look for a non-nil cell
-- Traverse the current cell chain until it ends, then
-- move to the enxt one
-- Stop if we get to the end of the last chain
while self.currcell == nil and
(self.currblock < self.srcmap.__capacity-1) do
self.currblock = self.currblock + 1
self.currcell = self.srcmap.__cells[self.currblock]
end
end
terra Iterator:done()
return self.currcell == nil
end
util.inline(Iterator.methods.done)
terra Iterator:key()
return m.copy(self.currcell.key)
end
util.inline(Iterator.methods.key)
-- Do not write to the value at this pointer; doing so
-- will break the hash map.
terra Iterator:keyPointer()
return &(self.currcell.key)
end
util.inline(Iterator.methods.keyPointer)
terra Iterator:val()
return m.copy(self.currcell.val)
end
util.inline(Iterator.methods.val)
terra Iterator:valPointer()
return &(self.currcell.val)
end
util.inline(Iterator.methods.valPointer)
terra Iterator:keyval()
return self:key(), self:val()
end
util.inline(Iterator.methods.keyval)
terra Iterator:keyvalPointer()
return self:keyPointer(), self:valPointer()
end
util.inline(Iterator.methods.keyvalPointer)
m.addConstructors(Iterator)
-----
terra HashMap:iterator()
return Iterator.stackAlloc(self)
end
return HashMap
end)
return HM