1
2 #include "taisei.h"
3
4 #include "hashtable.h"
5 #include "list.h"
6 #include "util/stringops.h"
7 #include "util/assert.h"
8 #include "log.h"
9
10 #include <SDL.h>
11
12 /******************************************************************************\
13 * ,. *
14 * ``` `.,:::,. `;'';,;'.` *
15 * `,`,;'''''';.+###############''''''' ,.` *
16 * ,.''.:''''+@@####################'';,' `: *
17 * ,`',;'''+#@@#########################++;, *
18 * :,';+++@@#############################:,: *
19 * .::,;++@@###############################; *
20 * .,:+;+,,,:+##@##########@##############+:, *
21 * +,:+#@@##'::::++#######@###@##@##+'::,;'#` *
22 * @++:@@@@@#@@@@###@#@##@##'@@##@@##@@@##@## *
23 * .+'++@@@@##@@@@#@#@@#+#@@#:'@##@@@@@#@#@@## *
24 * .,+;+'+@@@##@@@@@+@+@@;';@@.`:#@;';@##@#@#@@#, *
25 * :#:+'+@@@#@@@@@:+;:@::':@,``.:@,+::@:@#@@#@#; *
26 * '::+'@@@@@@@@#`,+`++';.`````:``+++,+:@@@.;@; *
27 * +@'+;@@@@#:,;;,`, ''';`````````+''+:.';.: '` *
28 * +@@@@@@@#:+',:`.,,.````````````.,,,.,:''. *
29 * '@@@@@@@':'+'+`....```````````````...:'+ Type-safe generics? In my C? *
30 * ;@@@@@@@':'+''`````````.`````````````,'+ *
31 * :#@@@@@@@:'+''`````````::::''````````:;; It's more likely than you think. *
32 * .;@@@@@@@@++,'`````````'::::.````````;:`: *
33 * .@@#@@@@#;'.:.`````````':'``````````@.: *
34 * ,#@,@@;@##@##``````````````````````@@#, [FREE HEADER CHECK] *
35 * :.@ +# @##@@# :``````````````````;@:@@: *
36 * # '``.@:,## :`````````````.+@#,..@: *
37 * ` '` @ `::,,,:::` ` +; . ;' *
38 * : + *
39 \******************************************************************************/
40
41 /***********************\
42 * User-defined macros *
43 \***********************/
44
45 /*
46 * HT_SUFFIX
47 *
48 * A name tag associated with your custom hashtable type.
49 * You must define this to a valid, unique identifier.
50 * Whenever you see XXX appear as part of an identifier in the documentation,
51 * substitute it for the value of HT_SUFFIX.
52 *
53 * Example:
54 *
55 * #define HT_SUFFIX str2ptr
56 */
57 #ifndef HT_SUFFIX
58 #error HT_SUFFIX not defined
59 #endif
60
61 /*
62 * HT_KEY_TYPE
63 *
64 * The type of hashtable keys. This may be virtually anything, except for
65 * incomplete types. It is possible to use a pointer to a heap-allocated
66 * data structure here that is to be used as the actual key (see below). This
67 * is useful if you want to use strings, for example.
68 *
69 * Example:
70 *
71 * #define HT_KEY_TYPE char*
72 */
73 #ifndef HT_KEY_TYPE
74 #error HT_KEY_TYPE not defined
75 #endif
76
77 /*
78 * HT_KEY_CONST
79 *
80 * Optional.
81 *
82 * If defined, the 'const' qualifier will be prepended to the key type where
83 * appropriate. Useful for pointer types.
84 */
85 #ifdef HT_KEY_CONST
86 #undef HT_KEY_CONST
87 #define HT_KEY_CONST const
88 #else
89 #define HT_KEY_CONST
90 #endif
91
92 /*
93 * HT_KEY_TYPE
94 *
95 * The type of hashtable keys. This must be a complete, non-array type.
96 *
97 * Example:
98 *
99 * #define HT_VALUE_TYPE void*
100 */
101 #ifndef HT_VALUE_TYPE
102 #error HT_VALUE_TYPE not defined
103 #endif
104
105 /*
106 * HT_FUNC_HASH_KEY(key)
107 *
108 * A function-like macro used to compute an integer hash based on key. It must
109 * produce consistent results for every possible key, and collisions should be
110 * minimized to improve performance.
111 *
112 * Type of the key parameter is the same as HT_KEY_TYPE.
113 * Type of the result expression should be hash_t.
114 *
115 * Example:
116 *
117 * #define HT_FUNC_HASH_KEY(key) htutil_hashfunc_string(key)
118 */
119 #ifndef HT_FUNC_HASH_KEY
120 #error HT_FUNC_HASH_KEY not defined
121 #endif
122
123 /*
124 * HT_FUNC_KEYS_EQUAL(key1, key2)
125 *
126 * Optional.
127 *
128 * A function-like macro used to test two keys for equality. Note that if two
129 * keys are considered equal, they must also hash to the same value. However,
130 * the converse doesn't apply: equality of hashes doesn't imply equality of keys.
131 *
132 * Type of the key1 and key2 parameters is the same as HT_KEY_TYPE.
133 * The result expression must be true if the keys are equal, false otherwise.
134 *
135 * If no definition is provided, the standard == operator is used.
136 *
137 * Example:
138 *
139 * #define HT_FUNC_KEYS_EQUAL(key1, key2) (!strcmp(key1, key2))
140 */
141 #ifndef HT_FUNC_KEYS_EQUAL
142 #define HT_FUNC_KEYS_EQUAL(key1, key2) ((key1) == (key2))
143 #endif
144
145 /*
146 * HT_FUNC_COPY_KEY(dst, src)
147 *
148 * Optional.
149 *
150 * A function-like macro that specifies how copying a key should be handled.
151 * This is useful with pointers to variably-sized structures, such as strings.
152 *
153 * Type of src is the same as HT_KEY_TYPE.
154 * dst is a pointer to HT_KEY_TYPE.
155 * Type of the result expression is ignored.
156 *
157 * If no definition is provided, the key is simply copied by value.
158 *
159 * Example:
160 *
161 * #define HT_FUNC_COPY_KEY(dst, src) (*(dst) = strdup(src))
162 */
163 #ifndef HT_FUNC_COPY_KEY
164 #define HT_FUNC_COPY_KEY(dst, src) (*(dst) = (src))
165 #endif
166
167 /*
168 * HT_FUNC_FREE_KEY(key)
169 *
170 * Optional.
171 *
172 * A function-like macro used to free any resources allocated for a key, if any.
173 * Use this to clean up after after any allocations done by HT_FUNC_COPY_KEY.
174 *
175 * Example:
176 *
177 * #define HT_FUNC_FREE_KEY(key) free(key)
178 */
179 #ifndef HT_FUNC_FREE_KEY
180 #define HT_FUNC_FREE_KEY(key) ((void)(key))
181 #endif
182
183 /*
184 * HT_THREAD_SAFE
185 *
186 * Optional.
187 *
188 * If defined, most hashtable operations will be guarded by a readers-writer lock,
189 * protecting them from data races when used by multiple threads. This has some
190 * impact on performance and memory usage, however.
191 *
192 * Some additional APIs are provided in this mode, as well as unsafe versions of
193 * some of the core APIs. They are documented below.
194 *
195 * The unsafe variants behave identically to their core counterparts, but they
196 * completely bypass the locking mechanism, making them somewhat faster.
197 *
198 * Example:
199 *
200 * #define HT_THREAD_SAFE
201 */
202 #ifndef HT_THREAD_SAFE
203 // no default needed
204 #endif
205
206 /*
207 * HT_DECL, HT_IMPL
208 *
209 * You must define at least one of these macros.
210 *
211 * If HT_DECL is defined, this header will generate declarations of the API types
212 * and functions. This should be used in headers.
213 *
214 * If HT_IMPL is defined, this header will generate definitions of the API functions.
215 *
216 * * Example:
217 *
218 * #define HT_IMPL
219 * #include "hashtable.inc.h"
220 */
221 #if !defined(HT_DECL) && !defined(HT_IMPL)
222 #error neither HT_DECL nor HT_IMPL defined
223 #endif
224
225 /*
226 * HT_MIN_SIZE
227 *
228 * Optional.
229 *
230 * How many buckets to allocate initially. Higher values increase memory usage,
231 * but may improve performance. Must be a power of 2.
232 */
233 #ifndef HT_MIN_SIZE
234 #define HT_MIN_SIZE 4
235 #endif
236
237 static_assert((HT_MIN_SIZE & (~HT_MIN_SIZE + 1)) == HT_MIN_SIZE, "HT_MIN_SIZE must be power of two");
238
239 /*
240 * The following macros comprise the core of the templating machinery.
241 * They are used to construct identifiers augmented with HT_SUFFIX.
242 */
243
244 #define _HT_NAME_INNER1(suffix, name) ht_##suffix##name
245 #define _HT_NAME_INNER2(suffix, name) _HT_NAME_INNER1(suffix, name)
246 #define HT_NAME(name) _HT_NAME_INNER2(HT_SUFFIX, name)
247
248 #define _HT_PRIV_NAME_INNER1(suffix, name) _ht_##suffix##name
249 #define _HT_PRIV_NAME_INNER2(suffix, name) _HT_PRIV_NAME_INNER1(suffix, name)
250 #define HT_PRIV_NAME(name) _HT_PRIV_NAME_INNER2(HT_SUFFIX, name)
251
252 #define HT_TYPE(name) HT_NAME(_##name##_t)
253 #define HT_FUNC(name) HT_NAME(_##name)
254 #define HT_PRIV_FUNC(name) HT_PRIV_NAME(_##name)
255 #define HT_BASETYPE HT_NAME(_t)
256
257 #define HT_DECLARE_FUNC(return_type, name, arguments) return_type HT_FUNC(name) arguments
258 #define HT_DECLARE_PRIV_FUNC(return_type, name, arguments) static return_type HT_PRIV_FUNC(name) arguments
259
260 /****************\
261 * Declarations *
262 \****************/
263 #ifdef HT_DECL
264
265 /*
266 * ht_XXX_t
267 *
268 * A structure representing a hash table.
269 * All its fields are considered private and should be left unmolested,
270 * please use the API functiosn instead of manipulating these directly.
271 */
272 typedef struct HT_BASETYPE HT_BASETYPE;
273
274 /*
275 * ht_XXX_key_t
276 *
277 * An alias for the key type (HT_KEY_TYPE).
278 */
279 typedef HT_KEY_TYPE HT_TYPE(key);
280
281 /*
282 * ht_XXX_const_key_t
283 *
284 * An alias for the key type (HT_KEY_TYPE).
285 *
286 * If HT_KEY_CONST is defined, this type has the 'const' qualifier.
287 */
288 typedef HT_KEY_CONST HT_KEY_TYPE HT_TYPE(const_key);
289
290 /*
291 * ht_XXX_value_t
292 *
293 * An alias for the value type (HT_VALUE_TYPE).
294 */
295 typedef HT_VALUE_TYPE HT_TYPE(value);
296
297 /*
298 * ht_XXX_key_list_t
299 *
300 * A list of keys.
301 */
302 typedef struct HT_TYPE(key_list) HT_TYPE(key_list);
303
304 /*
305 * ht_XXX_foreach_callback_t
306 *
307 * Pointer to a callback function for use with ht_XXX_foreach().
308 */
309 typedef void* (*HT_TYPE(foreach_callback))(HT_TYPE(const_key) key, HT_TYPE(value) data, void *arg);
310
311 /*
312 * ht_XXX_iter_t
313 *
314 * An iterator structure. See ht_XXX_iter_begin().
315 */
316 typedef struct HT_TYPE(iter) HT_TYPE(iter);
317
318 /*
319 * Forward declaration of the private element struct.
320 */
321 typedef struct HT_TYPE(element) HT_TYPE(element);
322
323 /*
324 * Definition for ht_XXX_key_list_t.
325 */
326 struct HT_TYPE(key_list) {
327 LIST_INTERFACE(HT_TYPE(key_list));
328 HT_TYPE(key) key;
329 };
330
331 /*
332 * Definition for ht_XXX_t.
333 * All of these fields are to be considered private.
334 */
335 struct HT_BASETYPE {
336 HT_TYPE(element) *elements;
337 ht_size_t num_elements_occupied;
338 ht_size_t num_elements_allocated;
339 ht_size_t max_psl;
340 hash_t hash_mask;
341
342 #ifdef HT_THREAD_SAFE
343 struct {
344 SDL_mutex *mutex;
345 SDL_cond *cond;
346 uint readers;
347 bool writing;
348 } sync;
349 #endif
350 };
351
352 /*
353 * Definition for ht_XXX_iter_t.
354 */
HT_TYPE(iter)355 struct HT_TYPE(iter) {
356 HT_BASETYPE *hashtable;
357 HT_TYPE(key) key;
358 HT_TYPE(value) value;
359 bool has_data;
360
361 struct {
362 ht_size_t i;
363 ht_size_t remaining;
364 } private;
365 };
366
367 /*
368 * void ht_XXX_create(ht_XXX_t *ht);
369 *
370 * Initialize a hashtable structure. Must be called before any other API function.
371 */
372 HT_DECLARE_FUNC(void, create, (HT_BASETYPE *ht))
373 attr_nonnull(1);
374
375 /*
376 * void ht_XXX_destroy(ht_XXX_t *ht);
377 *
378 * Destroy a hashtable, freeing all resources allocated to it.
379 * Other API functions, except for ht_XXX_create(), must not be called after this.
380 * Does not free the memory pointed to by [ht], as it's not necessarily heap-allocated.
381 */
382 HT_DECLARE_FUNC(void, destroy, (HT_BASETYPE *ht))
383 attr_nonnull(1);
384
385 /*
386 * ht_XXX_t* ht_XXX_new(void);
387 *
388 * Convenience function; allocates and initializes a new hashtable structure.
389 * You must free() it manually when you're done with it (but don't forget to
390 * ht_*_destroy() it as well).
391 *
392 * Returns the allocated hashtable structure.
393 */
394 INLINE attr_returns_allocated
395 HT_DECLARE_FUNC(HT_BASETYPE*, new, (void)) {
396 HT_BASETYPE *ht = calloc(1, sizeof(HT_BASETYPE));
397 HT_FUNC(create)(ht);
398 return ht;
399 }
400
401 #ifdef HT_THREAD_SAFE
402
403 /*
404 * void ht_XXX_lock(ht_XXX_t *ht);
405 *
406 * Acquire a read lock on a hashtable. The hashtable is guarateed to stay unmodified
407 * while this lock is held. You must not try to modify it from the same thread; that
408 * will result in a deadlock. Other threads are able to read the hashtable while the
409 * lock is held.
410 */
411 HT_DECLARE_FUNC(void, lock, (HT_BASETYPE *ht))
412 attr_nonnull(1);
413
414 /*
415 * void ht_XXX_unlock(ht_XXX_t *ht);
416 *
417 * Release a read lock on a hashtable previously acquired via ht_XXX_unlock().
418 */
419 HT_DECLARE_FUNC(void, unlock, (HT_BASETYPE *ht))
420 attr_nonnull(1);
421
422 #endif // HT_THREAD_SAFE
423
424 /*
425 * ht_XXX_value_t ht_XXX_get(ht_XXX_t *ht, ht_XXX_const_key_t key, ht_XXX_const_value_t fallback);
426 *
427 * Retrieve a value associated with [key]. If there is no association, [fallback] will
428 * be returned instead.
429 */
430 HT_DECLARE_FUNC(HT_TYPE(value), get_prehashed, (HT_BASETYPE *ht, HT_TYPE(const_key) key, hash_t hash, HT_TYPE(value) fallback))
431 attr_nonnull(1);
432
433 attr_nonnull(1)
HT_TYPE(value)434 INLINE HT_DECLARE_FUNC(HT_TYPE(value), get, (HT_BASETYPE *ht, HT_TYPE(const_key) key, HT_TYPE(value) fallback)) {
435 return HT_FUNC(get_prehashed)(ht, key, HT_FUNC_HASH_KEY(key), fallback);
436 }
437
438 #ifdef HT_THREAD_SAFE
439 /*
440 * ht_XXX_value_t ht_XXX_get_unsafe(ht_XXX_t *ht, ht_XXX_const_key_t key, ht_XXX_const_value_t fallback);
441 *
442 * A non-thread-safe version of ht_XXX_get().
443 */
444 HT_DECLARE_FUNC(HT_TYPE(value), get_unsafe_prehashed, (HT_BASETYPE *ht, HT_TYPE(const_key) key, hash_t hash, HT_TYPE(value) fallback))
445 attr_nonnull(1);
446
447 attr_nonnull(1)
HT_TYPE(value)448 INLINE HT_DECLARE_FUNC(HT_TYPE(value), get_unsafe, (HT_BASETYPE *ht, HT_TYPE(const_key) key, HT_TYPE(value) fallback)) {
449 return HT_FUNC(get_unsafe_prehashed)(ht, key, HT_FUNC_HASH_KEY(key), fallback);
450 }
451
452 #endif // HT_THREAD_SAFE
453
454 /*
455 * bool ht_XXX_lookup(ht_XXX_t *ht, ht_XXX_const_key_t key, ht_XXX_value_t *out_value);
456 *
457 * Check whether a key exists in the hashtable. If it does and [out_value] is not NULL,
458 * then the value associated with it will be also copied into *out_value.
459 *
460 * Returns true if an entry is found, false otherwise.
461 */
462 HT_DECLARE_FUNC(bool, lookup_prehashed, (HT_BASETYPE *ht, HT_TYPE(const_key) key, hash_t hash, HT_TYPE(value) *out_value))
463 attr_nonnull(1) attr_nodiscard;
464
465 attr_nonnull(1) attr_nodiscard
466 INLINE HT_DECLARE_FUNC(bool, lookup, (HT_BASETYPE *ht, HT_TYPE(const_key) key, HT_TYPE(value) *out_value)) {
467 return HT_FUNC(lookup_prehashed)(ht, key, HT_FUNC_HASH_KEY(key), out_value);
468 }
469
470 #ifdef HT_THREAD_SAFE
471 /*
472 * bool ht_XXX_lookup_unsafe(ht_XXX_t *ht, ht_XXX_const_key_t key, ht_XXX_value_t *out_value);
473 *
474 * A non-thread-safe version of ht_XXX_lookup().
475 */
476 HT_DECLARE_FUNC(bool, lookup_unsafe_prehashed, (HT_BASETYPE *ht, HT_TYPE(const_key) key, hash_t hash, HT_TYPE(value) *out_value))
477 attr_nonnull(1) attr_nodiscard;
478
479 attr_nonnull(1) attr_nodiscard
480 INLINE HT_DECLARE_FUNC(bool, lookup_unsafe, (HT_BASETYPE *ht, HT_TYPE(const_key) key, HT_TYPE(value) *out_value)) {
481 return HT_FUNC(lookup_unsafe_prehashed)(ht, key, HT_FUNC_HASH_KEY(key), out_value);
482 }
483
484 #endif // HT_THREAD_SAFE
485
486 /*
487 * void ht_XXX_set(ht_XXX_t *ht, ht_XXX_const_key_t key, ht_XXX_const_value_t value);
488 *
489 * Store a key-value pair in the hashtable.
490 * If this key already has a value associated with it, it will be overwritten.
491 *
492 * Returns true if a new key was inserted, false if a previous value was overwritten.
493 */
494 HT_DECLARE_FUNC(bool, set, (HT_BASETYPE *ht, HT_TYPE(const_key) key, HT_TYPE(value) value))
495 attr_nonnull(1);
496
497 /*
498 * bool ht_XXX_try_set(ht_XXX_t ht, ht_XXX_const_key_t key, ht_XXX_const_value_t value, ht_XXX_value_t (*value_transform)(ht_XXX_value_t), ht_XXX_value_t *out_value);
499 *
500 * See if [key] exists in the hashtable, and then:
501 *
502 * if key exists, then:
503 * if out_value is not NULL, then:
504 * store value associated with key in *out_value;
505 *
506 * return false;
507 * else:
508 * if value_transform is not NULL, then:
509 * newValue = value_transform(value);
510 * else
511 * newValue = value;
512 *
513 * if out_value is not NULL, then:
514 * store newValue in *out_value;
515 *
516 * associate key with newValue;
517 *
518 * return true;
519 *
520 * With HT_THREAD_SAFE defined, this is an atomic operation: the algorithm holds
521 * a write lock for its whole duration.
522 */
523 HT_DECLARE_FUNC(bool, try_set_prehashed, (HT_BASETYPE *ht, HT_TYPE(const_key) key, hash_t hash, HT_TYPE(value) value, HT_TYPE(value) (*value_transform)(HT_TYPE(value)), HT_TYPE(value) *out_value))
524 attr_nonnull(1) attr_nodiscard;
525
526 INLINE HT_DECLARE_FUNC(bool, try_set, (HT_BASETYPE *ht, HT_TYPE(const_key) key, HT_TYPE(value) value, HT_TYPE(value) (*value_transform)(HT_TYPE(value)), HT_TYPE(value) *out_value)) {
527 return HT_FUNC(try_set_prehashed)(ht, key, HT_FUNC_HASH_KEY(key), value, value_transform, out_value);
528 }
529
530 /*
531 * bool ht_XXX_unset(ht_XXX_t *ht, ht_XXX_const_key_t key);
532 *
533 * If there's a value associated with key, remove that association and return true.
534 * Otherwise, return false.
535 */
536 HT_DECLARE_FUNC(bool, unset, (HT_BASETYPE *ht, HT_TYPE(const_key) key))
537 attr_nonnull(1);
538
539 /*
540 * void ht_XXX_unset_list(ht_XXX_t *ht, const ht_XXX_key_list_t *keylist);
541 *
542 * Functionally equality to calling ht_XXX_unset() for every key in keylist, but
543 * more efficient.
544 */
545 HT_DECLARE_FUNC(void, unset_list, (HT_BASETYPE *ht, const HT_TYPE(key_list) *key_list))
546 attr_nonnull(1);
547
548 /*
549 * void ht_XXX_unset_all(ht_XXX_t *ht);
550 *
551 * Empty the hashtable completely.
552 * Functionally equivalent to calling ht_XXX_unset() for every key in the table,
553 * but more efficient.
554 */
555 HT_DECLARE_FUNC(void, unset_all, (HT_BASETYPE *ht))
556 attr_nonnull(1);
557
558 /*
559 * void* ht_XXX_foreach(ht_XXX_t *ht, ht_XXX_callback_t callback, void *arg);
560 *
561 * Call callback(key, value, arg) for each key-value pair in the hashtable.
562 *
563 * If the callback returns anything other than a NULL pointer, the loop is broken
564 * early, and this function returns whatever the callback returned.
565 *
566 * Otherwise, this function returns NULL once every pair has been processed.
567 *
568 * WARNING: Do not try to modify the hashtable from inside the callback.
569 */
570 HT_DECLARE_FUNC(void*, foreach, (HT_BASETYPE *ht, HT_TYPE(foreach_callback) callback, void *arg))
571 attr_nonnull(1, 2);
572
573 /*
574 * void ht_XXX_iter_begin(ht_XXX_t *ht, ht_XXX_iter_t *iter);
575 * void ht_XXX_iter_next(ht_XXX_iter_t *iter);
576 * void ht_XXX_iter_end(ht_XXX_iter_t *iter);
577 *
578 * These functions are used to iterate over the key-value pairs stored in the
579 * hashtable without having to provide a callback function.
580 *
581 * Declare a ht_XXX_iter_t structure and use ht_XXX_iter_begin() to initialize it.
582 * Then, if iter->has_data is true, iter->key and iter->value are set to the key
583 * and value of the first pair in the hashtable, respectively.
584 *
585 * While iter->has_data is true, you can call ht_XXX_iter_next() to advance the
586 * iterator to the next pair. If there are no more pairs, ht_XXX_iter_next() sets
587 * iter->has_data to false. Otherwise, iter->key and iter->data are updated.
588 * Calling ht_XXX_iter_next() has no effect if iter->has_data is already false.
589 *
590 * You must call ht_XXX_iter_end() when you are done iterating.
591 *
592 * Example:
593 *
594 * ht_XXX_iter_t iter;
595 * ht_XXX_iter_begin(&my_hashtable, &iter);
596 *
597 * while(iter.has_data) {
598 * do_something_with(iter.key, iter.value);
599 *
600 * if(some_condition) {
601 * break;
602 * }
603 *
604 * ht_XXX_iter_next(&iter);
605 * }
606 *
607 * ht_XXX_iter_end(&iter);
608 *
609 * WARNING: Do not try to modify the hashtable while iterating over its contents.
610 *
611 * If HT_THREAD_SAFE is defined, ht_XXX_iter_begin() acquires a read lock, and
612 * ht_XXX_iter_end() releases it. Thus, the hashtable is guarateed to stay
613 * unmodified while any threads have an iterator active.
614 */
615 HT_DECLARE_FUNC(void, iter_begin, (HT_BASETYPE *ht, HT_TYPE(iter) *iter))
616 attr_nonnull(1, 2);
617
618 HT_DECLARE_FUNC(void, iter_next, (HT_TYPE(iter) *iter))
619 attr_nonnull(1);
620
621 HT_DECLARE_FUNC(void, iter_end, (HT_TYPE(iter) *iter))
622 attr_nonnull(1);
623
624 /*
625 * hash_t ht_XXX_hash(ht_XXX_const_key_t key);
626 *
627 * Compute the key's hash, suitable to pass to _prehashed functions.
628 */
629 INLINE HT_DECLARE_FUNC(hash_t, hash, (HT_TYPE(const_key) key)) {
630 return HT_FUNC_HASH_KEY(key);
631 }
632
633 #endif // HT_DECL
634
635 /*******************\
636 * Implementations *
637 \*******************/
638
639 // #define HT_IMPL
640 #ifdef HT_IMPL
641
HT_TYPE(element)642 struct HT_TYPE(element) {
643 HT_TYPE(value) value;
644 HT_TYPE(key) key;
645 hash_t hash;
646 };
647
648 inline
649 HT_DECLARE_PRIV_FUNC(ht_size_t, get_psl, (ht_size_t zero_idx, ht_size_t actual_idx, ht_size_t num_allocated)) {
650 // returns the probe sequence length from zero_idx to actual_idx
651
652 if(actual_idx < zero_idx) {
653 return num_allocated - zero_idx + actual_idx;
654 }
655
656 return actual_idx - zero_idx;
657 }
658
659 HT_DECLARE_PRIV_FUNC(ht_size_t, get_element_psl, (HT_BASETYPE *ht, HT_TYPE(element) *e)) {
660 return HT_PRIV_FUNC(get_psl)(e->hash & ht->hash_mask, e - ht->elements, ht->num_elements_allocated);
661 }
662
663 HT_DECLARE_PRIV_FUNC(void, dump, (HT_BASETYPE *ht)) {
664 #if 0
665 log_debug(" -- begin dump of hashtable %p --", (void*)ht);
666 for(ht_size_t i = 0; i < ht->num_elements_allocated; ++i) {
667 HT_TYPE(element) *e = ht->elements + i;
668
669 if(e->hash & HT_HASH_LIVE_BIT) {
670 ht_size_t psl = HT_PRIV_FUNC(get_element_psl)(ht, e);
671 log_debug("%.4i. 0x%08x [%"HT_KEY_FMT"] ==> [%"HT_VALUE_FMT"] PSL: %u", i, e->hash, HT_KEY_PRINTABLE(e->key), HT_VALUE_PRINTABLE(e->value), psl);
672 assert(psl <= ht->max_psl);
673 } else {
674 log_debug("%.4i. 0x%08x -- empty --", i, e->hash);
675 }
676
677 }
678 log_debug("Max PSL: %u", ht->max_psl);
679 log_debug(" -- end dump of hashtable %p --", (void*)ht);
680 #endif
681 }
682
683 HT_DECLARE_PRIV_FUNC(void, begin_write, (HT_BASETYPE *ht)) {
684 #ifdef HT_THREAD_SAFE
685 SDL_LockMutex(ht->sync.mutex);
686
687 while(ht->sync.writing || ht->sync.readers) {
688 SDL_CondWait(ht->sync.cond, ht->sync.mutex);
689 }
690
691 ht->sync.writing = true;
692 SDL_UnlockMutex(ht->sync.mutex);
693 #endif
694 }
695
696 HT_DECLARE_PRIV_FUNC(void, end_write, (HT_BASETYPE *ht)) {
697 #ifdef HT_THREAD_SAFE
698 SDL_LockMutex(ht->sync.mutex);
699 ht->sync.writing = false;
700 SDL_CondBroadcast(ht->sync.cond);
701 SDL_UnlockMutex(ht->sync.mutex);
702 #endif
703 }
704
705 HT_DECLARE_PRIV_FUNC(void, begin_read, (HT_BASETYPE *ht)) {
706 #ifdef HT_THREAD_SAFE
707 SDL_LockMutex(ht->sync.mutex);
708
709 while(ht->sync.writing) {
710 SDL_CondWait(ht->sync.cond, ht->sync.mutex);
711 }
712
713 ++ht->sync.readers;
714 SDL_UnlockMutex(ht->sync.mutex);
715 #endif
716 }
717
718 HT_DECLARE_PRIV_FUNC(void, end_read, (HT_BASETYPE *ht)) {
719 #ifdef HT_THREAD_SAFE
720 SDL_LockMutex(ht->sync.mutex);
721
722 if(!--ht->sync.readers) {
723 SDL_CondSignal(ht->sync.cond);
724 }
725
726 SDL_UnlockMutex(ht->sync.mutex);
727 #endif
728 }
729
730 #ifdef HT_THREAD_SAFE
731 HT_DECLARE_FUNC(void, lock, (HT_BASETYPE *ht)) {
732 HT_PRIV_FUNC(begin_read)(ht);
733 }
734
735 HT_DECLARE_FUNC(void, unlock, (HT_BASETYPE *ht)) {
736 HT_PRIV_FUNC(end_read)(ht);
737 }
738 #endif // HT_THREAD_SAFE
739
740 HT_DECLARE_FUNC(void, create, (HT_BASETYPE *ht)) {
741 ht_size_t size = HT_MIN_SIZE;
742
743 ht->elements = calloc(size, sizeof(*ht->elements));
744 ht->num_elements_allocated = size;
745 ht->num_elements_occupied = 0;
746 ht->hash_mask = size - 1;
747
748 #ifdef HT_THREAD_SAFE
749 ht->sync.writing = false;
750 ht->sync.readers = 0;
751 ht->sync.mutex = SDL_CreateMutex();
752 ht->sync.cond = SDL_CreateCond();
753 #endif
754 }
755
756 HT_DECLARE_FUNC(void, destroy, (HT_BASETYPE *ht)) {
757 HT_FUNC(unset_all)(ht);
758 #ifdef HT_THREAD_SAFE
759 SDL_DestroyCond(ht->sync.cond);
760 SDL_DestroyMutex(ht->sync.mutex);
761 #endif
762 free(ht->elements);
763 }
764
HT_TYPE(element)765 HT_DECLARE_PRIV_FUNC(HT_TYPE(element)*, find_element, (HT_BASETYPE *ht, HT_TYPE(const_key) key, hash_t hash)) {
766 hash_t hash_mask = ht->hash_mask;
767 ht_size_t i = hash & hash_mask;
768 ht_size_t attr_unused zero_idx = i;
769 ht_size_t probe_len = 0;
770 ht_size_t max_probe_len = ht->max_psl;
771 hash |= HT_HASH_LIVE_BIT;
772
773 HT_TYPE(element) *elements = ht->elements;
774
775 // log_debug("%p %08x [%"HT_KEY_FMT"]", (void*)ht, hash, HT_KEY_PRINTABLE(key));
776
777 for(;;) {
778 HT_TYPE(element) *e = elements + i;
779 hash_t e_hash = e->hash;
780 // log_debug("i=%u :: %08x", i, e_hash);
781
782 if(e_hash == hash && HT_FUNC_KEYS_EQUAL(key, e->key)) {
783 // log_debug("found at %u (probe_len = %u)", i, probe_len);
784 assert(probe_len == HT_PRIV_FUNC(get_element_psl)(ht, e));
785 return e;
786 }
787
788 if(!(e_hash & HT_HASH_LIVE_BIT)) {
789 // log_debug("not found (probe_len = %u)", probe_len);
790 return NULL;
791 }
792
793 ht_size_t e_probe_len = HT_PRIV_FUNC(get_element_psl)(ht, e);
794 assert(probe_len == HT_PRIV_FUNC(get_psl)(zero_idx, i, ht->num_elements_allocated));
795
796 if(probe_len > e_probe_len) {
797 // log_debug("[%"HT_KEY_FMT"] probe len at %u lower than current (%u < %u), bailing", HT_KEY_PRINTABLE(key), i, e_probe_len, probe_len);
798 return NULL;
799 }
800
801 if(++probe_len > max_probe_len) {
802 // log_debug("max PSL reached, bailing (%u)", max_probe_len);
803 return NULL;
804 }
805
806 i = (i + 1) & hash_mask;
807 }
808 }
809
HT_TYPE(value)810 HT_DECLARE_FUNC(HT_TYPE(value), get_prehashed, (HT_BASETYPE *ht, HT_TYPE(const_key) key, hash_t hash, HT_TYPE(value) fallback)) {
811 assert(hash == HT_FUNC_HASH_KEY(key));
812 HT_TYPE(value) value;
813
814 HT_PRIV_FUNC(begin_read)(ht);
815 HT_TYPE(element) *e = HT_PRIV_FUNC(find_element)(ht, key, hash);
816 value = e ? e->value : fallback;
817 HT_PRIV_FUNC(end_read)(ht);
818
819 return value;
820 }
821
822 #ifdef HT_THREAD_SAFE
HT_TYPE(value)823 HT_DECLARE_FUNC(HT_TYPE(value), get_unsafe_prehashed, (HT_BASETYPE *ht, HT_TYPE(const_key) key, hash_t hash, HT_TYPE(value) fallback)) {
824 assert(hash == HT_FUNC_HASH_KEY(key));
825 HT_TYPE(element) *e = HT_PRIV_FUNC(find_element)(ht, key, hash);
826 return e ? e->value : fallback;
827 }
828 #endif // HT_THREAD_SAFE
829
830 HT_DECLARE_FUNC(bool, lookup_prehashed, (HT_BASETYPE *ht, HT_TYPE(const_key) key, hash_t hash, HT_TYPE(value) *out_value)) {
831 assert(hash == HT_FUNC_HASH_KEY(key));
832 bool found = false;
833
834 HT_PRIV_FUNC(begin_read)(ht);
835 HT_TYPE(element) *e = HT_PRIV_FUNC(find_element)(ht, key, hash);
836
837 if(e != NULL) {
838 if(out_value != NULL) {
839 *out_value = e->value;
840 }
841
842 found = true;
843 }
844
845 HT_PRIV_FUNC(end_read)(ht);
846
847 return found;
848 }
849
850 #ifdef HT_THREAD_SAFE
851 HT_DECLARE_FUNC(bool, lookup_unsafe_prehashed, (HT_BASETYPE *ht, HT_TYPE(const_key) key, hash_t hash, HT_TYPE(value) *out_value)) {
852 assert(hash == HT_FUNC_HASH_KEY(key));
853 HT_TYPE(element) *e = HT_PRIV_FUNC(find_element)(ht, key, hash);
854
855 if(e != NULL) {
856 if(out_value != NULL) {
857 *out_value = e->value;
858 }
859
860 return true;
861 }
862
863 return false;
864 }
865 #endif // HT_THREAD_SAFE
866
867 HT_DECLARE_PRIV_FUNC(void, unset_all, (HT_BASETYPE *ht)) {
868 for(ht_size_t i = 0; i < ht->num_elements_allocated; ++i) {
869 HT_TYPE(element) *e = ht->elements + i;
870 if(e->hash & HT_HASH_LIVE_BIT) {
871 HT_FUNC_FREE_KEY(e->key);
872 e->hash = 0;
873
874 if(--ht->num_elements_occupied == 0) {
875 break;
876 }
877 }
878 }
879 }
880
881 HT_DECLARE_FUNC(void, unset_all, (HT_BASETYPE *ht)) {
882 HT_PRIV_FUNC(begin_write)(ht);
883 HT_PRIV_FUNC(unset_all)(ht);
884 HT_PRIV_FUNC(end_write)(ht);
885 }
886
887 HT_DECLARE_PRIV_FUNC(void, unset_with_backshift, (HT_BASETYPE *ht, HT_TYPE(element) *e)) {
888 HT_TYPE(element) *elements = ht->elements;
889 hash_t hash_mask = ht->hash_mask;
890
891 HT_FUNC_FREE_KEY(e->key);
892 --ht->num_elements_occupied;
893
894 ht_size_t idx = e - elements;
895 for(;;) {
896 idx = (idx + 1) & hash_mask;
897 HT_TYPE(element) *next_e = elements + idx;
898
899 if(HT_PRIV_FUNC(get_element_psl)(ht, next_e) < 1) {
900 e->hash = 0;
901 return;
902 }
903
904 *e = *next_e;
905 e = next_e;
906 }
907 }
908
909 HT_DECLARE_FUNC(bool, unset, (HT_BASETYPE *ht, HT_TYPE(const_key) key)) {
910 hash_t hash = HT_FUNC_HASH_KEY(key);
911 bool success = false;
912
913 HT_PRIV_FUNC(begin_write)(ht);
914 HT_TYPE(element) *e = HT_PRIV_FUNC(find_element)(ht, key, hash);
915 if(e != NULL) {
916 HT_PRIV_FUNC(unset_with_backshift)(ht, e);
917 success = true;
918 }
919 HT_PRIV_FUNC(end_write)(ht);
920
921 return success;
922 }
923
924 HT_DECLARE_FUNC(void, unset_list, (HT_BASETYPE *ht, const HT_TYPE(key_list) *key_list)) {
925 HT_PRIV_FUNC(begin_write)(ht);
926
927 for(const HT_TYPE(key_list) *i = key_list; i; i = i->next) {
928 hash_t hash = HT_FUNC_HASH_KEY(i->key);
929 HT_TYPE(element) *e = HT_PRIV_FUNC(find_element)(ht, i->key, hash);
930
931 if(e != NULL) {
932 HT_PRIV_FUNC(unset_with_backshift)(ht, e);
933 }
934 }
935
936 HT_PRIV_FUNC(end_write)(ht);
937 }
938
HT_TYPE(element)939 HT_DECLARE_PRIV_FUNC(HT_TYPE(element)*, insert, (
940 HT_TYPE(element) *insertion_elem,
941 HT_TYPE(element) *elements,
942 hash_t hash_mask,
943 ht_size_t *p_max_psl
944 )) {
945 HT_TYPE(element) *e, *target = NULL, temp_elem;
946 ht_size_t idx = insertion_elem->hash & hash_mask;
947
948 for(;;) {
949 e = elements + idx;
950
951 if(!(e->hash & HT_HASH_LIVE_BIT)) {
952 *e = *insertion_elem;
953 if(target == NULL) {
954 target = e;
955 }
956
957 ht_size_t psl = HT_PRIV_FUNC(get_psl)(e->hash & hash_mask, idx, hash_mask + 1);
958 if(*p_max_psl < psl) {
959 *p_max_psl = psl;
960 }
961
962 break;
963 }
964
965 ht_size_t e_probe_len = HT_PRIV_FUNC(get_psl)(e->hash & hash_mask, idx, hash_mask + 1);
966 ht_size_t i_probe_len = HT_PRIV_FUNC(get_psl)(insertion_elem->hash & hash_mask, idx, hash_mask + 1);
967
968 if(e_probe_len < i_probe_len) {
969 // log_debug("SWAP %u (%u < %u)", idx, e_probe_len, i_probe_len);
970 temp_elem = *e;
971 *e = *insertion_elem;
972 if(target == NULL) {
973 target = e;
974 }
975 *insertion_elem = temp_elem;
976
977 ht_size_t psl = HT_PRIV_FUNC(get_psl)(e->hash & hash_mask, idx, hash_mask + 1);
978 if(*p_max_psl < psl) {
979 *p_max_psl = psl;
980 }
981 }
982
983 idx = (idx + 1) & hash_mask;
984 }
985
986 return target;
987 }
988
989 HT_DECLARE_PRIV_FUNC(bool, set, (
990 HT_BASETYPE *ht,
991 hash_t hash,
992 HT_TYPE(const_key) key,
993 HT_TYPE(value) value,
994 HT_TYPE(value) (*transform_value)(HT_TYPE(value)),
995 bool allow_overwrite,
996 HT_TYPE(value) *out_value
997 )) {
998 HT_TYPE(element) *e = HT_PRIV_FUNC(find_element)(ht, key, hash);
999 if(e) {
1000 if(!allow_overwrite) {
1001 if(out_value != NULL) {
1002 *out_value = e->value;
1003 }
1004
1005 return false;
1006 }
1007
1008 if(transform_value != NULL) {
1009 value = transform_value(value);
1010 }
1011
1012 if(out_value != NULL) {
1013 *out_value = value;
1014 }
1015
1016 e->value = value;
1017
1018 // log_debug("[%"HT_KEY_FMT"] ==> [%"HT_VALUE_FMT"] (replace)", HT_KEY_PRINTABLE(key), HT_VALUE_PRINTABLE(value));
1019 return true;
1020 }
1021
1022 // log_debug("[%"HT_KEY_FMT"] ==> [%"HT_VALUE_FMT"]", HT_KEY_PRINTABLE(key), HT_VALUE_PRINTABLE(value));
1023
1024 // log_debug(" *** BEFORE ***");
1025 HT_PRIV_FUNC(dump)(ht);
1026
1027 if(transform_value != NULL) {
1028 value = transform_value(value);
1029 }
1030
1031 if(out_value != NULL) {
1032 *out_value = value;
1033 }
1034
1035 HT_TYPE(element) insertion_elem;
1036 insertion_elem.value = value;
1037 HT_FUNC_COPY_KEY(&insertion_elem.key, key);
1038 insertion_elem.hash = hash | HT_HASH_LIVE_BIT;
1039 e = HT_PRIV_FUNC(insert)(&insertion_elem, ht->elements, ht->hash_mask, &ht->max_psl);
1040 assume(e != NULL);
1041
1042 ++ht->num_elements_occupied;
1043
1044 // log_debug(" *** AFTER ***");
1045 HT_PRIV_FUNC(dump)(ht);
1046 // log_debug(" *** END SET ***");
1047
1048 assert(HT_PRIV_FUNC(find_element)(ht, key, hash) == e);
1049 assert(e->value == value);
1050 return true;
1051 }
1052
1053 HT_DECLARE_PRIV_FUNC(void, check_elem_count, (HT_BASETYPE *ht)) {
1054 #ifdef DEBUG
1055 ht_size_t num_elements = 0;
1056 for(ht_size_t i = 0; i < ht->num_elements_allocated; ++i) {
1057 if(ht->elements[i].hash & HT_HASH_LIVE_BIT) {
1058 ++num_elements;
1059 }
1060 }
1061 assert(num_elements == ht->num_elements_occupied);
1062 #endif // DEBUG
1063 }
1064
1065 HT_DECLARE_PRIV_FUNC(void, resize, (HT_BASETYPE *ht, size_t new_size)) {
1066 assert(new_size != ht->num_elements_allocated);
1067
1068 HT_TYPE(element) *old_elements = ht->elements;
1069 ht_size_t old_size = ht->num_elements_allocated;
1070 HT_PRIV_FUNC(check_elem_count)(ht);
1071
1072 HT_TYPE(element) *new_elements = calloc(new_size, sizeof(*ht->elements));
1073 ht->max_psl = 0;
1074
1075 for(ht_size_t i = 0; i < old_size; ++i) {
1076 HT_TYPE(element) *e = old_elements + i;
1077 if(e->hash & HT_HASH_LIVE_BIT) {
1078 HT_PRIV_FUNC(insert)(e, new_elements, new_size - 1, &ht->max_psl);
1079 }
1080 }
1081
1082 ht->elements = new_elements;
1083 ht->num_elements_allocated = new_size;
1084 ht->hash_mask = new_size - 1;
1085
1086 free(old_elements);
1087
1088 /*
1089 log_debug(
1090 "Resized hashtable at %p: %"PRIuMAX" -> %"PRIuMAX"",
1091 (void*)ht, (uintmax_t)old_size, (uintmax_t)new_size
1092 );
1093 */
1094
1095 HT_PRIV_FUNC(dump)(ht);
1096 HT_PRIV_FUNC(check_elem_count)(ht);
1097 }
1098
1099 HT_DECLARE_PRIV_FUNC(bool, maybe_resize, (HT_BASETYPE *ht)) {
1100 if(
1101 ht->num_elements_occupied == ht->num_elements_allocated || ht->max_psl > 5
1102 ) {
1103 HT_PRIV_FUNC(resize)(ht, ht->num_elements_allocated * 2);
1104 return true;
1105 }
1106
1107 return false;
1108 }
1109
1110 HT_DECLARE_FUNC(bool, set, (HT_BASETYPE *ht, HT_TYPE(const_key) key, HT_TYPE(value) value)) {
1111 hash_t hash = HT_FUNC_HASH_KEY(key);
1112
1113 HT_PRIV_FUNC(begin_write)(ht);
1114 bool result = HT_PRIV_FUNC(set)(ht, hash, key, value, NULL, true, NULL);
1115 HT_PRIV_FUNC(maybe_resize)(ht);
1116 HT_PRIV_FUNC(end_write)(ht);
1117
1118 return result;
1119 }
1120
1121 HT_DECLARE_FUNC(bool, try_set_prehashed, (HT_BASETYPE *ht, HT_TYPE(const_key) key, hash_t hash, HT_TYPE(value) value, HT_TYPE(value) (*value_transform)(HT_TYPE(value)), HT_TYPE(value) *out_value)) {
1122 assert(hash == HT_FUNC_HASH_KEY(key));
1123
1124 HT_PRIV_FUNC(begin_write)(ht);
1125 bool result = HT_PRIV_FUNC(set)(ht, hash, key, value, value_transform, false, out_value);
1126 HT_PRIV_FUNC(maybe_resize)(ht);
1127 HT_PRIV_FUNC(end_write)(ht);
1128 return result;
1129 }
1130
1131 HT_DECLARE_FUNC(void*, foreach, (HT_BASETYPE *ht, HT_TYPE(foreach_callback) callback, void *arg)) {
1132 void *ret = NULL;
1133
1134 HT_PRIV_FUNC(begin_read)(ht);
1135
1136 for(ht_size_t i = 0, remaining = ht->num_elements_occupied; remaining; ++i) {
1137 HT_TYPE(element) *e = ht->elements + i;
1138 if(e->hash & HT_HASH_LIVE_BIT) {
1139 if((ret = callback(e->key, e->value, arg))) {
1140 break;
1141 }
1142 --remaining;
1143 }
1144 }
1145
1146 HT_PRIV_FUNC(end_read)(ht);
1147 return ret;
1148 }
1149
1150 HT_DECLARE_FUNC(void, iter_begin, (HT_BASETYPE *ht, HT_TYPE(iter) *iter)) {
1151 HT_PRIV_FUNC(begin_read)(ht);
1152 memset(iter, 0, sizeof(*iter));
1153 iter->hashtable = ht;
1154 iter->private.remaining = ht->num_elements_occupied;
1155 iter->has_data = iter->private.remaining;
1156 HT_FUNC(iter_next)(iter);
1157 }
1158
1159 HT_DECLARE_FUNC(void, iter_next, (HT_TYPE(iter) *iter)) {
1160 HT_BASETYPE *ht = iter->hashtable;
1161
1162 if(!iter->private.remaining) {
1163 iter->has_data = false;
1164 return;
1165 }
1166
1167 for(;;) {
1168 ht_size_t i = iter->private.i++;
1169 assert(i < ht->num_elements_allocated);
1170 HT_TYPE(element) *e = ht->elements + i;
1171
1172 if(e->hash & HT_HASH_LIVE_BIT) {
1173 --iter->private.remaining;
1174 iter->key = e->key;
1175 iter->value = e->value;
1176 return;
1177 }
1178 }
1179
1180 return;
1181 }
1182
1183 HT_DECLARE_FUNC(void, iter_end, (HT_TYPE(iter) *iter)) {
1184 HT_PRIV_FUNC(end_read)(iter->hashtable);
1185 }
1186
1187 #endif // HT_IMPL
1188
1189 /***********\
1190 * Cleanup *
1191 \***********/
1192
1193 // Generated with:
1194 // $ sed -e 's/.*#define\s*//' -e 's/[^A-Z0-9_].*//' -e '/^$/d' -e 's/^/#undef /' hashtable.inc.h | sort -u
1195 // I'm sure this can be done much more efficiently, but I don't care to figure it out.
1196
1197 // It is intentional that it also catches the examples in comments.
1198 // The user-supplied macros have to be #undef'd too.
1199
1200 #undef HT_BASETYPE
1201 #undef HT_DECL
1202 #undef HT_DECLARE_FUNC
1203 #undef HT_DECLARE_PRIV_FUNC
1204 #undef HT_FUNC
1205 #undef HT_FUNC_COPY_KEY
1206 #undef HT_FUNC_FREE_KEY
1207 #undef HT_FUNC_HASH_KEY
1208 #undef HT_FUNC_KEYS_EQUAL
1209 #undef HT_IMPL
1210 #undef HT_KEY_CONST
1211 #undef HT_KEY_TYPE
1212 #undef HT_MIN_SIZE
1213 #undef HT_NAME
1214 #undef HT_PRIV_FUNC
1215 #undef HT_PRIV_NAME
1216 #undef HT_SUFFIX
1217 #undef HT_THREAD_SAFE
1218 #undef HT_TYPE
1219 #undef HT_VALUE_CONST
1220 #undef HT_VALUE_TYPE
1221 #undef _HT_NAME_INNER1
1222 #undef _HT_NAME_INNER2
1223 #undef _HT_PRIV_NAME_INNER1
1224 #undef _HT_PRIV_NAME_INNER2
1225 #undef HT_KEY_FMT
1226 #undef HT_KEY_PRINTABLE
1227 #undef HT_VALUE_FMT
1228 #undef HT_VALUE_PRINTABLE
1229