libacfutils
A general purpose library of utility functions designed to make it easier to develop addons for the X-Plane flight simulator.
Loading...
Searching...
No Matches
htbl.c
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license in the file COPYING
10 * or http://www.opensource.org/licenses/CDDL-1.0.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file COPYING.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2023 Saso Kiselkov. All rights reserved.
24 */
25
26#include <stdlib.h>
27#include <string.h>
28#include <stddef.h>
29
30#include "acfutils/crc64.h"
31#include "acfutils/helpers.h"
32#define __INCLUDED_FROM_HTBL_C__
33#include "acfutils/htbl.h"
34
35typedef struct {
36 list_node_t bucket_node;
37 size_t value_sz;
38 union {
39 void *value;
40 list_t multi;
41 };
42 uint8_t key[1]; /* variable length, depends on htbl->key_sz */
44
46 void *value;
47 list_node_t node;
49};
50
51static inline uint64_t
52H(const void *p, size_t l)
53{
54 return (crc64(p, l));
55}
56
57static inline void
58htbl2_check_key_type(const htbl2_t REQ_PTR(htbl), size_t key_sz)
59{
60 ASSERT_MSG(key_sz == htbl->h.key_sz,
61 "Invalid hash table operation with key_sz %x, whereas the "
62 "hash table was created with key_sz %x. This most likely "
63 "indicates a data type error.",
64 (unsigned)key_sz, (unsigned)htbl->h.key_sz);
65}
66
67static inline void
68htbl2_check_value_type(size_t htbl_value_sz, size_t value_sz)
69{
70 ASSERT_MSG(value_sz == htbl_value_sz,
71 "Invalid hash table operation with value_sz %x, whereas "
72 "the hash table was created with value_sz %x. This most "
73 "likely indicates a data type error.",
74 (unsigned)value_sz, (unsigned)htbl_value_sz);
75}
76
77static inline void
78htbl2_check_types(const htbl2_t REQ_PTR(htbl), size_t key_sz, size_t value_sz)
79{
80 htbl2_check_key_type(htbl, key_sz);
81 htbl2_check_value_type(htbl->value_sz, value_sz);
82}
83
84static void
85htbl_create_impl(htbl_t *htbl, size_t tbl_sz, size_t key_sz, bool multi_value)
86{
87 ASSERT(htbl != NULL);
88 ASSERT(key_sz != 0);
89 ASSERT(tbl_sz != 0);
90 ASSERT(tbl_sz <= ((size_t)1 << ((sizeof(size_t) * 8) - 1)));
91
92 memset(htbl, 0, sizeof (*htbl));
93 /* round table size up to nearest multiple of 2 */
94 htbl->tbl_sz = P2ROUNDUP(tbl_sz);
95 htbl->buckets = safe_malloc(sizeof (*htbl->buckets) * htbl->tbl_sz);
96 ASSERT(htbl->buckets != NULL);
97 for (size_t i = 0; i < htbl->tbl_sz; i++) {
98 list_create(&htbl->buckets[i], sizeof (htbl_bucket_item_t),
99 offsetof(htbl_bucket_item_t, bucket_node));
100 }
101 htbl->key_sz = key_sz;
102 htbl->multi_value = multi_value;
103}
104
126void
127htbl_create(htbl_t REQ_PTR(htbl), size_t tbl_sz, size_t key_sz,
128 bool_t multi_value)
129{
130 htbl_create_impl(htbl, tbl_sz, key_sz, multi_value);
131}
132
133void
134htbl2_create(htbl2_t REQ_PTR(htbl), size_t tbl_sz, size_t key_sz,
135 size_t value_sz, bool multi_value)
136{
137 htbl_create_impl(&htbl->h, tbl_sz, key_sz, multi_value);
138 htbl->value_sz = value_sz;
139}
140
153void
155{
156 ASSERT(htbl != NULL);
157 ASSERT(htbl->num_values == 0);
158 ASSERT(htbl->buckets != NULL);
159 for (size_t i = 0; i < htbl->tbl_sz; i++)
160 list_destroy(&htbl->buckets[i]);
161 free(htbl->buckets);
162}
163
164void
165htbl2_destroy(htbl2_t REQ_PTR(htbl))
166{
167 htbl_destroy(&htbl->h);
168}
169
170static void
171htbl_empty_multi_item(htbl_bucket_item_t *item, void (*func)(void *, void *),
172 void *arg)
173{
174 for (htbl_multi_value_t *mv = list_head(&item->multi); mv;
175 mv = list_head(&item->multi)) {
176 if (func != NULL)
177 func(mv->value, arg);
178 list_remove_head(&item->multi);
179 free(mv);
180 }
181 list_destroy(&item->multi);
182}
183
202void
203htbl_empty(htbl_t REQ_PTR(htbl), void (*func)(void *value, void *userinfo),
204 void *userinfo)
205{
206 if (htbl->num_values == 0)
207 return;
208 for (size_t i = 0; i < htbl->tbl_sz; i++) {
209 for (htbl_bucket_item_t *item = list_head(&htbl->buckets[i]);
210 item; item = list_head(&htbl->buckets[i])) {
211 if (htbl->multi_value)
212 htbl_empty_multi_item(item, func, userinfo);
213 else if (func != NULL)
214 func(item->value, userinfo);
215 list_remove_head(&htbl->buckets[i]);
216 free(item);
217 }
218 }
219 htbl->num_values = 0;
220}
221
222void
223htbl2_empty(htbl2_t REQ_PTR(htbl), size_t value_sz,
224 void (*func)(void *value, void *userinfo), void *userinfo)
225{
226 htbl2_check_value_type(htbl->value_sz, value_sz);
227 htbl_empty(&htbl->h, func, userinfo);
228}
229
233size_t
234htbl_count(const htbl_t *htbl)
235{
236 ASSERT(htbl != NULL);
237 return (htbl->num_values);
238}
239
240size_t
241htbl2_count(const htbl2_t *htbl)
242{
243 ASSERT(htbl != NULL);
244 return (htbl->h.num_values);
245}
246
247static void
248htbl_multi_value_add(htbl_t *htbl, htbl_bucket_item_t *item, void *value)
249{
250 htbl_multi_value_t *mv = safe_malloc(sizeof (*mv));
251 ASSERT(htbl->multi_value);
252 mv->value = value;
253 mv->item = item;
254 list_insert_head(&item->multi, mv);
255 htbl->num_values++;
256}
257
258static void
259htbl_set_impl(htbl_t REQ_PTR(htbl), const void *key, void *value,
260 size_t value_sz)
261{
262 ASSERT(key != NULL);
263
264 list_t *bucket = &htbl->buckets[H(key, htbl->key_sz) &
265 (htbl->tbl_sz - 1)];
266 htbl_bucket_item_t *item;
267
268 ASSERT(key != NULL);
269 ASSERT(value != NULL);
270 for (item = list_head(bucket); item; item = list_next(bucket, item)) {
271 if (memcmp(item->key, key, htbl->key_sz) == 0) {
272 if (htbl->multi_value)
273 htbl_multi_value_add(htbl, item, value);
274 else
275 item->value = value;
276 return;
277 }
278 }
279 item = safe_calloc(sizeof (*item) + htbl->key_sz - 1, 1);
280 item->value_sz = value_sz;
281 memcpy(item->key, key, htbl->key_sz);
282 if (htbl->multi_value) {
283 list_create(&item->multi, sizeof (htbl_multi_value_t),
284 offsetof(htbl_multi_value_t, node));
285 htbl_multi_value_add(htbl, item, value);
286 } else {
287 item->value = value;
288 }
289 list_insert_head(bucket, item);
290 htbl->num_values++;
291}
292
312void
313htbl_set(htbl_t REQ_PTR(htbl), const void *key, void *value)
314{
315 htbl_set_impl(htbl, key, value, SIZE_MAX);
316}
317
318void
319htbl2_set(htbl2_t REQ_PTR(htbl), const void *key, size_t key_sz,
320 void *value, size_t value_sz)
321{
322 htbl2_check_types(htbl, key_sz, value_sz);
323 htbl_set_impl(&htbl->h, key, value, value_sz);
324}
325
340void
341htbl_remove(htbl_t REQ_PTR(htbl), const void *key, bool_t nil_ok)
342{
343 ASSERT(htbl != NULL);
344 ASSERT(key != NULL);
345
346 list_t *bucket = &htbl->buckets[H(key, htbl->key_sz) &
347 (htbl->tbl_sz - 1)];
348 htbl_bucket_item_t *item;
349
350 for (item = list_head(bucket); item; item = list_next(bucket, item)) {
351 if (memcmp(item->key, key, htbl->key_sz) == 0) {
352 list_remove(bucket, item);
353 if (htbl->multi_value) {
354 htbl_empty_multi_item(item, NULL, NULL);
355 ASSERT3U(htbl->num_values, >=,
356 list_count(&item->multi));
357 htbl->num_values -= list_count(&item->multi);
358 } else {
359 free(item);
360 ASSERT(htbl->num_values != 0);
361 htbl->num_values--;
362 }
363 return;
364 }
365 }
366 ASSERT(nil_ok);
367}
368
369void
370htbl2_remove(htbl2_t REQ_PTR(htbl), const void *key, size_t key_sz,
371 bool nil_ok)
372{
373 htbl2_check_key_type(htbl, key_sz);
374 htbl_remove(&htbl->h, key, nil_ok);
375}
376
401void
402htbl_remove_multi(htbl_t REQ_PTR(htbl), const void *key,
403 htbl_multi_value_t *list_item)
404{
405 ASSERT(htbl != NULL);
406 ASSERT(htbl->multi_value);
407 ASSERT(htbl->num_values != 0);
408 ASSERT(list_item != NULL);
409 ASSERT(key != NULL);
410
411 htbl_bucket_item_t *item = list_item->item;
412 ASSERT(item != NULL);
413
414 list_remove(&item->multi, list_item);
415 htbl->num_values--;
416 free(list_item);
417 if (list_count(&item->multi) == 0) {
418 list_t *bucket =
419 &htbl->buckets[H(key, htbl->key_sz) & (htbl->tbl_sz - 1)];
420 list_remove(bucket, item);
421 list_destroy(&item->multi);
422 free(item);
423 }
424}
425
426void
427htbl2_remove_multi(htbl2_t REQ_PTR(htbl), const void *key, size_t key_sz,
428 htbl2_multi_value_t *list_item)
429{
430 htbl2_check_key_type(htbl, key_sz);
431 htbl_remove_multi(&htbl->h, key, list_item);
432}
433
434static htbl_bucket_item_t *
435htbl_lookup_common(const htbl_t *htbl, const void *key)
436{
437 ASSERT(htbl != NULL);
438 ASSERT(key != NULL);
439
440 list_t *bucket = &htbl->buckets[H(key, htbl->key_sz) &
441 (htbl->tbl_sz - 1)];
442 htbl_bucket_item_t *item;
443
444 for (item = list_head(bucket); item; item = list_next(bucket, item)) {
445 if (memcmp(item->key, key, htbl->key_sz) == 0)
446 return (item);
447 }
448 return (NULL);
449}
450
461void *
462htbl_lookup(const htbl_t REQ_PTR(htbl), const void *key)
463{
464 htbl_bucket_item_t *item;
465 ASSERT(!htbl->multi_value);
466 item = htbl_lookup_common(htbl, key);
467 return (item != NULL ? item->value : NULL);
468}
469
470void *
471htbl2_lookup(const htbl2_t REQ_PTR(htbl), const void *key,
472 size_t key_sz, size_t value_sz)
473{
474 htbl2_check_types(htbl, key_sz, value_sz);
475 return (htbl_lookup(&htbl->h, key));
476}
477
491const list_t *
492htbl_lookup_multi(const htbl_t REQ_PTR(htbl), const void *key)
493{
494 htbl_bucket_item_t *item;
495 ASSERT(htbl->multi_value);
496 item = htbl_lookup_common(htbl, key);
497 return (item != NULL ? &item->multi : NULL);
498}
499
500const list_t *
501htbl2_lookup_multi(const htbl2_t REQ_PTR(htbl), const void *key,
502 size_t key_sz)
503{
504 htbl2_check_key_type(htbl, key_sz);
505 return (htbl_lookup_multi(&htbl->h, key));
506}
507
520void
522 void (*func)(const void *key, void *value, void *userinfo), void *userinfo)
523{
524 ASSERT(htbl != NULL);
525 ASSERT(func != NULL);
526 for (size_t i = 0; i < htbl->tbl_sz; i++) {
527 list_t *bucket = &htbl->buckets[i];
528 for (const htbl_bucket_item_t *item = list_head(bucket),
529 *next_item = NULL; item; item = next_item) {
530 /*
531 * To support current value removal in the callback,
532 * retrieve the next pointer right now.
533 */
534 next_item = list_next(bucket, item);
535 if (htbl->multi_value) {
536 const list_t *ml = &item->multi;
537 for (htbl_multi_value_t *mv = list_head(ml),
538 *mv_next = NULL; mv != NULL; mv = mv_next) {
539 mv_next = list_next(ml, mv);
540 func(item->key, mv->value, userinfo);
541 }
542 } else {
543 func(item->key, item->value, userinfo);
544 }
545 }
546 }
547}
548
549void
550htbl2_foreach(const htbl2_t REQ_PTR(htbl),
551 size_t key_sz, size_t value_sz,
552 void (*func)(const void *key, void *value, void *userinfo), void *userinfo)
553{
554 htbl2_check_types(htbl, key_sz, value_sz);
555 htbl_foreach(&htbl->h, func, userinfo);
556}
557
577void *
578htbl_value_multi(const htbl_multi_value_t *mv)
579{
580 ASSERT(mv != NULL);
581 // To check against accidentally crossing over from htbl2_t
582 htbl2_check_value_type(mv->item->value_sz, SIZE_MAX);
583 return (mv->value);
584}
585
586void *
587htbl2_value_multi(const htbl2_multi_value_t *mv, size_t value_sz)
588{
589 ASSERT(mv != NULL);
590 htbl2_check_value_type(mv->item->value_sz, value_sz);
591 return (mv->value);
592}
593
601char *
602htbl_dump(const htbl_t *htbl, bool_t printable_keys)
603{
604 char *result = NULL;
605 size_t result_sz = 0;
606
607 append_format(&result, &result_sz, "(%lu){\n",
608 (long unsigned)htbl->num_values);
609 for (size_t i = 0; i < htbl->tbl_sz; i++) {
610 list_t *bucket = &htbl->buckets[i];
611 append_format(&result, &result_sz, " [%lu] =",
612 (long unsigned)i);
613 if (list_head(bucket) == NULL)
614 append_format(&result, &result_sz, " <empty>");
615 for (const htbl_bucket_item_t *item = list_head(bucket); item;
616 item = list_next(bucket, item)) {
617 if (printable_keys) {
618 append_format(&result, &result_sz, " (%s) ",
619 item->key);
620 } else {
621 append_format(&result, &result_sz, " (#BIN)");
622 }
623 }
624 append_format(&result, &result_sz, "\n");
625 }
626 append_format(&result, &result_sz, "}");
627
628 return (result);
629}
#define ASSERT_MSG(x, fmt,...)
Definition assert.h:214
#define ASSERT3U(x, op, y)
Definition assert.h:210
#define ASSERT(x)
Definition assert.h:208
API_EXPORT uint64_t crc64(const void *input, size_t sz)
Definition crc64.c:65
void append_format(char **str, size_t *sz, const char *format,...)
Definition helpers.c:731
#define P2ROUNDUP(x)
Definition helpers.h:736
void htbl_foreach(const htbl_t *htbl, void(*func)(const void *key, void *value, void *userinfo), void *userinfo)
Definition htbl.c:521
void htbl_empty(htbl_t *htbl, void(*func)(void *value, void *userinfo), void *userinfo)
Definition htbl.c:203
void htbl_remove_multi(htbl_t *htbl, const void *key, htbl_multi_value_t *list_item)
Definition htbl.c:402
void htbl_set(htbl_t *htbl, const void *key, void *value)
Definition htbl.c:313
const list_t * htbl_lookup_multi(const htbl_t *htbl, const void *key)
Definition htbl.c:492
void htbl_create(htbl_t *htbl, size_t tbl_sz, size_t key_sz, bool_t multi_value)
Definition htbl.c:127
size_t htbl_count(const htbl_t *htbl)
Definition htbl.c:234
void htbl_remove(htbl_t *htbl, const void *key, bool_t nil_ok)
Definition htbl.c:341
void * htbl_value_multi(const htbl_multi_value_t *mv)
Definition htbl.c:578
char * htbl_dump(const htbl_t *htbl, bool_t printable_keys)
Definition htbl.c:602
void * htbl_lookup(const htbl_t *htbl, const void *key)
Definition htbl.c:462
void htbl_destroy(htbl_t *htbl)
Definition htbl.c:154
void list_destroy(list_t *)
Definition list.c:136
void * list_head(const list_t *)
Definition list.c:292
void list_create(list_t *, size_t, size_t)
Definition list.c:113
void list_insert_head(list_t *, void *)
Definition list.c:199
void * list_next(const list_t *, const void *)
Definition list.c:344
size_t list_count(const list_t *)
Definition list.c:543
void list_remove(list_t *, void *)
Definition list.c:226
void * list_remove_head(list_t *)
Definition list.c:251
static void * safe_calloc(size_t nmemb, size_t size)
Definition safe_alloc.h:71
static void * safe_malloc(size_t size)
Definition safe_alloc.h:56
Definition htbl.h:70
Definition htbl.h:62
#define REQ_PTR(x)
Definition sysmacros.h:334