CX Framework
Cross-platform C utility framework
Loading...
Searching...
No Matches
hashtable.h
Go to the documentation of this file.
1#pragma once
6
7#include <cx/cx.h>
8#include <cx/debug/assert.h>
9
10CX_C_BEGIN
11
16
21
22typedef struct hashtable_ref {
23 void* _is_hashtable;
24} hashtable_ref;
25
28typedef struct hashtable_ref* hashtable;
29typedef struct HTChunkInfo HTChunkInfo;
30
31// Internal structure - do not access directly
32// Use htSize(), htKeyType(), htValType() macros instead
33typedef struct HashTableHeader {
34 // hashtable header begins here
35 uint32 idxsz; // index size
36 uint32 idxused; // number of used index entries (including deleted)
37 uint32 storsz; // number of chunks in storage array
38 uint32 storused; // number of used storage slots (including deleted)
39 uint32 valid; // number of valid items in the hash table
40 uint32 flags;
41
42 stype keytype;
43 stype valtype;
44
45 HTChunkInfo* chunks;
46 void** keystorage;
47 void** valstorage;
48 uint32 index[1];
49} HashTableHeader;
50
51// Element handle - represents a slot in the hash table
52// Value of 0 is reserved for "null" / not found
53// Can be used to retrieve key/value pointers without a second lookup
54typedef uint32 htelem;
55
56// Hash table iterator state
57typedef struct htiter {
58 HashTableHeader* hdr;
59 uint32 slot;
60} htiter;
61
62// Storage allocation constants
63// HT_SLOTS_PER_CHUNK MUST BE A POWER OF TWO!
64#ifdef _64BIT
65#define HT_CHUNK_SHIFT 6
66#else
67#define HT_CHUNK_SHIFT 5
68#endif
69#define HT_SLOTS_PER_CHUNK (1 << HT_CHUNK_SHIFT)
70#define HT_CHUNK_MASK (HT_SLOTS_PER_CHUNK - 1)
71#define HT_QUARTER_CHUNK (HT_SLOTS_PER_CHUNK >> 2)
72#define HT_QCHUNK_MASK (HT_QUARTER_CHUNK - 1)
73
74// Internal macros for header access - do not use directly
75#define HTABLE_HDRSIZE (offsetof(HashTableHeader, index))
76#define HTABLE_HDR(ref) ((HashTableHeader*)(((uintptr)(&((ref)->_is_hashtable))) - HTABLE_HDRSIZE))
77
78// Internal function - use htSize(), htKeyType(), htValType() macros instead
79_Ret_valid_ _meta_inline HashTableHeader* _htHdr(_In_ hashtable ref)
80{
81 return HTABLE_HDR(ref);
82}
83
89#define htSize(ref) ((ref) ? _htHdr((ref))->valid : 0)
90
97#define htKeyType(ref) ((ref) ? _htHdr((ref))->keytype : stType(none))
98
107#define htValType(ref) ((ref) ? _htHdr((ref))->valtype : stType(none))
108
117#define hteKeyPtrHdr(hdr, type, elem) ((stStorageType(type)*)_hteElemKeyPtr(hdr, elem))
118
127#define hteValPtrHdr(hdr, type, elem) ((stStorageType(type)*)_hteElemValPtr(hdr, elem))
128
137#define hteKeyPtr(ref, type, elem) hteKeyPtrHdr(_htHdr(ref), type, elem)
138
147#define hteValPtr(ref, type, elem) hteValPtrHdr(_htHdr(ref), type, elem)
148
156#define hteKey(ref, type, elem) (*hteKeyPtr(ref, type, elem))
157
165#define hteVal(ref, type, elem) (*hteValPtr(ref, type, elem))
166
173#define htiKeyPtr(type, iter) (hteKeyPtrHdr((iter).hdr, type, (iter).slot))
174
181#define htiValPtr(type, iter) (hteValPtrHdr((iter).hdr, type, (iter).slot))
182
189#define htiKey(type, iter) (*htiKeyPtr(type, iter))
190
197#define htiVal(type, iter) (*htiValPtr(type, iter))
198
200
205
210
214 HT_RefKeys = 0x0002,
215
219 HT_Ref = 0x0004,
220
225 HT_InsertOpt = 0x0008,
226
231 HT_Compact = 0x0010,
232
233 // Internal use only - do not set manually
234 HTINT_Metadata = 0x1000, // metadata area (for hash fragments) is present after index
235 HTINT_Quadratic = 0x2000, // use quadratic probing rather than linear
236 HTINT_Pow2 = 0x4000, // size forced to power of 2 to avoid division
237};
238
239// Growth rate configuration - controls how much to grow when expanding the table
240enum HASHTABLE_GROWBY_ENUM {
241 HT_GROW_By100 = 0x00, // Double size (balanced default)
242 HT_GROW_By200 = 0x10, // Triple size
243 HT_GROW_By300 = 0x20, // Quadruple size (better performance, uses more memory)
244 HT_GROW_By50 = 0x40, // Grow by 50% (worse performance, memory efficient)
245 HT_GROW_BY_MASK = 0xf0,
246};
247
248// Growth threshold configuration - controls when to expand the table
249enum HASHTABLE_GROW_ENUM {
250 HT_GROW_At75 = 0x00, // Grow when 75% full (balanced default)
251 HT_GROW_At50 = 0x01, // Grow when 50% full (better performance, uses more memory)
252 HT_GROW_At90 = 0x02, // Grow when 90% full (worse performance, memory efficient)
253 HT_GROW_AT_MASK = 0x0f,
254
255 // Preset combinations for common use cases
256 HT_GROW_MaxSpeed = (uint32)HT_GROW_At50 | (uint32)HT_GROW_By300, // Maximum performance
257 HT_GROW_MinSize = (uint32)HT_GROW_At90 | (uint32)HT_GROW_By50, // Minimum memory usage
258};
259
264 HT_Ignore = 0x00010000,
265
269 HT_Borrow = 0x00020000,
270
271 // Internal use only - do not use directly
272 HTINT_Consume = 0x10000000,
273};
274
275// Helper macros for growth configuration
276
277#define HT_GROW_MASK (0xff000000)
278
322#define HT_Grow(flag) (((uint32)HT_GROW_##flag) << 24)
323
324// Internal macro - extracts growth settings from flags
325#define HT_GET_GROW(flags) ((flags) >> 24)
326
327void _htInit(_Outptr_ hashtable* out, stype keytype, stype valtype, uint32 initsz, flags_t flags);
328
344#define htInit(out, keytype, valtype, initsz, ...) \
345 _htInit(out, stType(keytype), stType(valtype), initsz, opt_flags(__VA_ARGS__))
346
353void htDestroy(_Inout_ptr_uninit_ hashtable* htbl);
354
360void htClear(_Inout_ptr_ hashtable* htbl);
361
363
368
375void htReindex(_Inout_ptr_ hashtable* htbl, uint32 minsz);
376
382void htRepack(_Inout_ptr_ hashtable* htbl);
383
390void htClone(_Outptr_ hashtable* out, _In_ hashtable ref);
391
392// Internal function - do not call directly, use htInsert() or htInsertC() macro instead
393htelem _htInsert(_Inout_ptr_ hashtable* htbl, _In_ stgeneric key, _In_ stgeneric val,
394 flags_t flags);
395
396// Internal function with type checking
397_meta_inline htelem _htInsertChecked(_Inout_ptr_ hashtable* htbl, stype keytype, _In_ stgeneric key,
398 stype valtype, _In_ stgeneric val, flags_t flags)
399{
400 devAssert(*htbl);
401 devAssert(stEq(htKeyType(*htbl), keytype) && stEq(htValType(*htbl), valtype));
402 return _htInsert(htbl, key, val, flags);
403}
404
405// Internal SAL annotation helper
406#define _ht_Consume_Arg_ \
407 _When_(flags& HTINT_Consume, _Pre_notnull_ _Post_invalid_) \
408 _When_(!(flags & HTINT_Consume), _Inout_)
409
410// Internal function - do not call directly, use htInsertC() macro instead
411htelem _htInsertPtr(_Inout_ptr_ hashtable* htbl, _In_ stgeneric key,
412 _ht_Consume_Arg_ stgeneric* val, flags_t flags);
413
414// Internal function with type checking
415_meta_inline htelem _htInsertCheckedC(_Inout_ptr_ hashtable* htbl, stype keytype,
416 _In_ stgeneric key, stype valtype,
417 _ht_Consume_Arg_ stgeneric* val, flags_t flags)
418{
419 devAssert(*htbl);
420 devAssert(stEq(htKeyType(*htbl), keytype) && stEq(htValType(*htbl), valtype));
421 return _htInsertPtr(htbl, key, val, flags);
422}
423
443#define htInsert(htbl, ktype, key, vtype, val, ...) \
444 _htInsertChecked(htbl, \
445 stCheckedArg(ktype, key), \
446 stCheckedArg(vtype, val), \
447 opt_flags(__VA_ARGS__))
448
470#define htInsertC(htbl, ktype, key, vtype, val, ...) \
471 _htInsertCheckedC(htbl, \
472 stCheckedArg(ktype, key), \
473 stCheckedPtrArg(vtype, val), \
474 opt_flags(__VA_ARGS__) | HTINT_Consume)
475
476// Internal function - do not call directly, use htFind() macro instead
477_Success_(return != 0) htelem _htFind(_In_ hashtable htbl, _In_ stgeneric key, _Inout_opt_ stgeneric* val,
478 flags_t flags);
479
480// Internal function with type checking
481_Success_(return != 0) _meta_inline htelem
482_htFindChecked(_In_ hashtable htbl, stype keytype, _In_ stgeneric key, stype valtype,
483 _stCopyDest_Anno_opt_(valtype) stgeneric* val, flags_t flags)
484{
485 devAssert(htbl);
486 devAssert(stEq(htKeyType(htbl), keytype));
487 devAssert(valtype == stType(none) || stEq(htValType(htbl), valtype));
488 return _htFind(htbl, key, val, flags);
489}
490
517#define htFind(htbl, ktype, key, vtype, val_copy_out, ...) \
518 _htFindChecked(htbl, \
519 stCheckedArg(ktype, key), \
520 stCheckedPtrArg(vtype, val_copy_out), \
521 opt_flags(__VA_ARGS__))
522
523// Internal function - do not call directly, use htExtract() or htRemove() macro instead
524_Success_(return) bool
525_htExtract(_Inout_ptr_ hashtable* htbl, _In_ stgeneric key, _Inout_opt_ stgeneric* val);
526
527// Internal function with type checking
528_Success_(return) _meta_inline bool
529_htExtractChecked(_Inout_ptr_ hashtable* htbl, stype keytype, _In_ stgeneric key, stype valtype,
530 _stCopyDest_Anno_opt_(valtype) stgeneric* val)
531{
532 devAssert(*htbl);
533 devAssert(stEq(htKeyType(*htbl), keytype));
534 devAssert(valtype == stType(none) || stEq(htValType(*htbl), valtype));
535 return _htExtract(htbl, key, val);
536}
537
559#define htExtract(htbl, ktype, key, vtype, val_copy_out) \
560 _htExtractChecked(htbl, stCheckedArg(ktype, key), stCheckedPtrArg(vtype, val_copy_out))
561
575#define htRemove(htbl, ktype, key) \
576 _htExtractChecked(htbl, stCheckedArg(ktype, key), stType(none), NULL)
577
578// Internal function - do not call directly, use htHasKey() macro instead
579bool _htHasKey(_In_ hashtable htbl, _In_ stgeneric key);
580
581// Internal function with type checking
582_meta_inline bool _htHasKeyChecked(_In_ hashtable htbl, stype keytype, _In_ stgeneric key)
583{
584 devAssert(htbl);
585 devAssert(stEq(htKeyType(htbl), keytype));
586 return _htHasKey(htbl, key);
587}
588
604#define htHasKey(htbl, ktype, key) _htHasKeyChecked(htbl, stCheckedArg(ktype, key))
605
607
608// Internal function - gets pointer to key storage for an element
609// Do not call directly - use hteKeyPtr() or hteKey() macros instead
610_Pre_satisfies_(elem > 0) void* _hteElemKeyPtr(_Inout_ HashTableHeader* hdr, htelem elem);
611
612// Internal function - gets pointer to value storage for an element
613// Do not call directly - use hteValPtr() or hteVal() macros instead
614_Pre_satisfies_(elem > 0) void* _hteElemValPtr(_Inout_ HashTableHeader* hdr, htelem elem);
615
635
636// define separately so the the prototype is all on one line and the tooltips in vscode work
637// properly...
638#define _htiInitAnno \
639 _Success_(return) _Post_satisfies_(iter->slot > 0) \
640 _On_failure_(_Post_satisfies_(iter->slot == 0))
641
651_htiInitAnno bool htiInit(_Out_ htiter* iter, _In_ hashtable htbl);
652
653#define _htiNextAnno \
654 _Success_(return) _Post_satisfies_(iter->slot > 0) \
655 _On_failure_(_Post_satisfies_(iter->slot == 0))
656
663_htiNextAnno bool htiNext(_Inout_ htiter* iter);
664
670void htiFinish(_Pre_notnull_ _Post_invalid_ htiter* iter);
671
676_Post_equal_to_(iter->slot > 0) _meta_inline bool htiValid(_In_ htiter* iter)
677{
678 return iter->slot > 0;
679}
680
682
684
685CX_C_END
Runtime assertion macros and failure handling.
#define devAssert(expr)
Definition assert.h:140
HASHTABLE_FUNC_FLAGS_ENUM
Function-specific flags.
Definition hashtable.h:261
void htClear(hashtable *htbl)
HASHTABLE_FLAGS_ENUM
Hash table configuration flags.
Definition hashtable.h:207
void htDestroy(hashtable *htbl)
@ HT_Borrow
Definition hashtable.h:269
@ HT_Ignore
Definition hashtable.h:264
@ HT_InsertOpt
Definition hashtable.h:225
@ HT_Compact
Definition hashtable.h:231
@ HT_Ref
Definition hashtable.h:219
@ HT_RefKeys
Definition hashtable.h:214
@ HT_CaseInsensitive
Case-insensitive key matching - only valid for string keys.
Definition hashtable.h:209
#define htValType(ref)
Definition hashtable.h:107
#define htKeyType(ref)
Definition hashtable.h:97
struct hashtable_ref * hashtable
Definition hashtable.h:28
void htiFinish(htiter *iter)
bool htiValid(htiter *iter)
Definition hashtable.h:676
_htiInitAnno bool htiInit(htiter *iter, hashtable htbl)
_htiNextAnno bool htiNext(htiter *iter)
void htClone(hashtable *out, hashtable ref)
void htRepack(hashtable *htbl)
void htReindex(hashtable *htbl, uint32 minsz)
bool stEq(stype s1, stype s2)
Definition stype.h:1785
#define stType(name)
Definition stype.h:972