1 #include "shotgun/lib/shotgun.h"
2 #include "shotgun/lib/tuple.h"
3 #include "shotgun/lib/hash.h"
4 #include "shotgun/lib/lookuptable.h"
6 /* MINSIZE MUST be a power of 2 */
11 #define find_bin(hash, total) (hash & (total - 1))
13 OBJECT
hash_new(STATE
) {
15 hsh
= hash_allocate(state
);
16 hash_setup(state
, hsh
, MINSIZE
);
20 /* size MUST be a power of 2 */
21 OBJECT
hash_new_sized(STATE
, int size
) {
23 hsh
= hash_allocate(state
);
24 hash_setup(state
, hsh
, size
);
28 void hash_setup(STATE
, OBJECT hsh
, int size
) {
30 sz
= size
== 0 ? MINSIZE
: size
;
31 // TODO: Remove @keys field
32 hash_set_keys(hsh
, Qnil
);
33 hash_set_values(hsh
, tuple_new(state
, sz
));
34 hash_set_bins(hsh
, I2N(sz
));
35 hash_set_entries(hsh
, I2N(0));
36 hash_set_default(hsh
, Qnil
);
39 OBJECT
hash_dup(STATE
, OBJECT hsh
) {
41 OBJECT dup
, cv
, vals
, ent
, next
, lst
, tmp
;
43 sz
= N2I(hash_get_bins(hsh
));
44 dup
= hash_new_sized(state
, sz
);
45 SET_CLASS(dup
, object_class(state
, hsh
));
47 hash_set_bins(dup
, I2N(sz
));
48 hash_set_entries(dup
, hash_get_entries(hsh
));
49 hash_set_default(dup
, hash_get_default(hsh
));
50 // TODO: Remove @keys field
51 hash_set_keys(dup
, Qnil
);
53 cv
= hash_get_values(hsh
);
55 vals
= tuple_new(state
, sz
);
56 hash_set_values(dup
, vals
);
58 for(i
= 0; i
< sz
; i
++) {
59 tmp
= tuple_at(state
, cv
, i
);
60 if(NIL_P(tmp
)) continue;
62 ent
= tuple_dup(state
, tmp
);
63 tuple_put(state
, vals
, i
, ent
);
65 next
= tuple_at(state
, ent
, 3);
68 next
= tuple_dup(state
, next
);
69 tuple_put(state
, lst
, 3, next
);
71 next
= tuple_at(state
, next
, 3);
78 static OBJECT
entry_new(STATE
, unsigned int hsh
, OBJECT key
, OBJECT data
) {
79 OBJECT tup
= tuple_new(state
, 4);
80 tuple_put(state
, tup
, 0, I2N(hsh
));
81 tuple_put(state
, tup
, 1, key
);
82 tuple_put(state
, tup
, 2, data
);
83 tuple_put(state
, tup
, 3, Qnil
);
87 static OBJECT
entry_append(STATE
, OBJECT top
, OBJECT nxt
) {
90 cur
= tuple_at(state
, top
, 3);
94 cur
= tuple_at(state
, cur
, 3);
98 // printf("Inserted new key to hash %d levels deep.\n", depth);
99 tuple_put(state
, last
, 3, nxt
);
103 static OBJECT
add_entry(STATE
, OBJECT h
, unsigned int hsh
, OBJECT ent
) {
104 unsigned int bin
, bins
;
108 bins
= (unsigned int)N2I(hash_get_bins(h
));
109 bin
= find_bin(hsh
, bins
);
111 entry
= tuple_at(state
, hash_get_values(h
), bin
);
114 tuple_put(state
, hash_get_values(h
), bin
, ent
);
116 entry_append(state
, entry
, ent
);
119 hash_set_entries(h
, I2N(N2I(hash_get_entries(h
)) + 1));
123 /* TODO: Why do we use signed int for bins and then use a
124 * loop like for(i = 0; i < bins; i++) ?
126 void hash_redistribute(STATE
, OBJECT hsh
) {
128 unsigned int bin
, hash
;
129 OBJECT values
, new_values
, entry
, next
, slot
;
131 bins
= N2I(hash_get_bins(hsh
));
132 values
= hash_get_values(hsh
);
134 /* TODO: calculate the size both up and down.
135 * i.e. don't assume we're only growing
138 new_values
= tuple_new(state
, size
);
140 // printf("Hash redistribute %p, %d => %d (%d)\n", hsh, bins, size, N2I(hash_get_entries(hsh)));
142 for(i
= 0; i
< bins
; i
++) {
143 entry
= tuple_at(state
, values
, i
);
145 while(!NIL_P(entry
)) {
146 next
= tuple_at(state
, entry
, 3);
147 tuple_put(state
, entry
, 3, Qnil
);
149 hash
= N2I(tuple_at(state
, entry
, 0));
150 bin
= find_bin(hash
, size
);
151 slot
= tuple_at(state
, new_values
, bin
);
154 tuple_put(state
, new_values
, bin
, entry
);
156 entry_append(state
, slot
, entry
);
163 hash_set_values(hsh
, new_values
);
164 hash_set_bins(hsh
, I2N(size
));
167 OBJECT
hash_find_entry(STATE
, OBJECT h
, unsigned int hsh
) {
168 unsigned int bin
, bins
;
171 bins
= (unsigned int)N2I(hash_get_bins(h
));
172 bin
= find_bin(hsh
, bins
);
173 entry
= tuple_at(state
, hash_get_values(h
), bin
);
175 // printf("start: %x, %ud, %d, %d\n", entry, hsh, bin, N2I(hash_get_bins(h)));
177 while(!NIL_P(entry
)) {
178 // printf("entry: %x\n", entry);
179 th
= tuple_at(state
, entry
, 0);
183 entry
= tuple_at(state
, entry
, 3);
188 OBJECT
hash_add(STATE
, OBJECT h
, unsigned int hsh
, OBJECT key
, OBJECT data
) {
192 // printf("hash_add: adding %od\n",hsh);
193 entry
= hash_find_entry(state
, h
, hsh
);
196 tuple_put(state
, entry
, 2, data
);
200 if(hash_redistribute_p(h
)) {
201 hash_redistribute(state
, h
);
204 i
= N2I(hash_get_entries(h
));
205 entry
= entry_new(state
, hsh
, key
, data
);
206 add_entry(state
, h
, hsh
, entry
);
210 /* Sets key/value pair to hash */
211 OBJECT
hash_set(STATE
, OBJECT hash
, OBJECT key
, OBJECT val
) {
212 return hash_add(state
, hash
, object_hash_int(state
, key
), key
, val
);
215 OBJECT
hash_get(STATE
, OBJECT hash
, unsigned int hsh
) {
218 entry
= hash_find_entry(state
, hash
, hsh
);
220 return tuple_at(state
, entry
, 2);
226 /* Find the +value+ for +key+, having hash value +hash+.
227 * Uses +==+ to verify the key in the table matches +key+.
228 * Return TRUE if key was found, and *value will be filled. */
229 int hash_lookup(STATE
, OBJECT tbl
, OBJECT key
, unsigned int hash
, OBJECT
*value
) {
232 ent
= hash_find_entry(state
, tbl
, hash
);
233 if(REFERENCE_P(ent
)) {
234 while(N2I(tuple_at(state
, ent
, 0)) == hash
) {
235 hk
= tuple_at(state
, ent
, 1);
237 *value
= tuple_at(state
, ent
, 2);
241 ent
= tuple_at(state
, ent
, 3);
242 if(NIL_P(ent
)) break;
249 /* Find the +value+ for +key+, having hash value +hash+.
250 * Uses +compare+ to verify the key in the table matches +key+.
251 * Return TRUE if key was found, and *value will be filled. */
252 int hash_lookup2(STATE
, int (*compare
)(STATE
, OBJECT
, OBJECT
),
253 OBJECT tbl
, OBJECT key
, unsigned int hash
, OBJECT
*value
) {
256 ent
= hash_find_entry(state
, tbl
, hash
);
257 if(REFERENCE_P(ent
)) {
258 while(N2I(tuple_at(state
, ent
, 0)) == hash
) {
259 hk
= tuple_at(state
, ent
, 1);
260 if(compare(state
, hk
, key
)) {
261 *value
= tuple_at(state
, ent
, 2);
265 ent
= tuple_at(state
, ent
, 3);
266 if(NIL_P(ent
)) break;
273 void hash_assign(STATE
, int (*compare
)(STATE
, OBJECT
, OBJECT
), OBJECT tbl
,
274 OBJECT key
, unsigned int hash
, OBJECT value
) {
275 OBJECT base
, ent
, hk
;
277 base
= ent
= hash_find_entry(state
, tbl
, hash
);
278 if(REFERENCE_P(ent
)) {
279 while(N2I(tuple_at(state
, ent
, 0)) == hash
) {
280 hk
= tuple_at(state
, ent
, 1);
281 if(compare(state
, hk
, key
)) {
282 tuple_put(state
, ent
, 2, value
);
286 ent
= tuple_at(state
, ent
, 3);
287 if(NIL_P(ent
)) break;
292 if(hash_redistribute_p(tbl
)) {
293 hash_redistribute(state
, tbl
);
296 if(REFERENCE_P(base
)) {
297 ent
= entry_new(state
, hash
, key
, value
);
298 entry_append(state
, base
, ent
);
299 hash_set_entries(tbl
, I2N(N2I(hash_get_entries(tbl
)) + 1));
303 ent
= entry_new(state
, hash
, key
, value
);
304 add_entry(state
, tbl
, hash
, ent
);
307 /* This version of hash_get returns Qundef if the entry was not found.
308 * This is handy when you need to tell the difference between:
309 * x = {} and x = {:a => nil}
312 OBJECT
hash_get_undef(STATE
, OBJECT hash
, unsigned int hsh
) {
315 entry
= hash_find_entry(state
, hash
, hsh
);
317 return tuple_at(state
, entry
, 2);
323 OBJECT
hash_delete(STATE
, OBJECT self
, unsigned int hsh
) {
325 OBJECT entry
, th
, lk
, val
, lst
;
327 bin
= find_bin(hsh
, N2I(hash_get_bins(self
)));
328 entry
= tuple_at(state
, hash_get_values(self
), bin
);
332 while(!NIL_P(entry
)) {
333 th
= tuple_at(state
, entry
, 0);
334 lk
= tuple_at(state
, entry
, 3);
337 val
= tuple_at(state
, entry
, 2);
339 tuple_put(state
, hash_get_values(self
), bin
, lk
);
341 tuple_put(state
, lst
, 3, lk
);
343 hash_set_entries(self
, I2N(N2I(hash_get_entries(self
)) - 1));
354 OBJECT
hash_s_from_tuple(STATE
, OBJECT tup
) {
355 OBJECT hsh
, key
, val
;
357 hsh
= hash_new(state
);
359 fel
= tuple_fields(state
, tup
);
362 key
= tuple_at(state
, tup
, i
);
363 val
= tuple_at(state
, tup
, i
+ 1);
365 hash_set(state
, hsh
, key
, val
);
371 OBJECT
csm_find(STATE
, OBJECT csm
, OBJECT key
) {
375 for(i
= 0; i
< CSM_SIZE
; i
+= 2) {
376 t
= tuple_at(state
, csm
, i
);
379 val
= tuple_at(state
, csm
, i
+ 1);
380 // printf("Found %s: %s\n", _inspect(key), _inspect(val));
385 //printf("Unable to find ivar %s\n", _inspect(key));
390 OBJECT
csm_add(STATE
, OBJECT csm
, OBJECT key
, OBJECT val
) {
394 for(i
= 0; i
< CSM_SIZE
; i
+= 2) {
395 tmp
= tuple_at(state
, csm
, i
);
396 if(tmp
== key
|| NIL_P(tmp
)) {
397 // printf("Added ivar %s at %d\n", _inspect(key), i);
398 tuple_put(state
, csm
, i
, key
);
399 tuple_put(state
, csm
, i
+ 1, val
);
407 OBJECT
csm_into_hash(STATE
, OBJECT csm
) {
411 hsh
= hash_new(state
);
413 for(i
= 0; i
< CSM_SIZE
; i
+= 2) {
414 k
= tuple_at(state
, csm
, i
);
416 hash_set(state
, hsh
, k
, tuple_at(state
, csm
, i
+ 1));
423 OBJECT
csm_into_lookuptable(STATE
, OBJECT csm
) {
427 tbl
= lookuptable_new(state
);
429 for(i
= 0; i
< CSM_SIZE
; i
+= 2) {
430 k
= tuple_at(state
, csm
, i
);
432 lookuptable_store(state
, tbl
, k
, tuple_at(state
, csm
, i
+ 1));