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
14
19
20typedef struct hashtable_ref {
21 void* _is_hashtable;
22} hashtable_ref;
23
26typedef struct hashtable_ref* hashtable;
27typedef struct HTChunkInfo HTChunkInfo;
28
29// Internal structure - do not access directly
30// Use htSize(), htKeyType(), htValType() macros instead
31typedef struct HashTableHeader {
32 // extended header, present only if HT_Extended is set
33 STypeOps keytypeops;
34 STypeOps valtypeops;
35
36 // hashtable header begins here
37 uint32 idxsz; // index size
38 uint32 idxused; // number of used index entries (including deleted)
39 uint32 storsz; // number of chunks in storage array
40 uint32 storused; // number of used storage slots (including deleted)
41 uint32 valid; // number of valid items in the hash table
42
43 stype keytype;
44 stype valtype;
45 uint32 flags;
46
47 HTChunkInfo* chunks;
48 void** keystorage;
49 void** valstorage;
50 uint32 index[1];
51} HashTableHeader;
52
53// Element handle - represents a slot in the hash table
54// Value of 0 is reserved for "null" / not found
55// Can be used to retrieve key/value pointers without a second lookup
56typedef uint32 htelem;
57
58// Hash table iterator state
59typedef struct htiter {
60 HashTableHeader* hdr;
61 uint32 slot;
62} htiter;
63
64// Storage allocation constants
65// HT_SLOTS_PER_CHUNK MUST BE A POWER OF TWO!
66#ifdef _64BIT
67#define HT_CHUNK_SHIFT 6
68#else
69#define HT_CHUNK_SHIFT 5
70#endif
71#define HT_SLOTS_PER_CHUNK (1 << HT_CHUNK_SHIFT)
72#define HT_CHUNK_MASK (HT_SLOTS_PER_CHUNK - 1)
73#define HT_QUARTER_CHUNK (HT_SLOTS_PER_CHUNK >> 2)
74#define HT_QCHUNK_MASK (HT_QUARTER_CHUNK - 1)
75
76// Internal macros for header access - do not use directly
77#define HTABLE_HDRSIZE (offsetof(HashTableHeader, index))
78#define HTABLE_HDR(ref) ((HashTableHeader*)(((uintptr)(&((ref)->_is_hashtable))) - HTABLE_HDRSIZE))
79
80// Internal function - use htSize(), htKeyType(), htValType() macros instead
81_Ret_valid_ _meta_inline HashTableHeader* _htHdr(_In_ hashtable ref)
82{
83 return HTABLE_HDR(ref);
84}
85
91#define htSize(ref) ((ref) ? _htHdr((ref))->valid : 0)
92
98#define htKeyType(ref) ((ref) ? _htHdr((ref))->keytype : 0)
99
105#define htValType(ref) ((ref) ? _htHdr((ref))->valtype : 0)
106
115#define hteKeyPtrHdr(hdr, type, elem) ((stStorageType(type)*)_hteElemKeyPtr(hdr, elem))
116
125#define hteValPtrHdr(hdr, type, elem) ((stStorageType(type)*)_hteElemValPtr(hdr, elem))
126
135#define hteKeyPtr(ref, type, elem) hteKeyPtrHdr(_htHdr(ref), type, elem)
136
145#define hteValPtr(ref, type, elem) hteValPtrHdr(_htHdr(ref), type, elem)
146
154#define hteKey(ref, type, elem) (*hteKeyPtr(ref, type, elem))
155
163#define hteVal(ref, type, elem) (*hteValPtr(ref, type, elem))
164
171#define htiKeyPtr(type, iter) (hteKeyPtrHdr((iter).hdr, type, (iter).slot))
172
179#define htiValPtr(type, iter) (hteValPtrHdr((iter).hdr, type, (iter).slot))
180
187#define htiKey(type, iter) (*htiKeyPtr(type, iter))
188
195#define htiVal(type, iter) (*htiValPtr(type, iter))
196
198
203
208
212 HT_RefKeys = 0x0002,
213
217 HT_Ref = 0x0004,
218
223 HT_InsertOpt = 0x0008,
224
229 HT_Compact = 0x0010,
230
231 // Internal use only - do not set manually
232 HTINT_Metadata = 0x1000, // metadata area (for hash fragments) is present after index
233 HTINT_Quadratic = 0x2000, // use quadratic probing rather than linear
234 HTINT_Pow2 = 0x4000, // size forced to power of 2 to avoid division
235 HTINT_Extended = 0x8000, // extended header is present
236};
237
238// Growth rate configuration - controls how much to grow when expanding the table
239enum HASHTABLE_GROWBY_ENUM {
240 HT_GROW_By100 = 0x00, // Double size (balanced default)
241 HT_GROW_By200 = 0x10, // Triple size
242 HT_GROW_By300 = 0x20, // Quadruple size (better performance, uses more memory)
243 HT_GROW_By50 = 0x40, // Grow by 50% (worse performance, memory efficient)
244 HT_GROW_BY_MASK = 0xf0,
245};
246
247// Growth threshold configuration - controls when to expand the table
248enum HASHTABLE_GROW_ENUM {
249 HT_GROW_At75 = 0x00, // Grow when 75% full (balanced default)
250 HT_GROW_At50 = 0x01, // Grow when 50% full (better performance, uses more memory)
251 HT_GROW_At90 = 0x02, // Grow when 90% full (worse performance, memory efficient)
252 HT_GROW_AT_MASK = 0x0f,
253
254 // Preset combinations for common use cases
255 HT_GROW_MaxSpeed = (uint32)HT_GROW_At50 | (uint32)HT_GROW_By300, // Maximum performance
256 HT_GROW_MinSize = (uint32)HT_GROW_At90 | (uint32)HT_GROW_By50, // Minimum memory usage
257};
258
263 HT_Ignore = 0x00010000,
264
268 HT_Borrow = 0x00020000,
269
270 // Internal use only - do not use directly
271 HTINT_Consume = 0x10000000,
272};
273
274// Helper macros for growth configuration
275
276#define HT_GROW_MASK (0xff000000)
277
321#define HT_Grow(flag) (((uint32)HT_GROW_##flag) << 24)
322
323// Internal macro - extracts growth settings from flags
324#define HT_GET_GROW(flags) ((flags) >> 24)
325
326void _htInit(_Outptr_ hashtable* out, stype keytype, _In_opt_ STypeOps* keyops, stype valtype,
327 _In_opt_ STypeOps* valops, uint32 initsz, flags_t flags);
328
344#define htInit(out, keytype, valtype, initsz, ...) \
345 _htInit(out, stFullType(keytype), stFullType(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)
478htelem _htFind(_In_ hashtable htbl, _In_ stgeneric key, _Inout_opt_ stgeneric* val, flags_t flags);
479
480// Internal function with type checking
481_Success_(return != 0) _meta_inline
482htelem _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(stGetId(valtype) == stTypeId(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)
529_meta_inline bool _htExtractChecked(_Inout_ptr_ hashtable* htbl, stype keytype, _In_ stgeneric key,
530 stype valtype, _stCopyDest_Anno_opt_(valtype) stgeneric* val)
531{
532 devAssert(*htbl);
533 devAssert(stEq(htKeyType(*htbl), keytype));
534 devAssert(stGetId(valtype) == stTypeId(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
Runtime assertion macros and failure handling.
#define devAssert(expr)
Definition assert.h:138
HASHTABLE_FUNC_FLAGS_ENUM
Function-specific flags.
Definition hashtable.h:260
void htClear(hashtable *htbl)
HASHTABLE_FLAGS_ENUM
Hash table configuration flags.
Definition hashtable.h:205
void htDestroy(hashtable *htbl)
@ HT_Borrow
Definition hashtable.h:268
@ HT_Ignore
Definition hashtable.h:263
@ HT_InsertOpt
Definition hashtable.h:223
@ HT_Compact
Definition hashtable.h:229
@ HT_Ref
Definition hashtable.h:217
@ HT_RefKeys
Definition hashtable.h:212
@ HT_CaseInsensitive
Case-insensitive key matching - only valid for string keys.
Definition hashtable.h:207
#define htValType(ref)
Definition hashtable.h:105
#define htKeyType(ref)
Definition hashtable.h:98
struct hashtable_ref * hashtable
Definition hashtable.h:26
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)
#define stGetId(st)
Definition stype.h:507
#define stTypeId(name)
Definition stype.h:419
bool stEq(stype sta, stype stb)
Definition stype.h:537