blob: 914f530eca8742ed39fc814ab6587a4514c966b5 [file] [log] [blame]
Austin Schuh3333ec72022-12-29 16:21:06 -08001/* Copyright (C) 2013-2016, The Regents of The University of Michigan.
2All rights reserved.
3This software was developed in the APRIL Robotics Lab under the
4direction of Edwin Olson, ebolson@umich.edu. This software may be
5available under alternative licensing terms; contact the address above.
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are met:
81. Redistributions of source code must retain the above copyright notice, this
9 list of conditions and the following disclaimer.
102. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation
12 and/or other materials provided with the distribution.
13THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23The views and conclusions contained in the software and documentation are those
24of the authors and should not be interpreted as representing official policies,
25either expressed or implied, of the Regents of The University of Michigan.
26*/
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <assert.h>
32
33#include "zhash.h"
34
35// force a rehash when our capacity is less than this many times the size
36#define ZHASH_FACTOR_CRITICAL 2
37
38// When resizing, how much bigger do we want to be? (should be greater than _CRITICAL)
39#define ZHASH_FACTOR_REALLOC 4
40
41struct zhash
42{
43 size_t keysz, valuesz;
44 int entrysz; // valid byte (1) + keysz + values
45
46 uint32_t(*hash)(const void *a);
47
48 // returns 1 if equal
49 int(*equals)(const void *a, const void *b);
50
51 int size; // # of items in hash table
52
53 char *entries; // each entry of size entrysz;
54 int nentries; // how many entries are allocated? Never 0.
55};
56
57zhash_t *zhash_create_capacity(size_t keysz, size_t valuesz,
58 uint32_t(*hash)(const void *a), int(*equals)(const void *a, const void*b),
59 int capacity)
60{
61 assert(hash != NULL);
62 assert(equals != NULL);
63
64 // resize...
65 int _nentries = ZHASH_FACTOR_REALLOC * capacity;
66 if (_nentries < 8)
67 _nentries = 8;
68
69 // to a power of 2.
70 int nentries = _nentries;
71 if ((nentries & (nentries - 1)) != 0) {
72 nentries = 8;
73 while (nentries < _nentries)
74 nentries *= 2;
75 }
76
77 zhash_t *zh = (zhash_t*) calloc(1, sizeof(zhash_t));
78 zh->keysz = keysz;
79 zh->valuesz = valuesz;
80 zh->hash = hash;
81 zh->equals = equals;
82 zh->nentries = nentries;
83
84 zh->entrysz = 1 + zh->keysz + zh->valuesz;
85
86 zh->entries = calloc(zh->nentries, zh->entrysz);
87 zh->nentries = nentries;
88
89 return zh;
90}
91
92zhash_t *zhash_create(size_t keysz, size_t valuesz,
93 uint32_t(*hash)(const void *a), int(*equals)(const void *a, const void *b))
94{
95 return zhash_create_capacity(keysz, valuesz, hash, equals, 8);
96}
97
98void zhash_destroy(zhash_t *zh)
99{
100 if (zh == NULL)
101 return;
102
103 free(zh->entries);
104 free(zh);
105}
106
107int zhash_size(const zhash_t *zh)
108{
109 return zh->size;
110}
111
112void zhash_clear(zhash_t *zh)
113{
114 memset(zh->entries, 0, zh->nentries * zh->entrysz);
115 zh->size = 0;
116}
117
118int zhash_get_volatile(const zhash_t *zh, const void *key, void *out_value)
119{
120 uint32_t code = zh->hash(key);
121 uint32_t entry_idx = code & (zh->nentries - 1);
122
123 while (zh->entries[entry_idx * zh->entrysz]) {
124 void *this_key = &zh->entries[entry_idx * zh->entrysz + 1];
125 if (zh->equals(key, this_key)) {
126 *((void**) out_value) = &zh->entries[entry_idx * zh->entrysz + 1 + zh->keysz];
127 return 1;
128 }
129
130 entry_idx = (entry_idx + 1) & (zh->nentries - 1);
131 }
132
133 return 0;
134}
135
136int zhash_get(const zhash_t *zh, const void *key, void *out_value)
137{
138 void *tmp;
139 if (zhash_get_volatile(zh, key, &tmp)) {
140 memcpy(out_value, tmp, zh->valuesz);
141 return 1;
142 }
143
144 return 0;
145}
146
147int zhash_put(zhash_t *zh, const void *key, const void *value, void *oldkey, void *oldvalue)
148{
149 uint32_t code = zh->hash(key);
150 uint32_t entry_idx = code & (zh->nentries - 1);
151
152 while (zh->entries[entry_idx * zh->entrysz]) {
153 void *this_key = &zh->entries[entry_idx * zh->entrysz + 1];
154 void *this_value = &zh->entries[entry_idx * zh->entrysz + 1 + zh->keysz];
155
156 if (zh->equals(key, this_key)) {
157 // replace
158 if (oldkey)
159 memcpy(oldkey, this_key, zh->keysz);
160 if (oldvalue)
161 memcpy(oldvalue, this_value, zh->valuesz);
162 memcpy(this_key, key, zh->keysz);
163 memcpy(this_value, value, zh->valuesz);
164 zh->entries[entry_idx * zh->entrysz] = 1; // mark valid
165 return 1;
166 }
167
168 entry_idx = (entry_idx + 1) & (zh->nentries - 1);
169 }
170
171 // add the entry
172 zh->entries[entry_idx * zh->entrysz] = 1;
173 memcpy(&zh->entries[entry_idx * zh->entrysz + 1], key, zh->keysz);
174 memcpy(&zh->entries[entry_idx * zh->entrysz + 1 + zh->keysz], value, zh->valuesz);
175 zh->size++;
176
177 if (zh->nentries < ZHASH_FACTOR_CRITICAL * zh->size) {
178 zhash_t *newhash = zhash_create_capacity(zh->keysz, zh->valuesz,
179 zh->hash, zh->equals,
180 zh->size);
181
182 for (int idx = 0; idx < zh->nentries; idx++) {
183
184 if (zh->entries[idx * zh->entrysz]) {
185 void *this_key = &zh->entries[idx * zh->entrysz + 1];
186 void *this_value = &zh->entries[idx * zh->entrysz + 1 + zh->keysz];
187 if (zhash_put(newhash, this_key, this_value, NULL, NULL))
188 assert(0); // shouldn't already be present.
189 }
190 }
191
192 // play switch-a-roo
193 zhash_t tmp;
194 memcpy(&tmp, zh, sizeof(zhash_t));
195 memcpy(zh, newhash, sizeof(zhash_t));
196 memcpy(newhash, &tmp, sizeof(zhash_t));
197 zhash_destroy(newhash);
198 }
199
200 return 0;
201}
202
203int zhash_remove(zhash_t *zh, const void *key, void *old_key, void *old_value)
204{
205 uint32_t code = zh->hash(key);
206 uint32_t entry_idx = code & (zh->nentries - 1);
207
208 while (zh->entries[entry_idx * zh->entrysz]) {
209 void *this_key = &zh->entries[entry_idx * zh->entrysz + 1];
210 void *this_value = &zh->entries[entry_idx * zh->entrysz + 1 + zh->keysz];
211
212 if (zh->equals(key, this_key)) {
213 if (old_key)
214 memcpy(old_key, this_key, zh->keysz);
215 if (old_value)
216 memcpy(old_value, this_value, zh->valuesz);
217
218 // mark this entry as available
219 zh->entries[entry_idx * zh->entrysz] = 0;
220 zh->size--;
221
222 // reinsert any consecutive entries that follow
223 while (1) {
224 entry_idx = (entry_idx + 1) & (zh->nentries - 1);
225
226 if (zh->entries[entry_idx * zh->entrysz]) {
227 // completely remove this entry
228 char *tmp = malloc(sizeof(char)*zh->entrysz);
229 memcpy(tmp, &zh->entries[entry_idx * zh->entrysz], zh->entrysz);
230 zh->entries[entry_idx * zh->entrysz] = 0;
231 zh->size--;
232 // reinsert it
233 if (zhash_put(zh, &tmp[1], &tmp[1+zh->keysz], NULL, NULL))
234 assert(0);
235 free(tmp);
236 } else {
237 break;
238 }
239 }
240 return 1;
241 }
242
243 entry_idx = (entry_idx + 1) & (zh->nentries - 1);
244 }
245
246 return 0;
247}
248
249zhash_t *zhash_copy(const zhash_t *zh)
250{
251 zhash_t *newhash = zhash_create_capacity(zh->keysz, zh->valuesz,
252 zh->hash, zh->equals,
253 zh->size);
254
255 for (int entry_idx = 0; entry_idx < zh->nentries; entry_idx++) {
256 if (zh->entries[entry_idx * zh->entrysz]) {
257 void *this_key = &zh->entries[entry_idx * zh->entrysz + 1];
258 void *this_value = &zh->entries[entry_idx * zh->entrysz + 1 + zh->keysz];
259 if (zhash_put(newhash, this_key, this_value, NULL, NULL))
260 assert(0); // shouldn't already be present.
261 }
262 }
263
264 return newhash;
265}
266
267int zhash_contains(const zhash_t *zh, const void *key)
268{
269 void *tmp;
270 return zhash_get_volatile(zh, key, &tmp);
271}
272
273void zhash_iterator_init(zhash_t *zh, zhash_iterator_t *zit)
274{
275 zit->zh = zh;
276 zit->czh = zh;
277 zit->last_entry = -1;
278}
279
280void zhash_iterator_init_const(const zhash_t *zh, zhash_iterator_t *zit)
281{
282 zit->zh = NULL;
283 zit->czh = zh;
284 zit->last_entry = -1;
285}
286
287int zhash_iterator_next_volatile(zhash_iterator_t *zit, void *outkey, void *outvalue)
288{
289 const zhash_t *zh = zit->czh;
290
291 while (1) {
292 if (zit->last_entry + 1 >= zh->nentries)
293 return 0;
294
295 zit->last_entry++;
296
297 if (zh->entries[zit->last_entry * zh->entrysz]) {
298 void *this_key = &zh->entries[zit->last_entry * zh->entrysz + 1];
299 void *this_value = &zh->entries[zit->last_entry * zh->entrysz + 1 + zh->keysz];
300
301 if (outkey != NULL)
302 *((void**) outkey) = this_key;
303 if (outvalue != NULL)
304 *((void**) outvalue) = this_value;
305
306 return 1;
307 }
308 }
309}
310
311int zhash_iterator_next(zhash_iterator_t *zit, void *outkey, void *outvalue)
312{
313 const zhash_t *zh = zit->czh;
314
315 void *outkeyp, *outvaluep;
316
317 if (!zhash_iterator_next_volatile(zit, &outkeyp, &outvaluep))
318 return 0;
319
320 if (outkey != NULL)
321 memcpy(outkey, outkeyp, zh->keysz);
322 if (outvalue != NULL)
323 memcpy(outvalue, outvaluep, zh->valuesz);
324
325 return 1;
326}
327
328void zhash_iterator_remove(zhash_iterator_t *zit)
329{
330 assert(zit->zh); // can't call _remove on a iterator with const zhash
331 zhash_t *zh = zit->zh;
332
333 zh->entries[zit->last_entry * zh->entrysz] = 0;
334 zh->size--;
335
336 // re-insert following entries
337 int entry_idx = (zit->last_entry + 1) & (zh->nentries - 1);
338 while (zh->entries[entry_idx *zh->entrysz]) {
339 // completely remove this entry
340 char *tmp = malloc(sizeof(char)*zh->entrysz);
341 memcpy(tmp, &zh->entries[entry_idx * zh->entrysz], zh->entrysz);
342 zh->entries[entry_idx * zh->entrysz] = 0;
343 zh->size--;
344
345 // reinsert it
346 if (zhash_put(zh, &tmp[1], &tmp[1+zh->keysz], NULL, NULL))
347 assert(0);
348 free(tmp);
349
350 entry_idx = (entry_idx + 1) & (zh->nentries - 1);
351 }
352
353 zit->last_entry--;
354}
355
356void zhash_map_keys(zhash_t *zh, void (*f)())
357{
358 assert(zh != NULL);
359 if (f == NULL)
360 return;
361
362 zhash_iterator_t itr;
363 zhash_iterator_init(zh, &itr);
364
365 void *key, *value;
366
367 while(zhash_iterator_next_volatile(&itr, &key, &value)) {
368 f(key);
369 }
370}
371
372void zhash_vmap_keys(zhash_t * zh, void (*f)())
373{
374 assert(zh != NULL);
375 if (f == NULL)
376 return;
377
378 zhash_iterator_t itr;
379 zhash_iterator_init(zh, &itr);
380
381 void *key, *value;
382
383 while(zhash_iterator_next_volatile(&itr, &key, &value)) {
384 void *p = *(void**) key;
385 f(p);
386 }
387}
388
389void zhash_map_values(zhash_t * zh, void (*f)())
390{
391 assert(zh != NULL);
392 if (f == NULL)
393 return;
394
395 zhash_iterator_t itr;
396 zhash_iterator_init(zh, &itr);
397
398 void *key, *value;
399 while(zhash_iterator_next_volatile(&itr, &key, &value)) {
400 f(value);
401 }
402}
403
404void zhash_vmap_values(zhash_t * zh, void (*f)())
405{
406 assert(zh != NULL);
407 if (f == NULL)
408 return;
409
410 zhash_iterator_t itr;
411 zhash_iterator_init(zh, &itr);
412
413 void *key, *value;
414 while(zhash_iterator_next_volatile(&itr, &key, &value)) {
415 void *p = *(void**) value;
416 f(p);
417 }
418}
419
420zarray_t *zhash_keys(const zhash_t *zh)
421{
422 assert(zh != NULL);
423
424 zarray_t *za = zarray_create(zh->keysz);
425
426 zhash_iterator_t itr;
427 zhash_iterator_init_const(zh, &itr);
428
429 void *key, *value;
430 while(zhash_iterator_next_volatile(&itr, &key, &value)) {
431 zarray_add(za, key);
432 }
433
434 return za;
435}
436
437zarray_t *zhash_values(const zhash_t *zh)
438{
439 assert(zh != NULL);
440
441 zarray_t *za = zarray_create(zh->valuesz);
442
443 zhash_iterator_t itr;
444 zhash_iterator_init_const(zh, &itr);
445
446 void *key, *value;
447 while(zhash_iterator_next_volatile(&itr, &key, &value)) {
448 zarray_add(za, value);
449 }
450
451 return za;
452}
453
454
455uint32_t zhash_uint32_hash(const void *_a)
456{
457 assert(_a != NULL);
458
459 uint32_t a = *((uint32_t*) _a);
460 return a;
461}
462
463int zhash_uint32_equals(const void *_a, const void *_b)
464{
465 assert(_a != NULL);
466 assert(_b != NULL);
467
468 uint32_t a = *((uint32_t*) _a);
469 uint32_t b = *((uint32_t*) _b);
470
471 return a==b;
472}
473
474uint32_t zhash_uint64_hash(const void *_a)
475{
476 assert(_a != NULL);
477
478 uint64_t a = *((uint64_t*) _a);
479 return (uint32_t) (a ^ (a >> 32));
480}
481
482int zhash_uint64_equals(const void *_a, const void *_b)
483{
484 assert(_a != NULL);
485 assert(_b != NULL);
486
487 uint64_t a = *((uint64_t*) _a);
488 uint64_t b = *((uint64_t*) _b);
489
490 return a==b;
491}
492
493
494union uintpointer
495{
496 const void *p;
497 uint32_t i;
498};
499
500uint32_t zhash_ptr_hash(const void *a)
501{
502 assert(a != NULL);
503
504 union uintpointer ip;
505 ip.p = * (void**)a;
506
507 // compute a hash from the lower 32 bits of the pointer (on LE systems)
508 uint32_t hash = ip.i;
509 hash ^= (hash >> 7);
510
511 return hash;
512}
513
514
515int zhash_ptr_equals(const void *a, const void *b)
516{
517 assert(a != NULL);
518 assert(b != NULL);
519
520 const void * ptra = * (void**)a;
521 const void * ptrb = * (void**)b;
522 return ptra == ptrb;
523}
524
525
526int zhash_str_equals(const void *_a, const void *_b)
527{
528 assert(_a != NULL);
529 assert(_b != NULL);
530
531 char *a = * (char**)_a;
532 char *b = * (char**)_b;
533
534 return !strcmp(a, b);
535}
536
537uint32_t zhash_str_hash(const void *_a)
538{
539 assert(_a != NULL);
540
541 char *a = * (char**)_a;
542
543 uint32_t hash = 0;
544 while (*a != 0) {
545 // optimization of hash x FNV_prime
546 hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
547 hash ^= *a;
548 a++;
549 }
550
551 return hash;
552}
553
554
555void zhash_debug(zhash_t *zh)
556{
557 for (int entry_idx = 0; entry_idx < zh->nentries; entry_idx++) {
558 char *k, *v;
559 memcpy(&k, &zh->entries[entry_idx * zh->entrysz + 1], sizeof(char*));
560 memcpy(&v, &zh->entries[entry_idx * zh->entrysz + 1 + zh->keysz], sizeof(char*));
561 printf("%d: %d, %s => %s\n", entry_idx, zh->entries[entry_idx * zh->entrysz], k, v);
562 }
563}