data-structures-c
Loading...
Searching...
No Matches
fhashtable_template.h
Go to the documentation of this file.
1// Copyright (c) 2026 abxh
2// SPDX-License-Identifier: MIT
3
34
39
40#ifdef __cplusplus
41#ifdef __GNUC__
42#define restrict __restrict__
43#else
44#define restrict
45#endif
46extern "C" {
47#endif
48
49#include <stdbool.h>
50#include <stddef.h>
51#include <stdint.h>
52
53// macro definitions: {{{
54
59#ifndef PASTE
60#define PASTE(a, b) a##b
61#endif
62
67#ifndef XPASTE
68#define XPASTE(a, b) PASTE(a, b)
69#endif
70
75#ifndef JOIN
76#define JOIN(a, b) XPASTE(a, XPASTE(_, b))
77#endif
78
86#ifndef IS_POW2
87#define IS_POW2(X) ((X) != 0 && ((X) & ((X) - 1)) == 0)
88#endif
89
94#ifndef FHASHTABLE_EMPTY_SLOT_OFFSET
95#define FHASHTABLE_EMPTY_SLOT_OFFSET (UINT32_MAX)
96#endif
97
110#ifndef FHASHTABLE_FOR_EACH
111#define FHASHTABLE_FOR_EACH(self, index, key_, value_) \
112 for ((index) = 0; (index) < (self)->capacity; (index)++) \
113 if ((self)->slots[(index)].offset != FHASHTABLE_EMPTY_SLOT_OFFSET \
114 && ((key_) = (self)->slots[(index)].key, (value_) = (self)->slots[(index)].value, true))
115#endif
116
127#ifndef FHASHTABLE_CALC_SIZEOF
128#define FHASHTABLE_CALC_SIZEOF(fhashtable_name, capacity) \
129 (uint32_t)(offsetof(struct fhashtable_name, slots) + capacity * sizeof(((struct fhashtable_name *)0)->slots[0]))
130#endif
131
142#ifndef FHASHTABLE_CALC_SIZEOF_OVERFLOWS
143#define FHASHTABLE_CALC_SIZEOF_OVERFLOWS(fhashtable_name, capacity) \
144 (capacity \
145 > (UINT32_MAX - offsetof(struct fhashtable_name, slots)) / sizeof(((struct fhashtable_name *)0)->slots[0]))
146#endif
147
155#ifndef NAME
156#error "Must define NAME."
157#else
158#define FHASHTABLE_NAME NAME
159#endif
160
168#ifndef KEY_TYPE
169#define KEY_TYPE int
170#define FUNCTION_DEFINITIONS
171#define TYPE_DEFINITIONS
172#error "Must define KEY_TYPE."
173#endif
174
182#ifndef VALUE_TYPE
183#define VALUE_TYPE int
184#error "Must define VALUE_TYPE."
185#endif
186
191#ifndef FUNCTION_LINKAGE
192#define FUNCTION_LINKAGE
193#endif
194
196#define FHASHTABLE_TYPE struct FHASHTABLE_NAME
197#define FHASHTABLE_SLOT_TYPE struct JOIN(FHASHTABLE_NAME, slot)
198#define FHASHTABLE_SLOT JOIN(FHASHTABLE_NAME, slot)
199#define FHASHTABLE_INIT JOIN(FHASHTABLE_NAME, init)
200#define FHASHTABLE_IS_FULL JOIN(FHASHTABLE_NAME, is_full)
201#define FHASHTABLE_CONTAINS_KEY JOIN(FHASHTABLE_NAME, contains_key)
202#define FHASHTABLE_SWAP_SLOTS JOIN(internal, JOIN(FHASHTABLE_NAME, swap_slots))
203#define FHASHTABLE_BACKSHIFT JOIN(internal, JOIN(FHASHTABLE_NAME, backshift))
205
206// }}}
207
208// type definitions: {{{
209
210struct JOIN(FHASHTABLE_NAME, slot);
211struct FHASHTABLE_NAME;
212
217#ifdef TYPE_DEFINITIONS
218
223struct JOIN(FHASHTABLE_NAME, slot) {
224 uint32_t offset;
227};
228
233struct FHASHTABLE_NAME {
234 uint32_t count;
235 uint32_t capacity;
236 FHASHTABLE_SLOT_TYPE slots[];
237};
238
239#endif
240
241// }}}
242
243// function declarations: {{{
244
251FUNCTION_LINKAGE FHASHTABLE_TYPE *JOIN(FHASHTABLE_NAME, init)(FHASHTABLE_TYPE *self, const uint32_t pow2_capacity);
252
266FUNCTION_LINKAGE FHASHTABLE_TYPE *
267 JOIN(FHASHTABLE_NAME, create_custom)(const uint32_t min_capacity, void *context_ptr,
268 void *(*allocate)(void *context_ptr, size_t alignment, size_t size));
269
281FUNCTION_LINKAGE FHASHTABLE_TYPE *JOIN(FHASHTABLE_NAME, create)(const uint32_t min_capacity);
282
293FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, destroy_custom)(FHASHTABLE_TYPE *self, void *context_ptr,
294 void (*deallocate)(void *context_ptr, void *mem));
295
304FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, destroy)(FHASHTABLE_TYPE *self);
305
313FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, is_empty)(const FHASHTABLE_TYPE *self);
314
322FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, is_full)(const FHASHTABLE_TYPE *self);
323
332FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, contains_key)(const FHASHTABLE_TYPE *self, const KEY_TYPE key);
333
347FUNCTION_LINKAGE VALUE_TYPE *JOIN(FHASHTABLE_NAME, get_value_mut)(FHASHTABLE_TYPE *self, const KEY_TYPE key);
348
361FUNCTION_LINKAGE VALUE_TYPE JOIN(FHASHTABLE_NAME, get_value)(const FHASHTABLE_TYPE *self, const KEY_TYPE key,
362 VALUE_TYPE default_value);
363
377FUNCTION_LINKAGE VALUE_TYPE *JOIN(FHASHTABLE_NAME, search)(FHASHTABLE_TYPE *self, const KEY_TYPE key);
378
387FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, insert)(FHASHTABLE_TYPE *self, KEY_TYPE key, VALUE_TYPE value);
388
397FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, update)(FHASHTABLE_TYPE *self, KEY_TYPE key, VALUE_TYPE value);
398
408FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, delete)(FHASHTABLE_TYPE *self, const KEY_TYPE key);
409
415FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, clear)(FHASHTABLE_TYPE *self);
416
423FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, copy)(FHASHTABLE_TYPE *restrict dest_ptr,
424 const FHASHTABLE_TYPE *restrict src_ptr);
425
426// @}}}
427
428// function definitions: {{{
429
434#ifdef FUNCTION_DEFINITIONS
435
436#include <assert.h>
437#include <stdalign.h>
438#include <stdlib.h>
439#include <string.h>
440
441#include "round_up_pow2_32.h" // round_up_pow2_32
442
460#ifndef KEY_IS_EQUAL
461#error "Must define KEY_IS_EQUAL."
462#define KEY_IS_EQUAL(a, b) ((a) == (b))
463#endif
464
475#ifndef HASH_FUNCTION
476#error "Must define HASH_FUNCTION."
477#define HASH_FUNCTION(key) (0)
478#endif
479
480FUNCTION_LINKAGE FHASHTABLE_TYPE *JOIN(FHASHTABLE_NAME, init)(FHASHTABLE_TYPE *self, const uint32_t pow2_capacity)
481{
482 assert(self);
483 assert(IS_POW2(pow2_capacity));
484
485 self->count = 0;
486 self->capacity = pow2_capacity;
487
488 for (uint32_t i = 0; i < self->capacity; i++) {
489 self->slots[i].offset = FHASHTABLE_EMPTY_SLOT_OFFSET;
490 }
491
492 return self;
493}
494
495FUNCTION_LINKAGE FHASHTABLE_TYPE *JOIN(FHASHTABLE_NAME, create_custom)(const uint32_t min_capacity, void *context_ptr,
496 void *(*allocate)(void *context_ptr,
497 size_t alignment, size_t size))
498{
499 if (min_capacity == 0 || min_capacity > UINT32_MAX / 2 + 1) {
500 return NULL;
501 }
502
503 const uint32_t capacity = round_up_pow2_32(min_capacity);
504
505 if (FHASHTABLE_CALC_SIZEOF_OVERFLOWS(FHASHTABLE_NAME, capacity)) {
506 return NULL;
507 }
508
509 const uint32_t size = FHASHTABLE_CALC_SIZEOF(FHASHTABLE_NAME, capacity);
510
511 FHASHTABLE_TYPE *self = (FHASHTABLE_TYPE *)allocate(context_ptr, alignof(FHASHTABLE_TYPE), size);
512
513 if (!self) {
514 return NULL;
515 }
516
517 memset(self, 0, size);
518 FHASHTABLE_INIT(self, capacity);
519
520 return self;
521}
522
524static inline void *JOIN(internal, JOIN(FHASHTABLE_NAME, allocate))(void *context_ptr, size_t alignment, size_t size)
525{
526 (void)context_ptr;
527 (void)alignment;
528 return malloc(size);
529}
531
532FUNCTION_LINKAGE FHASHTABLE_TYPE *JOIN(FHASHTABLE_NAME, create)(const uint32_t capacity)
533{
534 return JOIN(FHASHTABLE_NAME, create_custom)(capacity, NULL, JOIN(internal, JOIN(FHASHTABLE_NAME, allocate)));
535}
536
537FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, destroy_custom)(FHASHTABLE_TYPE *self, void *context_ptr,
538 void (*deallocate)(void *context_ptr, void *mem))
539{
540 assert(self != NULL);
541
542 deallocate(context_ptr, self);
543}
544
546static inline void JOIN(internal, JOIN(FHASHTABLE_NAME, deallocate))(void *context_ptr, void *mem)
547{
548 (void)context_ptr;
549 free(mem);
550}
552
553FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, destroy)(FHASHTABLE_TYPE *self)
554{
555 assert(self != NULL);
556
557 JOIN(FHASHTABLE_NAME, destroy_custom)(self, NULL, JOIN(internal, JOIN(FHASHTABLE_NAME, deallocate)));
558}
559
560FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, is_empty)(const FHASHTABLE_TYPE *self)
561{
562 assert(self != NULL);
563
564 return self->count == 0;
565}
566
567FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, is_full)(const FHASHTABLE_TYPE *self)
568{
569 assert(self != NULL);
570
571 return self->count == self->capacity;
572}
573
574FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, contains_key)(const FHASHTABLE_TYPE *self, const KEY_TYPE key)
575{
576 assert(self != NULL);
577
578 const uint32_t key_hash = HASH_FUNCTION(key);
579 const uint32_t index_mask = self->capacity - 1;
580
581 uint32_t index = key_hash & index_mask;
582 uint32_t max_possible_offset = 0;
583
584 while (true) {
585 const bool not_empty = self->slots[index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
586
587 const bool below_max = max_possible_offset <= self->slots[index].offset;
588
589 if (!(not_empty && below_max)) {
590 break;
591 }
592
593 if (KEY_IS_EQUAL(self->slots[index].key, key)) {
594 return true;
595 }
596
597 index++;
598 index &= index_mask;
599 max_possible_offset++;
600 }
601 return false;
602}
603
604FUNCTION_LINKAGE VALUE_TYPE *JOIN(FHASHTABLE_NAME, get_value_mut)(FHASHTABLE_TYPE *self, const KEY_TYPE key)
605{
606 assert(self != NULL);
607
608 const uint32_t key_hash = HASH_FUNCTION(key);
609 const uint32_t index_mask = self->capacity - 1;
610
611 uint32_t index = key_hash & index_mask;
612 uint32_t max_possible_offset = 0;
613
614 while (true) {
615 const bool not_empty = self->slots[index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
616
617 const bool below_max = max_possible_offset <= self->slots[index].offset;
618
619 if (!(not_empty && below_max)) {
620 break;
621 }
622
623 if (KEY_IS_EQUAL(self->slots[index].key, key)) {
624 return &self->slots[index].value;
625 }
626
627 index++;
628 index &= index_mask;
629 max_possible_offset++;
630 }
631 return NULL;
632}
633
634FUNCTION_LINKAGE VALUE_TYPE JOIN(FHASHTABLE_NAME, get_value)(const FHASHTABLE_TYPE *self, const KEY_TYPE key,
635 VALUE_TYPE default_value)
636{
637 assert(self != NULL);
638
639 const uint32_t key_hash = HASH_FUNCTION(key);
640 const uint32_t index_mask = self->capacity - 1;
641
642 uint32_t index = key_hash & index_mask;
643 uint32_t max_possible_offset = 0;
644
645 while (true) {
646 const bool not_empty = self->slots[index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
647
648 const bool below_max = max_possible_offset <= self->slots[index].offset;
649
650 if (!(not_empty && below_max)) {
651 break;
652 }
653
654 if (KEY_IS_EQUAL(self->slots[index].key, key)) {
655 return self->slots[index].value;
656 }
657
658 index++;
659 index &= index_mask;
660 max_possible_offset++;
661 }
662 return default_value;
663}
664
665FUNCTION_LINKAGE VALUE_TYPE *JOIN(FHASHTABLE_NAME, search)(FHASHTABLE_TYPE *self, const KEY_TYPE key)
666{
667 return JOIN(FHASHTABLE_NAME, get_value_mut)(self, key);
668}
669
671static inline void JOIN(internal, JOIN(FHASHTABLE_NAME, swap_slots))(FHASHTABLE_SLOT_TYPE *a, FHASHTABLE_SLOT_TYPE *b)
672{
673 FHASHTABLE_SLOT_TYPE temp = *a;
674 *a = *b;
675 *b = temp;
676}
678
679FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, insert)(FHASHTABLE_TYPE *self, KEY_TYPE key, VALUE_TYPE value)
680{
681 assert(self != NULL);
682 assert(FHASHTABLE_CONTAINS_KEY(self, key) == false);
683
684 const uint32_t index_mask = self->capacity - 1;
685 const uint32_t key_hash = HASH_FUNCTION(key);
686
687 uint32_t index = key_hash & index_mask;
688 FHASHTABLE_SLOT_TYPE current_slot = {.offset = 0, .key = key, .value = value};
689
690 while (true) {
691 const bool not_empty = self->slots[index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
692
693 if (!not_empty) {
694 break;
695 }
696
697 if (current_slot.offset > self->slots[index].offset) {
698 FHASHTABLE_SWAP_SLOTS(&self->slots[index], &current_slot);
699 }
700
701 index++;
702 index &= index_mask;
703 current_slot.offset++;
704 }
705 self->slots[index] = current_slot;
706 self->count++;
707}
708
709FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, update)(FHASHTABLE_TYPE *self, KEY_TYPE key, VALUE_TYPE value)
710{
711 assert(self != NULL);
712
713 const uint32_t index_mask = self->capacity - 1;
714 const uint32_t key_hash = HASH_FUNCTION(key);
715
716 uint32_t index = key_hash & index_mask;
717 FHASHTABLE_SLOT_TYPE current_slot = {.offset = 0, .key = key, .value = value};
718
719 while (true) {
720 const bool not_empty = self->slots[index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
721
722 if (!not_empty) {
723 break;
724 }
725
726 const bool offset_is_same = current_slot.offset == self->slots[index].offset;
727
728 const bool key_is_equal = KEY_IS_EQUAL(current_slot.key, self->slots[index].key);
729
730 if (offset_is_same && key_is_equal) {
731 self->slots[index].value = current_slot.value;
732 return;
733 }
734
735 if (current_slot.offset > self->slots[index].offset) {
736 FHASHTABLE_SWAP_SLOTS(&current_slot, &self->slots[index]);
737 }
738
739 index++;
740 index &= index_mask;
741 current_slot.offset++;
742 }
743
744 self->slots[index] = current_slot;
745 self->count++;
746}
747
749static inline void JOIN(internal, JOIN(FHASHTABLE_NAME, backshift))(FHASHTABLE_TYPE *self, const uint32_t index_mask,
750 uint32_t index)
751{
752 assert(self);
753
754 uint32_t next_index = (index + 1) & index_mask;
755
756 while (true) {
757 const bool not_empty = self->slots[next_index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
758
759 const bool offset_is_non_zero = self->slots[next_index].offset > 0;
760
761 if (!(not_empty && offset_is_non_zero)) {
762 break;
763 }
764
765 self->slots[index] = self->slots[next_index];
766 self->slots[index].offset--;
767
768 self->slots[next_index].offset = FHASHTABLE_EMPTY_SLOT_OFFSET;
769
770 index = next_index;
771 next_index = (index + 1) & index_mask;
772 }
773}
775
776FUNCTION_LINKAGE bool JOIN(FHASHTABLE_NAME, delete)(FHASHTABLE_TYPE *self, const KEY_TYPE key)
777{
778 assert(self != NULL);
779
780 const uint32_t index_mask = self->capacity - 1;
781 const uint32_t key_hash = HASH_FUNCTION(key);
782
783 uint32_t index = key_hash & index_mask;
784 uint32_t max_possible_offset = 0;
785
786 while (true) {
787 const bool not_empty = self->slots[index].offset != FHASHTABLE_EMPTY_SLOT_OFFSET;
788
789 const bool below_max = max_possible_offset <= self->slots[index].offset;
790
791 if (!(not_empty && below_max)) {
792 break;
793 }
794
795 const bool key_is_equal = KEY_IS_EQUAL(key, self->slots[index].key);
796
797 if (!key_is_equal) {
798 index = (index + 1) & index_mask;
799 max_possible_offset++;
800 continue;
801 }
802
803 self->slots[index].offset = FHASHTABLE_EMPTY_SLOT_OFFSET;
804 self->count--;
805
806 FHASHTABLE_BACKSHIFT(self, index_mask, index);
807
808 return true;
809 }
810 return false;
811}
812
813FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, clear)(FHASHTABLE_TYPE *self)
814{
815 assert(self != NULL);
816
817 for (uint32_t i = 0; i < self->capacity; i++) {
818 self->slots[i].offset = FHASHTABLE_EMPTY_SLOT_OFFSET;
819 }
820 self->count = 0;
821}
822
823FUNCTION_LINKAGE void JOIN(FHASHTABLE_NAME, copy)(FHASHTABLE_TYPE *restrict dest_ptr,
824 const FHASHTABLE_TYPE *restrict src_ptr)
825{
826 assert(src_ptr != NULL);
827 assert(dest_ptr != NULL);
828 assert(src_ptr->capacity <= dest_ptr->capacity);
829 assert(dest_ptr->count == 0);
830
831 uint32_t index;
832 KEY_TYPE key;
833 VALUE_TYPE value;
834 FHASHTABLE_FOR_EACH(src_ptr, index, key, value)
835 {
836 JOIN(FHASHTABLE_NAME, insert)(dest_ptr, key, value);
837 }
838}
839
840#endif
841
842// }}}
843
844// macro undefs: {{{
845
846#undef NAME
847#undef KEY_TYPE
848#undef VALUE_TYPE
849#undef KEY_IS_EQUAL
850#undef HASH_FUNCTION
851#undef FUNCTION_LINKAGE
852#undef FUNCTION_DEFINITIONS
853#undef TYPE_DEFINITIONS
854
855#undef FHASHTABLE_NAME
856#undef FHASHTABLE_TYPE
857#undef FHASHTABLE_SLOT_TYPE
858#undef FHASHTABLE_INIT
859#undef FHASHTABLE_IS_FULL
860#undef FHASHTABLE_CONTAINS_KEY
861#undef FHASHTABLE_CALC_SIZEOF
862#undef FHASHTABLE_SWAP_SLOTS
863#undef FHASHTABLE_BACKSHIFT
864
865// }}}
866
867#ifdef __cplusplus
868}
869#endif
870
871// 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:128
#define JOIN(a, b)
First expand tokens, then paste them together with a _ in between.
Definition fhashtable_template.h:76
#define HASH_FUNCTION(key)
Used to compute indicies of keys. This must be manually defined before including this header file.
Definition fhashtable_template.h:477
#define IS_POW2(X)
Macro to check if a number is a power of two.
Definition fhashtable_template.h:87
#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:462
#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:143
#define FHASHTABLE_EMPTY_SLOT_OFFSET
Offset constant used to flag empty slots.
Definition fhashtable_template.h:95
#define FHASHTABLE_FOR_EACH(self, index, key_, value_)
Iterate over the non-empty slots in the hashtable in arbitary order.
Definition fhashtable_template.h:111
#define VALUE_TYPE
The value type. This must be manually defined before including this header file.
Definition fhashtable_template.h:183
#define KEY_TYPE
The key type. This must be manually defined before including this header file.
Definition fhashtable_template.h:169
#define FUNCTION_LINKAGE
Specify function linkage e.g. static inline.
Definition fstack_template.h:146
#define JOIN(a, b)
First expand tokens, then paste them together with a _ in between.
Definition fstack_template.h:50
#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:224
VALUE_TYPE value
The value in this slot.
Definition fhashtable_template.h:226
KEY_TYPE key
The key in this slot.
Definition fhashtable_template.h:225
fhashtable_slot_type slots[]
Array of slots.
Definition fhashtable_template.h:236
uint32_t count
Number of non-empty slots.
Definition fhashtable_template.h:234
uint32_t capacity
Number of slots.
Definition fhashtable_template.h:235