dsa-c
 
Loading...
Searching...
No Matches
fhashtable_template.h
Go to the documentation of this file.
1
29
34
35#ifdef __cplusplus
36#ifdef __GNUC__
37#define restrict __restrict__
38#else
39#define restrict
40#endif
41extern "C" {
42#endif
43
44#include <assert.h>
45#include <stdbool.h>
46#include <stdint.h>
47#include <stdio.h>
48#include <stdlib.h>
49
50// macro definitions: {{{
51
56#ifndef PASTE
57#define PASTE(a, b) a##b
58#endif
59
64#ifndef XPASTE
65#define XPASTE(a, b) PASTE(a, b)
66#endif
67
72#ifndef JOIN
73#define JOIN(a, b) XPASTE(a, XPASTE(_, b))
74#endif
75
83#ifndef IS_POW2
84#define IS_POW2(X) ((X) != 0 && ((X) & ((X) - 1)) == 0)
85#endif
86
91#ifndef FHASHTABLE_EMPTY_SLOT_OFFSET
92#define FHASHTABLE_EMPTY_SLOT_OFFSET (UINT32_MAX)
93#endif
94
107#ifndef FHASHTABLE_FOR_EACH
108#define FHASHTABLE_FOR_EACH(self, index, key_, value_) \
109 for ((index) = 0; (index) < (self)->capacity; (index)++) \
110 if ((self)->slots[(index)].offset != FHASHTABLE_EMPTY_SLOT_OFFSET \
111 && ((key_) = (self)->slots[(index)].key, (value_) = (self)->slots[(index)].value, true))
112#endif
113
124#ifndef FHASHTABLE_CALC_SIZEOF
125#define FHASHTABLE_CALC_SIZEOF(fhashtable_name, capacity) \
126 (uint32_t)(offsetof(struct fhashtable_name, slots) + capacity * sizeof(((struct fhashtable_name *)0)->slots[0]))
127#endif
128
139#ifndef FHASHTABLE_CALC_SIZEOF_OVERFLOWS
140#define FHASHTABLE_CALC_SIZEOF_OVERFLOWS(fhashtable_name, capacity) \
141 (capacity \
142 > (UINT32_MAX - offsetof(struct fhashtable_name, slots)) / sizeof(((struct fhashtable_name *)0)->slots[0]))
143#endif
144
152#ifndef NAME
153#error "Must define NAME."
154#else
155#define FHASHTABLE_NAME NAME
156#endif
157
165#ifndef KEY_TYPE
166#define KEY_TYPE int
167#define FUNCTION_DEFINITIONS
168#define TYPE_DEFINITIONS
169#error "Must define KEY_TYPE."
170#endif
171
179#ifndef VALUE_TYPE
180#define VALUE_TYPE int
181#error "Must define VALUE_TYPE."
182#endif
183
201#ifndef KEY_IS_EQUAL
202#error "Must define KEY_IS_EQUAL."
203#define KEY_IS_EQUAL(a, b) ((a) == (b))
204#endif
205
216#ifndef HASH_FUNCTION
217#error "Must define HASH_FUNCTION."
218#define HASH_FUNCTION(key) (0)
219#endif
220
225#ifndef FUNCTION_LINKAGE
226#define FUNCTION_LINKAGE
227#endif
228
230#define FHASHTABLE_TYPE struct FHASHTABLE_NAME
231#define FHASHTABLE_SLOT_TYPE struct JOIN(FHASHTABLE_NAME, slot)
232#define FHASHTABLE_SLOT JOIN(FHASHTABLE_NAME, slot)
233#define FHASHTABLE_INIT JOIN(FHASHTABLE_NAME, init)
234#define FHASHTABLE_IS_FULL JOIN(FHASHTABLE_NAME, is_full)
235#define FHASHTABLE_CONTAINS_KEY JOIN(FHASHTABLE_NAME, contains_key)
236#define FHASHTABLE_SWAP_SLOTS JOIN(internal, JOIN(FHASHTABLE_NAME, swap_slots))
237#define FHASHTABLE_BACKSHIFT JOIN(internal, JOIN(FHASHTABLE_NAME, backshift))
239
240// }}}
241
242// type definitions: {{{
243
248#ifdef TYPE_DEFINITIONS
249
254struct JOIN(FHASHTABLE_NAME, slot) {
255 uint32_t offset;
258};
259
264struct FHASHTABLE_NAME {
265 uint32_t count;
266 uint32_t capacity;
267 FHASHTABLE_SLOT_TYPE slots[];
268};
269
270#endif
271
272// }}}
273
274// function declarations: {{{
275
282FUNCTION_LINKAGE FHASHTABLE_TYPE *JOIN(FHASHTABLE_NAME, init)(FHASHTABLE_TYPE *self, const uint32_t pow2_capacity);
283
295FUNCTION_LINKAGE FHASHTABLE_TYPE *JOIN(FHASHTABLE_NAME, create)(const uint32_t min_capacity);
296
305FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, destroy)(FHASHTABLE_TYPE *self);
306
314FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, is_empty)(const FHASHTABLE_TYPE *self);
315
323FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, is_full)(const FHASHTABLE_TYPE *self);
324
333FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, contains_key)(const FHASHTABLE_TYPE *self, const KEY_TYPE key);
334
348FUNCTION_LINKAGE VALUE_TYPE *JOIN(FHASHTABLE_NAME, get_value_mut)(FHASHTABLE_TYPE *self, const KEY_TYPE key);
349
362FUNCTION_LINKAGE VALUE_TYPE JOIN(FHASHTABLE_NAME, get_value)(const FHASHTABLE_TYPE *self, const KEY_TYPE key,
363 VALUE_TYPE default_value);
364
378FUNCTION_LINKAGE VALUE_TYPE *JOIN(FHASHTABLE_NAME, search)(FHASHTABLE_TYPE *self, const KEY_TYPE key);
379
388FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, insert)(FHASHTABLE_TYPE *self, KEY_TYPE key, VALUE_TYPE value);
389
398FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, update)(FHASHTABLE_TYPE *self, KEY_TYPE key, VALUE_TYPE value);
399
409FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, delete)(FHASHTABLE_TYPE *self, const KEY_TYPE key);
410
416FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, clear)(FHASHTABLE_TYPE *self);
417
424FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, copy)(FHASHTABLE_TYPE *restrict dest_ptr,
425 const FHASHTABLE_TYPE *restrict src_ptr);
426
427// @}}}
428
429// function definitions: {{{
430
435#ifdef FUNCTION_DEFINITIONS
436
437#include "round_up_pow2_32.h" // round_up_pow2_32
438
439FUNCTION_LINKAGE FHASHTABLE_TYPE *JOIN(FHASHTABLE_NAME, init)(FHASHTABLE_TYPE *self, const uint32_t pow2_capacity)
440{
441 assert(self);
442 assert(IS_POW2(pow2_capacity));
443
444 self->count = 0;
445 self->capacity = pow2_capacity;
446
447 for (uint32_t i = 0; i < self->capacity; i++) {
448 self->slots[i].offset = FHASHTABLE_EMPTY_SLOT_OFFSET;
449 }
450
451 return self;
452}
453
454FUNCTION_LINKAGE FHASHTABLE_TYPE *JOIN(FHASHTABLE_NAME, create)(const uint32_t min_capacity)
455{
456 if (min_capacity == 0 || min_capacity > UINT32_MAX / 2 + 1) {
457 return NULL;
458 }
459
460 const uint32_t capacity = round_up_pow2_32(min_capacity);
461
462 if (FHASHTABLE_CALC_SIZEOF_OVERFLOWS(FHASHTABLE_NAME, capacity)) {
463 return NULL;
464 }
465
466 const uint32_t size = FHASHTABLE_CALC_SIZEOF(FHASHTABLE_NAME, capacity);
467
468 FHASHTABLE_TYPE *self = (FHASHTABLE_TYPE *)calloc(1, size);
469
470 if (!self) {
471 return NULL;
472 }
473
474 FHASHTABLE_INIT(self, capacity);
475
476 return self;
477}
478
479FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, destroy)(FHASHTABLE_TYPE *self)
480{
481 assert(self);
482
483 free(self);
484}
485
486FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, is_empty)(const FHASHTABLE_TYPE *self)
487{
488 assert(self != NULL);
489
490 return self->count == 0;
491}
492
493FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, is_full)(const FHASHTABLE_TYPE *self)
494{
495 assert(self != NULL);
496
497 return self->count == self->capacity;
498}
499
500FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, contains_key)(const FHASHTABLE_TYPE *self, const KEY_TYPE key)
501{
502 assert(self != NULL);
503
504 const uint32_t key_hash = HASH_FUNCTION(key);
505 const uint32_t index_mask = self->capacity - 1;
506
507 uint32_t index = key_hash & index_mask;
508 uint32_t max_possible_offset = 0;
509
510 while (true) {
511 const bool not_empty = self->slots[index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
512
513 const bool below_max = max_possible_offset <= self->slots[index].offset;
514
515 if (!(not_empty && below_max)) {
516 break;
517 }
518
519 if (KEY_IS_EQUAL(self->slots[index].key, key)) {
520 return true;
521 }
522
523 index++;
524 index &= index_mask;
525 max_possible_offset++;
526 }
527 return false;
528}
529
530FUNCTION_LINKAGE VALUE_TYPE *JOIN(FHASHTABLE_NAME, get_value_mut)(FHASHTABLE_TYPE *self, const KEY_TYPE key)
531{
532 assert(self != NULL);
533
534 const uint32_t key_hash = HASH_FUNCTION(key);
535 const uint32_t index_mask = self->capacity - 1;
536
537 uint32_t index = key_hash & index_mask;
538 uint32_t max_possible_offset = 0;
539
540 while (true) {
541 const bool not_empty = self->slots[index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
542
543 const bool below_max = max_possible_offset <= self->slots[index].offset;
544
545 if (!(not_empty && below_max)) {
546 break;
547 }
548
549 if (KEY_IS_EQUAL(self->slots[index].key, key)) {
550 return &self->slots[index].value;
551 }
552
553 index++;
554 index &= index_mask;
555 max_possible_offset++;
556 }
557 return NULL;
558}
559
560FUNCTION_LINKAGE VALUE_TYPE JOIN(FHASHTABLE_NAME, get_value)(const FHASHTABLE_TYPE *self, const KEY_TYPE key,
561 VALUE_TYPE default_value)
562{
563 assert(self != NULL);
564
565 const uint32_t key_hash = HASH_FUNCTION(key);
566 const uint32_t index_mask = self->capacity - 1;
567
568 uint32_t index = key_hash & index_mask;
569 uint32_t max_possible_offset = 0;
570
571 while (true) {
572 const bool not_empty = self->slots[index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
573
574 const bool below_max = max_possible_offset <= self->slots[index].offset;
575
576 if (!(not_empty && below_max)) {
577 break;
578 }
579
580 if (KEY_IS_EQUAL(self->slots[index].key, key)) {
581 return self->slots[index].value;
582 }
583
584 index++;
585 index &= index_mask;
586 max_possible_offset++;
587 }
588 return default_value;
589}
590
591FUNCTION_LINKAGE VALUE_TYPE *JOIN(FHASHTABLE_NAME, search)(FHASHTABLE_TYPE *self, const KEY_TYPE key)
592{
593 return JOIN(FHASHTABLE_NAME, get_value_mut)(self, key);
594}
595
597static inline void JOIN(internal, JOIN(FHASHTABLE_NAME, swap_slots))(FHASHTABLE_SLOT_TYPE *a, FHASHTABLE_SLOT_TYPE *b)
598{
599 FHASHTABLE_SLOT_TYPE temp = *a;
600 *a = *b;
601 *b = temp;
602}
604
605FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, insert)(FHASHTABLE_TYPE *self, KEY_TYPE key, VALUE_TYPE value)
606{
607 assert(self != NULL);
608 assert(FHASHTABLE_CONTAINS_KEY(self, key) == false);
609
610 const uint32_t index_mask = self->capacity - 1;
611 const uint32_t key_hash = HASH_FUNCTION(key);
612
613 uint32_t index = key_hash & index_mask;
614 FHASHTABLE_SLOT_TYPE current_slot = {.offset = 0, .key = key, .value = value};
615
616 while (true) {
617 const bool not_empty = self->slots[index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
618
619 if (!not_empty) {
620 break;
621 }
622
623 if (current_slot.offset > self->slots[index].offset) {
624 FHASHTABLE_SWAP_SLOTS(&self->slots[index], &current_slot);
625 }
626
627 index++;
628 index &= index_mask;
629 current_slot.offset++;
630 }
631 self->slots[index] = current_slot;
632 self->count++;
633}
634
635FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, update)(FHASHTABLE_TYPE *self, KEY_TYPE key, VALUE_TYPE value)
636{
637 assert(self != NULL);
638
639 const uint32_t index_mask = self->capacity - 1;
640 const uint32_t key_hash = HASH_FUNCTION(key);
641
642 uint32_t index = key_hash & index_mask;
643 FHASHTABLE_SLOT_TYPE current_slot = {.offset = 0, .key = key, .value = value};
644
645 while (true) {
646 const bool not_empty = self->slots[index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
647
648 if (!not_empty) {
649 break;
650 }
651
652 const bool offset_is_same = current_slot.offset == self->slots[index].offset;
653
654 const bool key_is_equal = KEY_IS_EQUAL(current_slot.key, self->slots[index].key);
655
656 if (offset_is_same && key_is_equal) {
657 self->slots[index].value = current_slot.value;
658 return;
659 }
660
661 if (current_slot.offset > self->slots[index].offset) {
662 FHASHTABLE_SWAP_SLOTS(&current_slot, &self->slots[index]);
663 }
664
665 index++;
666 index &= index_mask;
667 current_slot.offset++;
668 }
669
670 self->slots[index] = current_slot;
671 self->count++;
672}
673
675static inline void JOIN(internal, JOIN(FHASHTABLE_NAME, backshift))(FHASHTABLE_TYPE *self, const uint32_t index_mask,
676 uint32_t index)
677{
678 assert(self);
679
680 uint32_t next_index = (index + 1) & index_mask;
681
682 while (true) {
683 const bool not_empty = self->slots[next_index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
684
685 const bool offset_is_non_zero = self->slots[next_index].offset > 0;
686
687 if (!(not_empty && offset_is_non_zero)) {
688 break;
689 }
690
691 self->slots[index] = self->slots[next_index];
692 self->slots[index].offset--;
693
694 self->slots[next_index].offset = FHASHTABLE_EMPTY_SLOT_OFFSET;
695
696 index = next_index;
697 next_index = (index + 1) & index_mask;
698 }
699}
701
702FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, delete)(FHASHTABLE_TYPE *self, const KEY_TYPE key)
703{
704 assert(self != NULL);
705
706 const uint32_t index_mask = self->capacity - 1;
707 const uint32_t key_hash = HASH_FUNCTION(key);
708
709 uint32_t index = key_hash & index_mask;
710 uint32_t max_possible_offset = 0;
711
712 while (true) {
713 const bool not_empty = self->slots[index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
714
715 const bool below_max = max_possible_offset <= self->slots[index].offset;
716
717 if (!(not_empty && below_max)) {
718 break;
719 }
720
721 const bool key_is_equal = KEY_IS_EQUAL(key, self->slots[index].key);
722
723 if (!key_is_equal) {
724 index = (index + 1) & index_mask;
725 max_possible_offset++;
726 continue;
727 }
728
729 self->slots[index].offset = FHASHTABLE_EMPTY_SLOT_OFFSET;
730 self->count--;
731
732 FHASHTABLE_BACKSHIFT(self, index_mask, index);
733
734 return true;
735 }
736 return false;
737}
738
739FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, clear)(FHASHTABLE_TYPE *self)
740{
741 assert(self != NULL);
742
743 for (uint32_t i = 0; i < self->capacity; i++) {
744 self->slots[i].offset = FHASHTABLE_EMPTY_SLOT_OFFSET;
745 }
746 self->count = 0;
747}
748
749FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, copy)(FHASHTABLE_TYPE *restrict dest_ptr,
750 const FHASHTABLE_TYPE *restrict src_ptr)
751{
752 assert(src_ptr != NULL);
753 assert(dest_ptr != NULL);
754 assert(src_ptr->capacity <= dest_ptr->capacity);
755 assert(dest_ptr->count == 0);
756
757 uint32_t index;
758 KEY_TYPE key;
759 VALUE_TYPE value;
760 FHASHTABLE_FOR_EACH(src_ptr, index, key, value) {
761 JOIN(FHASHTABLE_NAME, insert)(dest_ptr, key, value);
762 }
763}
764
765#endif
766
767// }}}
768
769// macro undefs: {{{
770
771#undef NAME
772#undef KEY_TYPE
773#undef VALUE_TYPE
774#undef KEY_IS_EQUAL
775#undef HASH_FUNCTION
776#undef FUNCTION_LINKAGE
777#undef FUNCTION_DEFINITIONS
778#undef TYPE_DEFINITIONS
779
780#undef FHASHTABLE_NAME
781#undef FHASHTABLE_TYPE
782#undef FHASHTABLE_SLOT_TYPE
783#undef FHASHTABLE_INIT
784#undef FHASHTABLE_IS_FULL
785#undef FHASHTABLE_CONTAINS_KEY
786#undef FHASHTABLE_CALC_SIZEOF
787#undef FHASHTABLE_SWAP_SLOTS
788#undef FHASHTABLE_BACKSHIFT
789
790// }}}
791
792#ifdef __cplusplus
793}
794#endif
795
796// vim: ft=c fdm=marker
#define FHASHTABLE_CALC_SIZEOF(fhashtable_name, capacity)
Calculate the size of the hashtable struct. No overflow checks.
Definition fhashtable_template.h:125
#define JOIN(a, b)
First expand tokens, then paste them together with a _ in between.
Definition fhashtable_template.h:73
#define HASH_FUNCTION(key)
Used to compute indicies of keys. This must be manually defined before including this header file.
Definition fhashtable_template.h:218
#define IS_POW2(X)
Macro to check if a number is a power of two.
Definition fhashtable_template.h:84
#define KEY_IS_EQUAL(a, b)
Used to compare two keys This must be manually defined before including this header file.
Definition fhashtable_template.h:203
#define FHASHTABLE_CALC_SIZEOF_OVERFLOWS(fhashtable_name, capacity)
Check for a given capacity, if the equivalent size of the hashtable struct overflows.
Definition fhashtable_template.h:140
#define FHASHTABLE_EMPTY_SLOT_OFFSET
Offset constant used to flag empty slots.
Definition fhashtable_template.h:92
#define FHASHTABLE_FOR_EACH(self, index, key_, value_)
Iterate over the non-empty slots in the hashtable in arbitary order.
Definition fhashtable_template.h:108
#define VALUE_TYPE
The value type. This must be manually defined before including this header file.
Definition fhashtable_template.h:180
#define KEY_TYPE
The key type. This must be manually defined before including this header file.
Definition fhashtable_template.h:166
#define FUNCTION_LINKAGE
Specify function linkage e.g. static inline.
Definition fstack_template.h:146
#define VALUE_TYPE
Stack value type. This must be manually defined before including this header file.
Definition fstack_template.h:135
uint32_t offset
Offset from the ideal slot index.
Definition fhashtable_template.h:255
VALUE_TYPE value
The value in this slot.
Definition fhashtable_template.h:257
KEY_TYPE key
The key in this slot.
Definition fhashtable_template.h:256
fhashtable_slot_type slots[]
Array of slots.
Definition fhashtable_template.h:267
uint32_t count
Number of non-empty slots.
Definition fhashtable_template.h:265
uint32_t capacity
Number of slots.
Definition fhashtable_template.h:266