isomorfeus-iodine 0.7.45 → 0.7.46

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,409 +1,409 @@
1
- /*
2
- Copyright: Boaz Segev, 2017-2019
3
- License: MIT
4
- */
5
-
6
- #include <fiobject.h>
7
-
8
- #include <assert.h>
9
- #include <fiobj_hash.h>
10
-
11
- #define FIO_SET_CALLOC(size, count) fio_calloc((size), (count))
12
- #define FIO_SET_REALLOC(ptr, original_size, size, valid_data_length) \
13
- fio_realloc2((ptr), (size), (valid_data_length))
14
- #define FIO_SET_FREE(ptr, size) fio_free((ptr))
15
-
16
- #define FIO_SET_NAME fio_hash__
17
- #define FIO_SET_KEY_TYPE FIOBJ
18
- #define FIO_SET_KEY_COMPARE(o1, o2) \
19
- ((o2) == ((FIOBJ)-1) || (o1) == ((FIOBJ)-1) || fiobj_iseq((o1), (o2)))
20
- #define FIO_SET_KEY_COPY(dest, obj) ((dest) = fiobj_dup((obj)))
21
- #define FIO_SET_KEY_DESTROY(obj) \
22
- do { \
23
- fiobj_free((obj)); \
24
- (obj) = FIOBJ_INVALID; \
25
- } while (0)
26
- #define FIO_SET_OBJ_TYPE FIOBJ
27
- #define FIO_SET_OBJ_COMPARE(o1, o2) fiobj_iseq((o1), (o2))
28
- #define FIO_SET_OBJ_COPY(dest, obj) ((dest) = fiobj_dup(obj))
29
- #define FIO_SET_OBJ_DESTROY(obj) \
30
- do { \
31
- fiobj_free((obj)); \
32
- (obj) = FIOBJ_INVALID; \
33
- } while (0)
34
-
35
- #include <fio.h>
36
-
37
- #include <errno.h>
38
-
39
- #include <pthread.h>
40
-
41
- /* *****************************************************************************
42
- Hash types
43
- ***************************************************************************** */
44
- typedef struct {
45
- fiobj_object_header_s head;
46
- fio_hash___s hash;
47
- } fiobj_hash_s;
48
-
49
- #define obj2hash(o) ((fiobj_hash_s *)(FIOBJ2PTR(o)))
50
-
51
- void fiobj_hash_rehash(FIOBJ h) {
52
- assert(h && FIOBJ_TYPE_IS(h, FIOBJ_T_HASH));
53
- fio_hash___rehash(&obj2hash(h)->hash);
54
- }
55
-
56
- /* *****************************************************************************
57
- Hash alloc + VTable
58
- ***************************************************************************** */
59
-
60
- static void fiobj_hash_dealloc(FIOBJ o, void (*task)(FIOBJ, void *),
61
- void *arg) {
62
- FIO_SET_FOR_LOOP(&obj2hash(o)->hash, i) {
63
- if (i->obj.key)
64
- task((FIOBJ)i->obj.obj, arg);
65
- fiobj_free((FIOBJ)i->obj.key);
66
- i->obj.key = FIOBJ_INVALID;
67
- i->obj.obj = FIOBJ_INVALID;
68
- }
69
- obj2hash(o)->hash.count = 0;
70
- fio_hash___free(&obj2hash(o)->hash);
71
- fio_free(FIOBJ2PTR(o));
72
- }
73
-
74
- static pthread_key_t each_at_key;
75
- static pthread_once_t each_at_key_once = PTHREAD_ONCE_INIT;
76
- static void init_each_at_key(void) {
77
- pthread_key_create(&each_at_key, free);
78
- }
79
- static void init_each_at_key_ptr(void) {
80
- FIOBJ *eak = malloc(sizeof(FIOBJ));
81
- FIO_ASSERT_ALLOC(eak);
82
- *eak = FIOBJ_INVALID;
83
- pthread_setspecific(each_at_key, eak);
84
- }
85
-
86
- static size_t fiobj_hash_each1(FIOBJ o, size_t start_at,
87
- int (*task)(FIOBJ obj, void *arg), void *arg) {
88
- assert(o && FIOBJ_TYPE_IS(o, FIOBJ_T_HASH));
89
- pthread_once(&each_at_key_once, init_each_at_key);
90
- FIOBJ *each_at_key_ptr = (FIOBJ *)pthread_getspecific(each_at_key);
91
- if (!each_at_key_ptr) {
92
- init_each_at_key_ptr();
93
- each_at_key_ptr = (FIOBJ *)pthread_getspecific(each_at_key);
94
- }
95
- FIOBJ old_each_at_key = *each_at_key_ptr;
96
- fio_hash___s *hash = &obj2hash(o)->hash;
97
- size_t count = 0;
98
- if (hash->count == hash->pos) {
99
- /* no holes in the hash, we can work as we please. */
100
- for (count = start_at; count < hash->count; ++count) {
101
- *each_at_key_ptr = hash->ordered[count].obj.key;
102
- if (task((FIOBJ)hash->ordered[count].obj.obj, arg) == -1) {
103
- ++count;
104
- goto end;
105
- }
106
- }
107
- } else {
108
- size_t pos = 0;
109
- for (; pos < start_at && pos < hash->pos; ++pos) {
110
- /* counting */
111
- if (hash->ordered[pos].obj.key == FIOBJ_INVALID)
112
- ++start_at;
113
- else
114
- ++count;
115
- }
116
- for (; pos < hash->pos; ++pos) {
117
- /* performing */
118
- if (hash->ordered[pos].obj.key == FIOBJ_INVALID)
119
- continue;
120
- ++count;
121
- *each_at_key_ptr = hash->ordered[pos].obj.key;
122
- if (task((FIOBJ)hash->ordered[pos].obj.obj, arg) == -1)
123
- break;
124
- }
125
- }
126
- end:
127
- *each_at_key_ptr = old_each_at_key;
128
- return count;
129
- }
130
-
131
- FIOBJ fiobj_hash_key_in_loop(void) {
132
- pthread_once(&each_at_key_once, init_each_at_key);
133
- FIOBJ *each_at_key_ptr = (FIOBJ *)pthread_getspecific(each_at_key);
134
- if (!each_at_key_ptr) {
135
- init_each_at_key_ptr();
136
- each_at_key_ptr = (FIOBJ *)pthread_getspecific(each_at_key);
137
- }
138
- return *each_at_key_ptr;
139
- }
140
-
141
- static size_t fiobj_hash_is_eq(const FIOBJ self, const FIOBJ other) {
142
- if (fio_hash___count(&obj2hash(self)->hash) !=
143
- fio_hash___count(&obj2hash(other)->hash))
144
- return 0;
145
- return 1;
146
- }
147
-
148
- /** Returns the number of elements in the Array. */
149
- size_t fiobj_hash_count(const FIOBJ o) {
150
- assert(o && FIOBJ_TYPE_IS(o, FIOBJ_T_HASH));
151
- return fio_hash___count(&obj2hash(o)->hash);
152
- }
153
-
154
- intptr_t fiobj_hash2num(const FIOBJ o) { return (intptr_t)fiobj_hash_count(o); }
155
-
156
- static size_t fiobj_hash_is_true(const FIOBJ o) {
157
- return fiobj_hash_count(o) != 0;
158
- }
159
-
160
- fio_str_info_s fiobject___noop_to_str(const FIOBJ o);
161
- intptr_t fiobject___noop_to_i(const FIOBJ o);
162
- double fiobject___noop_to_f(const FIOBJ o);
163
-
164
- const fiobj_object_vtable_s FIOBJECT_VTABLE_HASH = {
165
- .class_name = "Hash",
166
- .dealloc = fiobj_hash_dealloc,
167
- .is_eq = fiobj_hash_is_eq,
168
- .count = fiobj_hash_count,
169
- .each = fiobj_hash_each1,
170
- .is_true = fiobj_hash_is_true,
171
- .to_str = fiobject___noop_to_str,
172
- .to_i = fiobj_hash2num,
173
- .to_f = fiobject___noop_to_f,
174
- };
175
-
176
- /* *****************************************************************************
177
- Hash API
178
- ***************************************************************************** */
179
-
180
- /**
181
- * Creates a mutable empty Hash object. Use `fiobj_free` when done.
182
- *
183
- * Notice that these Hash objects are designed for smaller collections and
184
- * retain order of object insertion.
185
- */
186
- FIOBJ fiobj_hash_new(void) {
187
- fiobj_hash_s *h = fio_malloc(sizeof(*h));
188
- FIO_ASSERT_ALLOC(h);
189
- *h = (fiobj_hash_s){.head = {.ref = 1, .type = FIOBJ_T_HASH},
190
- .hash = FIO_SET_INIT};
191
- return (FIOBJ)h | FIOBJECT_HASH_FLAG;
192
- }
193
-
194
- /**
195
- * Creates a mutable empty Hash object with an initial capacity of `capa`. Use
196
- * `fiobj_free` when done.
197
- *
198
- * Notice that these Hash objects are designed for smaller collections and
199
- * retain order of object insertion.
200
- */
201
- FIOBJ fiobj_hash_new2(size_t capa) {
202
- fiobj_hash_s *h = fio_malloc(sizeof(*h));
203
- FIO_ASSERT_ALLOC(h);
204
- *h = (fiobj_hash_s){.head = {.ref = 1, .type = FIOBJ_T_HASH},
205
- .hash = FIO_SET_INIT};
206
- fio_hash___capa_require(&h->hash, capa);
207
- return (FIOBJ)h | FIOBJECT_HASH_FLAG;
208
- }
209
-
210
- /**
211
- * Returns a temporary theoretical Hash map capacity.
212
- * This could be used for testing performance and memory consumption.
213
- */
214
- size_t fiobj_hash_capa(const FIOBJ hash) {
215
- assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
216
- return fio_hash___capa(&obj2hash(hash)->hash);
217
- }
218
-
219
- /**
220
- * Sets a key-value pair in the Hash, duplicating the Symbol and **moving**
221
- * the ownership of the object to the Hash.
222
- *
223
- * Returns -1 on error.
224
- */
225
- int fiobj_hash_set(FIOBJ hash, FIOBJ key, FIOBJ obj) {
226
- assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
227
- if (FIOBJ_TYPE_IS(key, FIOBJ_T_STRING))
228
- fiobj_str_freeze(key);
229
- fio_hash___insert(&obj2hash(hash)->hash, fiobj_obj2hash(key), key, obj, NULL);
230
- fiobj_free(obj); /* take ownership - free the user's reference. */
231
- return 0;
232
- }
233
-
234
- /**
235
- * Allows the Hash to be used as a stack.
236
- *
237
- * If a pointer `key` is provided, it will receive ownership of the key
238
- * (remember to free).
239
- *
240
- * Returns FIOBJ_INVALID on error.
241
- *
242
- * Returns and object if successful (remember to free).
243
- */
244
- FIOBJ fiobj_hash_pop(FIOBJ hash, FIOBJ *key) {
245
- assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
246
- FIOBJ old;
247
- if (fio_hash___count(&obj2hash(hash)->hash))
248
- return FIOBJ_INVALID;
249
- old = fiobj_dup(fio_hash___last(&obj2hash(hash)->hash).obj);
250
- if (key)
251
- *key = fiobj_dup(fio_hash___last(&obj2hash(hash)->hash).key);
252
- fio_hash___pop(&obj2hash(hash)->hash);
253
- return old;
254
- }
255
-
256
- /**
257
- * Replaces the value in a key-value pair, returning the old value (and it's
258
- * ownership) to the caller.
259
- *
260
- * A return value of NULL indicates that no previous object existed (but a new
261
- * key-value pair was created.
262
- *
263
- * Errors are silently ignored.
264
- */
265
- FIOBJ fiobj_hash_replace(FIOBJ hash, FIOBJ key, FIOBJ obj) {
266
- assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
267
- FIOBJ old = FIOBJ_INVALID;
268
- fio_hash___insert(&obj2hash(hash)->hash, fiobj_obj2hash(key), key, obj, &old);
269
- fiobj_free(obj); /* take ownership - free the user's reference. */
270
- return old;
271
- }
272
-
273
- /**
274
- * Removes a key-value pair from the Hash, if it exists.
275
- */
276
- FIOBJ fiobj_hash_remove(FIOBJ hash, FIOBJ key) {
277
- assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
278
- FIOBJ old = FIOBJ_INVALID;
279
- fio_hash___remove(&obj2hash(hash)->hash, fiobj_obj2hash(key), key, &old);
280
- return old;
281
- }
282
-
283
- /**
284
- * Removes a key-value pair from the Hash, if it exists, returning the old
285
- * object (instead of freeing it).
286
- */
287
- FIOBJ fiobj_hash_remove2(FIOBJ hash, uint64_t hash_value) {
288
- assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
289
- FIOBJ old = FIOBJ_INVALID;
290
- fio_hash___remove(&obj2hash(hash)->hash, hash_value, -1, &old);
291
- return old;
292
- }
293
-
294
- /**
295
- * Deletes a key-value pair from the Hash, if it exists, freeing the
296
- * associated object.
297
- *
298
- * Returns -1 on type error or if the object never existed.
299
- */
300
- int fiobj_hash_delete(FIOBJ hash, FIOBJ key) {
301
- return fio_hash___remove(&obj2hash(hash)->hash, fiobj_obj2hash(key), key,
302
- NULL);
303
- }
304
-
305
- /**
306
- * Deletes a key-value pair from the Hash, if it exists, freeing the
307
- * associated object.
308
- *
309
- * This function takes a `uintptr_t` Hash value (see `fio_siphash`) to
310
- * perform a lookup in the HashMap, which is slightly faster than the other
311
- * variations.
312
- *
313
- * Returns -1 on type error or if the object never existed.
314
- */
315
- int fiobj_hash_delete2(FIOBJ hash, uint64_t key_hash) {
316
- return fio_hash___remove(&obj2hash(hash)->hash, key_hash, -1, NULL);
317
- }
318
-
319
- /**
320
- * Returns a temporary handle to the object associated with the Symbol, NULL
321
- * if none.
322
- */
323
- FIOBJ fiobj_hash_get(const FIOBJ hash, FIOBJ key) {
324
- assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
325
- return fio_hash___find(&obj2hash(hash)->hash, fiobj_obj2hash(key), key);
326
- ;
327
- }
328
-
329
- /**
330
- * Returns a temporary handle to the object associated hashed key value.
331
- *
332
- * This function takes a `uintptr_t` Hash value (see `fio_siphash`) to
333
- * perform a lookup in the HashMap.
334
- *
335
- * Returns NULL if no object is associated with this hashed key value.
336
- */
337
- FIOBJ fiobj_hash_get2(const FIOBJ hash, uint64_t key_hash) {
338
- assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
339
- return fio_hash___find(&obj2hash(hash)->hash, key_hash, -1);
340
- ;
341
- }
342
-
343
- /**
344
- * Returns 1 if the key (Symbol) exists in the Hash, even if value is NULL.
345
- */
346
- int fiobj_hash_haskey(const FIOBJ hash, FIOBJ key) {
347
- assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
348
- return fio_hash___find(&obj2hash(hash)->hash, fiobj_obj2hash(key), key) !=
349
- FIOBJ_INVALID;
350
- }
351
-
352
- /**
353
- * Empties the Hash.
354
- */
355
- void fiobj_hash_clear(const FIOBJ hash) {
356
- assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
357
- fio_hash___free(&obj2hash(hash)->hash);
358
- }
359
-
360
- /* *****************************************************************************
361
- Simple Tests
362
- ***************************************************************************** */
363
-
364
- #if DEBUG
365
- void fiobj_test_hash(void) {
366
- fprintf(stderr, "=== Testing Hash\n");
367
- #define TEST_ASSERT(cond, ...) \
368
- if (!(cond)) { \
369
- fprintf(stderr, "* " __VA_ARGS__); \
370
- fprintf(stderr, "Testing failed.\n"); \
371
- exit(-1); \
372
- }
373
- FIOBJ o = fiobj_hash_new();
374
- FIOBJ str_key = fiobj_str_new("Hello World!", 12);
375
- TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_HASH), "Type identification error!\n");
376
- TEST_ASSERT(fiobj_hash_count(o) == 0, "Hash should be empty!\n");
377
- fiobj_hash_set(o, str_key, fiobj_true());
378
- TEST_ASSERT(fiobj_str_write(str_key, "should fail...", 13) == 0,
379
- "wrote to frozen string?");
380
- TEST_ASSERT(fiobj_obj2cstr(str_key).len == 12,
381
- "String was mutated (not frozen)!\n");
382
- TEST_ASSERT(fiobj_hash_get(o, str_key) == fiobj_true(),
383
- "full compare didn't get value back");
384
- TEST_ASSERT(fiobj_hash_get2(o, fiobj_obj2hash(str_key)) == fiobj_true(),
385
- "hash compare didn't get value back");
386
-
387
- FIOBJ o2 = fiobj_hash_new2(3);
388
- TEST_ASSERT(obj2hash(o2)->hash.capa >= 3,
389
- "Hash capacity should be larger than 3! %zu != 4\n",
390
- (size_t)obj2hash(o2)->hash.capa);
391
- fiobj_hash_set(o2, str_key, fiobj_true());
392
- TEST_ASSERT(fiobj_hash_is_eq(o, o2), "Hashes not equal at core! %zu != %zu\n",
393
- fiobj_hash_count(o), fiobj_hash_count(o2));
394
- TEST_ASSERT(fiobj_iseq(o, o2), "Hashes not equal!\n");
395
- TEST_ASSERT(obj2hash(o2)->hash.capa > 3,
396
- "Hash capacity should be larger than 3! %zu != 4\n",
397
- (size_t)obj2hash(o2)->hash.capa);
398
-
399
- fiobj_hash_delete(o, str_key);
400
-
401
- TEST_ASSERT(fiobj_hash_get2(o, fiobj_obj2hash(str_key)) == 0,
402
- "item wasn't deleted!");
403
- fiobj_free(
404
- str_key); /* note that a copy will remain in the Hash until rehashing. */
405
- fiobj_free(o);
406
- fiobj_free(o2);
407
- fprintf(stderr, "* passed.\n");
408
- }
409
- #endif
1
+ /*
2
+ Copyright: Boaz Segev, 2017-2019
3
+ License: MIT
4
+ */
5
+
6
+ #include <fiobject.h>
7
+
8
+ #include <assert.h>
9
+ #include <fiobj_hash.h>
10
+
11
+ #define FIO_SET_CALLOC(size, count) fio_calloc((size), (count))
12
+ #define FIO_SET_REALLOC(ptr, original_size, size, valid_data_length) \
13
+ fio_realloc2((ptr), (size), (valid_data_length))
14
+ #define FIO_SET_FREE(ptr, size) fio_free((ptr))
15
+
16
+ #define FIO_SET_NAME fio_hash__
17
+ #define FIO_SET_KEY_TYPE FIOBJ
18
+ #define FIO_SET_KEY_COMPARE(o1, o2) \
19
+ ((o2) == ((FIOBJ)-1) || (o1) == ((FIOBJ)-1) || fiobj_iseq((o1), (o2)))
20
+ #define FIO_SET_KEY_COPY(dest, obj) ((dest) = fiobj_dup((obj)))
21
+ #define FIO_SET_KEY_DESTROY(obj) \
22
+ do { \
23
+ fiobj_free((obj)); \
24
+ (obj) = FIOBJ_INVALID; \
25
+ } while (0)
26
+ #define FIO_SET_OBJ_TYPE FIOBJ
27
+ #define FIO_SET_OBJ_COMPARE(o1, o2) fiobj_iseq((o1), (o2))
28
+ #define FIO_SET_OBJ_COPY(dest, obj) ((dest) = fiobj_dup(obj))
29
+ #define FIO_SET_OBJ_DESTROY(obj) \
30
+ do { \
31
+ fiobj_free((obj)); \
32
+ (obj) = FIOBJ_INVALID; \
33
+ } while (0)
34
+
35
+ #include <fio.h>
36
+
37
+ #include <errno.h>
38
+
39
+ #include <pthread.h>
40
+
41
+ /* *****************************************************************************
42
+ Hash types
43
+ ***************************************************************************** */
44
+ typedef struct {
45
+ fiobj_object_header_s head;
46
+ fio_hash___s hash;
47
+ } fiobj_hash_s;
48
+
49
+ #define obj2hash(o) ((fiobj_hash_s *)(FIOBJ2PTR(o)))
50
+
51
+ void fiobj_hash_rehash(FIOBJ h) {
52
+ assert(h && FIOBJ_TYPE_IS(h, FIOBJ_T_HASH));
53
+ fio_hash___rehash(&obj2hash(h)->hash);
54
+ }
55
+
56
+ /* *****************************************************************************
57
+ Hash alloc + VTable
58
+ ***************************************************************************** */
59
+
60
+ static void fiobj_hash_dealloc(FIOBJ o, void (*task)(FIOBJ, void *),
61
+ void *arg) {
62
+ FIO_SET_FOR_LOOP(&obj2hash(o)->hash, i) {
63
+ if (i->obj.key)
64
+ task((FIOBJ)i->obj.obj, arg);
65
+ fiobj_free((FIOBJ)i->obj.key);
66
+ i->obj.key = FIOBJ_INVALID;
67
+ i->obj.obj = FIOBJ_INVALID;
68
+ }
69
+ obj2hash(o)->hash.count = 0;
70
+ fio_hash___free(&obj2hash(o)->hash);
71
+ fio_free(FIOBJ2PTR(o));
72
+ }
73
+
74
+ static pthread_key_t each_at_key;
75
+ static pthread_once_t each_at_key_once = PTHREAD_ONCE_INIT;
76
+ static void init_each_at_key(void) {
77
+ pthread_key_create(&each_at_key, free);
78
+ }
79
+ static void init_each_at_key_ptr(void) {
80
+ FIOBJ *eak = malloc(sizeof(FIOBJ));
81
+ FIO_ASSERT_ALLOC(eak);
82
+ *eak = FIOBJ_INVALID;
83
+ pthread_setspecific(each_at_key, eak);
84
+ }
85
+
86
+ static size_t fiobj_hash_each1(FIOBJ o, size_t start_at,
87
+ int (*task)(FIOBJ obj, void *arg), void *arg) {
88
+ assert(o && FIOBJ_TYPE_IS(o, FIOBJ_T_HASH));
89
+ pthread_once(&each_at_key_once, init_each_at_key);
90
+ FIOBJ *each_at_key_ptr = (FIOBJ *)pthread_getspecific(each_at_key);
91
+ if (!each_at_key_ptr) {
92
+ init_each_at_key_ptr();
93
+ each_at_key_ptr = (FIOBJ *)pthread_getspecific(each_at_key);
94
+ }
95
+ FIOBJ old_each_at_key = *each_at_key_ptr;
96
+ fio_hash___s *hash = &obj2hash(o)->hash;
97
+ size_t count = 0;
98
+ if (hash->count == hash->pos) {
99
+ /* no holes in the hash, we can work as we please. */
100
+ for (count = start_at; count < hash->count; ++count) {
101
+ *each_at_key_ptr = hash->ordered[count].obj.key;
102
+ if (task((FIOBJ)hash->ordered[count].obj.obj, arg) == -1) {
103
+ ++count;
104
+ goto end;
105
+ }
106
+ }
107
+ } else {
108
+ size_t pos = 0;
109
+ for (; pos < start_at && pos < hash->pos; ++pos) {
110
+ /* counting */
111
+ if (hash->ordered[pos].obj.key == FIOBJ_INVALID)
112
+ ++start_at;
113
+ else
114
+ ++count;
115
+ }
116
+ for (; pos < hash->pos; ++pos) {
117
+ /* performing */
118
+ if (hash->ordered[pos].obj.key == FIOBJ_INVALID)
119
+ continue;
120
+ ++count;
121
+ *each_at_key_ptr = hash->ordered[pos].obj.key;
122
+ if (task((FIOBJ)hash->ordered[pos].obj.obj, arg) == -1)
123
+ break;
124
+ }
125
+ }
126
+ end:
127
+ *each_at_key_ptr = old_each_at_key;
128
+ return count;
129
+ }
130
+
131
+ FIOBJ fiobj_hash_key_in_loop(void) {
132
+ pthread_once(&each_at_key_once, init_each_at_key);
133
+ FIOBJ *each_at_key_ptr = (FIOBJ *)pthread_getspecific(each_at_key);
134
+ if (!each_at_key_ptr) {
135
+ init_each_at_key_ptr();
136
+ each_at_key_ptr = (FIOBJ *)pthread_getspecific(each_at_key);
137
+ }
138
+ return *each_at_key_ptr;
139
+ }
140
+
141
+ static size_t fiobj_hash_is_eq(const FIOBJ self, const FIOBJ other) {
142
+ if (fio_hash___count(&obj2hash(self)->hash) !=
143
+ fio_hash___count(&obj2hash(other)->hash))
144
+ return 0;
145
+ return 1;
146
+ }
147
+
148
+ /** Returns the number of elements in the Array. */
149
+ size_t fiobj_hash_count(const FIOBJ o) {
150
+ assert(o && FIOBJ_TYPE_IS(o, FIOBJ_T_HASH));
151
+ return fio_hash___count(&obj2hash(o)->hash);
152
+ }
153
+
154
+ intptr_t fiobj_hash2num(const FIOBJ o) { return (intptr_t)fiobj_hash_count(o); }
155
+
156
+ static size_t fiobj_hash_is_true(const FIOBJ o) {
157
+ return fiobj_hash_count(o) != 0;
158
+ }
159
+
160
+ fio_str_info_s fiobject___noop_to_str(const FIOBJ o);
161
+ intptr_t fiobject___noop_to_i(const FIOBJ o);
162
+ double fiobject___noop_to_f(const FIOBJ o);
163
+
164
+ const fiobj_object_vtable_s FIOBJECT_VTABLE_HASH = {
165
+ .class_name = "Hash",
166
+ .dealloc = fiobj_hash_dealloc,
167
+ .is_eq = fiobj_hash_is_eq,
168
+ .count = fiobj_hash_count,
169
+ .each = fiobj_hash_each1,
170
+ .is_true = fiobj_hash_is_true,
171
+ .to_str = fiobject___noop_to_str,
172
+ .to_i = fiobj_hash2num,
173
+ .to_f = fiobject___noop_to_f,
174
+ };
175
+
176
+ /* *****************************************************************************
177
+ Hash API
178
+ ***************************************************************************** */
179
+
180
+ /**
181
+ * Creates a mutable empty Hash object. Use `fiobj_free` when done.
182
+ *
183
+ * Notice that these Hash objects are designed for smaller collections and
184
+ * retain order of object insertion.
185
+ */
186
+ FIOBJ fiobj_hash_new(void) {
187
+ fiobj_hash_s *h = fio_malloc(sizeof(*h));
188
+ FIO_ASSERT_ALLOC(h);
189
+ *h = (fiobj_hash_s){.head = {.ref = 1, .type = FIOBJ_T_HASH},
190
+ .hash = FIO_SET_INIT};
191
+ return (FIOBJ)h | FIOBJECT_HASH_FLAG;
192
+ }
193
+
194
+ /**
195
+ * Creates a mutable empty Hash object with an initial capacity of `capa`. Use
196
+ * `fiobj_free` when done.
197
+ *
198
+ * Notice that these Hash objects are designed for smaller collections and
199
+ * retain order of object insertion.
200
+ */
201
+ FIOBJ fiobj_hash_new2(size_t capa) {
202
+ fiobj_hash_s *h = fio_malloc(sizeof(*h));
203
+ FIO_ASSERT_ALLOC(h);
204
+ *h = (fiobj_hash_s){.head = {.ref = 1, .type = FIOBJ_T_HASH},
205
+ .hash = FIO_SET_INIT};
206
+ fio_hash___capa_require(&h->hash, capa);
207
+ return (FIOBJ)h | FIOBJECT_HASH_FLAG;
208
+ }
209
+
210
+ /**
211
+ * Returns a temporary theoretical Hash map capacity.
212
+ * This could be used for testing performance and memory consumption.
213
+ */
214
+ size_t fiobj_hash_capa(const FIOBJ hash) {
215
+ assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
216
+ return fio_hash___capa(&obj2hash(hash)->hash);
217
+ }
218
+
219
+ /**
220
+ * Sets a key-value pair in the Hash, duplicating the Symbol and **moving**
221
+ * the ownership of the object to the Hash.
222
+ *
223
+ * Returns -1 on error.
224
+ */
225
+ int fiobj_hash_set(FIOBJ hash, FIOBJ key, FIOBJ obj) {
226
+ assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
227
+ if (FIOBJ_TYPE_IS(key, FIOBJ_T_STRING))
228
+ fiobj_str_freeze(key);
229
+ fio_hash___insert(&obj2hash(hash)->hash, fiobj_obj2hash(key), key, obj, NULL);
230
+ fiobj_free(obj); /* take ownership - free the user's reference. */
231
+ return 0;
232
+ }
233
+
234
+ /**
235
+ * Allows the Hash to be used as a stack.
236
+ *
237
+ * If a pointer `key` is provided, it will receive ownership of the key
238
+ * (remember to free).
239
+ *
240
+ * Returns FIOBJ_INVALID on error.
241
+ *
242
+ * Returns and object if successful (remember to free).
243
+ */
244
+ FIOBJ fiobj_hash_pop(FIOBJ hash, FIOBJ *key) {
245
+ assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
246
+ FIOBJ old;
247
+ if (fio_hash___count(&obj2hash(hash)->hash))
248
+ return FIOBJ_INVALID;
249
+ old = fiobj_dup(fio_hash___last(&obj2hash(hash)->hash).obj);
250
+ if (key)
251
+ *key = fiobj_dup(fio_hash___last(&obj2hash(hash)->hash).key);
252
+ fio_hash___pop(&obj2hash(hash)->hash);
253
+ return old;
254
+ }
255
+
256
+ /**
257
+ * Replaces the value in a key-value pair, returning the old value (and it's
258
+ * ownership) to the caller.
259
+ *
260
+ * A return value of NULL indicates that no previous object existed (but a new
261
+ * key-value pair was created.
262
+ *
263
+ * Errors are silently ignored.
264
+ */
265
+ FIOBJ fiobj_hash_replace(FIOBJ hash, FIOBJ key, FIOBJ obj) {
266
+ assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
267
+ FIOBJ old = FIOBJ_INVALID;
268
+ fio_hash___insert(&obj2hash(hash)->hash, fiobj_obj2hash(key), key, obj, &old);
269
+ fiobj_free(obj); /* take ownership - free the user's reference. */
270
+ return old;
271
+ }
272
+
273
+ /**
274
+ * Removes a key-value pair from the Hash, if it exists.
275
+ */
276
+ FIOBJ fiobj_hash_remove(FIOBJ hash, FIOBJ key) {
277
+ assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
278
+ FIOBJ old = FIOBJ_INVALID;
279
+ fio_hash___remove(&obj2hash(hash)->hash, fiobj_obj2hash(key), key, &old);
280
+ return old;
281
+ }
282
+
283
+ /**
284
+ * Removes a key-value pair from the Hash, if it exists, returning the old
285
+ * object (instead of freeing it).
286
+ */
287
+ FIOBJ fiobj_hash_remove2(FIOBJ hash, uint64_t hash_value) {
288
+ assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
289
+ FIOBJ old = FIOBJ_INVALID;
290
+ fio_hash___remove(&obj2hash(hash)->hash, hash_value, -1, &old);
291
+ return old;
292
+ }
293
+
294
+ /**
295
+ * Deletes a key-value pair from the Hash, if it exists, freeing the
296
+ * associated object.
297
+ *
298
+ * Returns -1 on type error or if the object never existed.
299
+ */
300
+ int fiobj_hash_delete(FIOBJ hash, FIOBJ key) {
301
+ return fio_hash___remove(&obj2hash(hash)->hash, fiobj_obj2hash(key), key,
302
+ NULL);
303
+ }
304
+
305
+ /**
306
+ * Deletes a key-value pair from the Hash, if it exists, freeing the
307
+ * associated object.
308
+ *
309
+ * This function takes a `uintptr_t` Hash value (see `fio_siphash`) to
310
+ * perform a lookup in the HashMap, which is slightly faster than the other
311
+ * variations.
312
+ *
313
+ * Returns -1 on type error or if the object never existed.
314
+ */
315
+ int fiobj_hash_delete2(FIOBJ hash, uint64_t key_hash) {
316
+ return fio_hash___remove(&obj2hash(hash)->hash, key_hash, -1, NULL);
317
+ }
318
+
319
+ /**
320
+ * Returns a temporary handle to the object associated with the Symbol, NULL
321
+ * if none.
322
+ */
323
+ FIOBJ fiobj_hash_get(const FIOBJ hash, FIOBJ key) {
324
+ assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
325
+ return fio_hash___find(&obj2hash(hash)->hash, fiobj_obj2hash(key), key);
326
+ ;
327
+ }
328
+
329
+ /**
330
+ * Returns a temporary handle to the object associated hashed key value.
331
+ *
332
+ * This function takes a `uintptr_t` Hash value (see `fio_siphash`) to
333
+ * perform a lookup in the HashMap.
334
+ *
335
+ * Returns NULL if no object is associated with this hashed key value.
336
+ */
337
+ FIOBJ fiobj_hash_get2(const FIOBJ hash, uint64_t key_hash) {
338
+ assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
339
+ return fio_hash___find(&obj2hash(hash)->hash, key_hash, -1);
340
+ ;
341
+ }
342
+
343
+ /**
344
+ * Returns 1 if the key (Symbol) exists in the Hash, even if value is NULL.
345
+ */
346
+ int fiobj_hash_haskey(const FIOBJ hash, FIOBJ key) {
347
+ assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
348
+ return fio_hash___find(&obj2hash(hash)->hash, fiobj_obj2hash(key), key) !=
349
+ FIOBJ_INVALID;
350
+ }
351
+
352
+ /**
353
+ * Empties the Hash.
354
+ */
355
+ void fiobj_hash_clear(const FIOBJ hash) {
356
+ assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
357
+ fio_hash___free(&obj2hash(hash)->hash);
358
+ }
359
+
360
+ /* *****************************************************************************
361
+ Simple Tests
362
+ ***************************************************************************** */
363
+
364
+ #if DEBUG
365
+ void fiobj_test_hash(void) {
366
+ fprintf(stderr, "=== Testing Hash\n");
367
+ #define TEST_ASSERT(cond, ...) \
368
+ if (!(cond)) { \
369
+ fprintf(stderr, "* " __VA_ARGS__); \
370
+ fprintf(stderr, "Testing failed.\n"); \
371
+ exit(-1); \
372
+ }
373
+ FIOBJ o = fiobj_hash_new();
374
+ FIOBJ str_key = fiobj_str_new("Hello World!", 12);
375
+ TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_HASH), "Type identification error!\n");
376
+ TEST_ASSERT(fiobj_hash_count(o) == 0, "Hash should be empty!\n");
377
+ fiobj_hash_set(o, str_key, fiobj_true());
378
+ TEST_ASSERT(fiobj_str_write(str_key, "should fail...", 13) == 0,
379
+ "wrote to frozen string?");
380
+ TEST_ASSERT(fiobj_obj2cstr(str_key).len == 12,
381
+ "String was mutated (not frozen)!\n");
382
+ TEST_ASSERT(fiobj_hash_get(o, str_key) == fiobj_true(),
383
+ "full compare didn't get value back");
384
+ TEST_ASSERT(fiobj_hash_get2(o, fiobj_obj2hash(str_key)) == fiobj_true(),
385
+ "hash compare didn't get value back");
386
+
387
+ FIOBJ o2 = fiobj_hash_new2(3);
388
+ TEST_ASSERT(obj2hash(o2)->hash.capa >= 3,
389
+ "Hash capacity should be larger than 3! %zu != 4\n",
390
+ (size_t)obj2hash(o2)->hash.capa);
391
+ fiobj_hash_set(o2, str_key, fiobj_true());
392
+ TEST_ASSERT(fiobj_hash_is_eq(o, o2), "Hashes not equal at core! %zu != %zu\n",
393
+ fiobj_hash_count(o), fiobj_hash_count(o2));
394
+ TEST_ASSERT(fiobj_iseq(o, o2), "Hashes not equal!\n");
395
+ TEST_ASSERT(obj2hash(o2)->hash.capa > 3,
396
+ "Hash capacity should be larger than 3! %zu != 4\n",
397
+ (size_t)obj2hash(o2)->hash.capa);
398
+
399
+ fiobj_hash_delete(o, str_key);
400
+
401
+ TEST_ASSERT(fiobj_hash_get2(o, fiobj_obj2hash(str_key)) == 0,
402
+ "item wasn't deleted!");
403
+ fiobj_free(
404
+ str_key); /* note that a copy will remain in the Hash until rehashing. */
405
+ fiobj_free(o);
406
+ fiobj_free(o2);
407
+ fprintf(stderr, "* passed.\n");
408
+ }
409
+ #endif