File Coverage

hashmap_generic.h
Criterion Covered Total %
statement 3949 5402 73.1
branch 2183 4120 52.9
condition n/a
subroutine n/a
pod n/a
total 6132 9522 64.4


line stmt bran cond sub pod time code
1             /*
2             * hashmap_generic.h — Macro-template for type-specialized hashmaps.
3             *
4             * Before including this file, define:
5             * HM_PREFIX — function name prefix (e.g., hashmap_ii)
6             * HM_NODE_TYPE — node struct name
7             * HM_MAP_TYPE — map struct name
8             *
9             * Key type (choose one):
10             * HM_KEY_IS_INT — define for integer keys
11             * (leave undefined for string keys: char* + uint32_t len + uint32_t hash)
12             *
13             * Value type (choose one):
14             * HM_VALUE_IS_STR — define for string values (char* + uint32_t len)
15             * HM_VALUE_IS_SV — define for opaque pointer values (void*, refcounted externally)
16             * (leave undefined for integer values)
17             *
18             * Integer width (optional, defaults to int64_t):
19             * HM_INT_TYPE — integer type (int32_t or int64_t)
20             * HM_INT_MIN — minimum value (sentinel: empty key)
21             * HM_INT_MAX — maximum value (for overflow checks)
22             *
23             * Optional:
24             * HM_HAS_COUNTERS — define to generate incr/decr functions (int values only)
25             */
26              
27             #include
28             #include
29             #include
30             #include
31             #include
32              
33             /* ---- Macro helpers ---- */
34              
35             #define HM_PASTE2(a, b) a##_##b
36             #define HM_PASTE(a, b) HM_PASTE2(a, b)
37             #define HM_FN(name) HM_PASTE(HM_PREFIX, name)
38              
39             /* ---- Integer type defaults ---- */
40              
41             #ifndef HM_INT_TYPE
42             #define HM_INT_TYPE int64_t
43             #define HM_INT_MIN INT64_MIN
44             #define HM_INT_MAX INT64_MAX
45             #endif
46              
47             /* ---- Constants ---- */
48              
49             #ifndef HM_INITIAL_CAPACITY
50             #define HM_INITIAL_CAPACITY 16
51             #endif
52              
53             #ifdef HM_KEY_IS_INT
54             #define HM_EMPTY_KEY HM_INT_MIN
55             #define HM_TOMBSTONE_KEY (HM_INT_MIN + 1)
56             #define HM_IS_RESERVED_KEY(k) ((k) == HM_EMPTY_KEY || (k) == HM_TOMBSTONE_KEY)
57             #endif
58              
59             /* ---- UTF-8 flag packing in uint32_t length ---- */
60              
61             #ifndef HM_UTF8_MACROS_DEFINED
62             #define HM_UTF8_MACROS_DEFINED
63             #define HM_UTF8_FLAG ((uint32_t)0x80000000U)
64             #define HM_LEN_MASK ((uint32_t)0x7FFFFFFFU)
65             #define HM_PACK_LEN(len, is_utf8) ((uint32_t)(len) | ((is_utf8) ? HM_UTF8_FLAG : 0))
66             #define HM_UNPACK_LEN(packed) ((uint32_t)((packed) & HM_LEN_MASK))
67             #define HM_UNPACK_UTF8(packed) (((packed) & HM_UTF8_FLAG) != 0)
68             #endif
69              
70             /* ---- LRU sentinel ---- */
71              
72             #ifndef HM_LRU_NONE_DEFINED
73             #define HM_LRU_NONE_DEFINED
74             #define HM_LRU_NONE UINT32_MAX
75             #endif
76              
77             /* ---- Hash functions ---- */
78              
79             #ifndef HM_HASH_FUNCTIONS_DEFINED
80             #define HM_HASH_FUNCTIONS_DEFINED
81              
82             /* xxHash64-like mix for integer keys */
83 3453537           static inline size_t hm_hash_int64(int64_t key) {
84 3453537           const uint64_t k1 = 0x9E3779B185EBCA87ULL;
85 3453537           const uint64_t k2 = 0xC2B2AE3D27D4EB4FULL;
86 3453537           uint64_t x = (uint64_t)key;
87 3453537           x ^= x >> 27;
88 3453537           x *= k1;
89 3453537           x ^= x >> 31;
90 3453537           x *= k2;
91 3453537           x ^= x >> 27;
92 3453537           return (size_t)x;
93             }
94              
95             /* xxHash32-inspired string hash */
96 860275           static inline uint32_t hm_hash_string(const char* data, uint32_t len) {
97 860275           const uint32_t prime1 = 0x9E3779B1U;
98 860275           const uint32_t prime2 = 0x85EBCA77U;
99 860275           const uint32_t prime3 = 0xC2B2AE3DU;
100 860275           const uint32_t prime4 = 0x27D4EB2FU;
101 860275           const uint32_t prime5 = 0x165667B1U;
102              
103             uint32_t h;
104 860275           const uint8_t* p = (const uint8_t*)data;
105 860275           const uint8_t* end = p + len;
106              
107 860275 50         if (len >= 16) {
108 0           uint32_t v1 = prime1 + prime2;
109 0           uint32_t v2 = prime2;
110 0           uint32_t v3 = 0;
111 0           uint32_t v4 = 0 - prime1;
112             do {
113             uint32_t k;
114 0           memcpy(&k, p, 4); v1 += k * prime2; v1 = (v1 << 13) | (v1 >> 19); v1 *= prime1; p += 4;
115 0           memcpy(&k, p, 4); v2 += k * prime2; v2 = (v2 << 13) | (v2 >> 19); v2 *= prime1; p += 4;
116 0           memcpy(&k, p, 4); v3 += k * prime2; v3 = (v3 << 13) | (v3 >> 19); v3 *= prime1; p += 4;
117 0           memcpy(&k, p, 4); v4 += k * prime2; v4 = (v4 << 13) | (v4 >> 19); v4 *= prime1; p += 4;
118 0 0         } while (p <= end - 16);
119 0           h = ((v1 << 1) | (v1 >> 31)) + ((v2 << 7) | (v2 >> 25)) +
120 0           ((v3 << 12) | (v3 >> 20)) + ((v4 << 18) | (v4 >> 14));
121             } else {
122 860275           h = prime5;
123             }
124              
125 860275           h += (uint32_t)len;
126              
127 1696809 100         while (p + 4 <= end) {
128             uint32_t k;
129 836534           memcpy(&k, p, 4);
130 836534           h += k * prime3;
131 836534           h = ((h << 17) | (h >> 15)) * prime4;
132 836534           p += 4;
133             }
134 2195090 100         while (p < end) {
135 1334815           h += (*p) * prime5;
136 1334815           h = ((h << 11) | (h >> 21)) * prime1;
137 1334815           p++;
138             }
139              
140 860275           h ^= h >> 15;
141 860275           h *= prime2;
142 860275           h ^= h >> 13;
143 860275           h *= prime3;
144 860275           h ^= h >> 16;
145              
146 860275           return h;
147             }
148              
149             #endif /* HM_HASH_FUNCTIONS_DEFINED */
150              
151             /* ---- Tombstone marker for string keys ---- */
152              
153             #ifndef HM_KEY_IS_INT
154             #ifndef HM_STR_TOMBSTONE_DEFINED
155             #define HM_STR_TOMBSTONE_DEFINED
156             static char hm_str_tombstone_marker;
157             #endif
158             #define HM_STR_TOMBSTONE (&hm_str_tombstone_marker)
159             #endif
160              
161             /* ---- Node struct ---- */
162              
163             typedef struct {
164             #ifdef HM_KEY_IS_INT
165             HM_INT_TYPE key;
166             #else
167             char* key; /* NULL = empty, HM_STR_TOMBSTONE = deleted */
168             uint32_t key_len; /* high bit = UTF-8 flag */
169             uint32_t key_hash;
170             #endif
171             #ifdef HM_VALUE_IS_STR
172             char* value;
173             uint32_t val_len; /* high bit = UTF-8 flag */
174             #elif defined(HM_VALUE_IS_SV)
175             void* value; /* opaque SV* — refcounted by caller */
176             #else
177             HM_INT_TYPE value;
178             #endif
179             } HM_NODE_TYPE;
180              
181             typedef struct {
182             HM_NODE_TYPE* nodes;
183             size_t capacity;
184             size_t size;
185             size_t tombstones;
186             size_t mask;
187             /* LRU fields (active only when max_size > 0) */
188             size_t max_size;
189             uint32_t lru_head;
190             uint32_t lru_tail;
191             uint32_t* lru_prev;
192             uint32_t* lru_next;
193             /* TTL fields (active only when default_ttl > 0) */
194             uint32_t default_ttl;
195             uint32_t* expires_at;
196             /* Iterator state for each() */
197             size_t iter_pos;
198             #ifdef HM_VALUE_IS_SV
199             void (*free_value_fn)(void*); /* SvREFCNT_dec callback */
200             #endif
201             } HM_MAP_TYPE;
202              
203             /* ---- Slot state macros ---- */
204              
205             #ifdef HM_KEY_IS_INT
206             #define HM_SLOT_IS_EMPTY(n) ((n)->key == HM_EMPTY_KEY)
207             #define HM_SLOT_IS_TOMBSTONE(n) ((n)->key == HM_TOMBSTONE_KEY)
208             #define HM_SLOT_IS_LIVE(n) (!HM_SLOT_IS_EMPTY(n) && !HM_SLOT_IS_TOMBSTONE(n))
209             #else
210             #define HM_SLOT_IS_EMPTY(n) ((n)->key == NULL)
211             #define HM_SLOT_IS_TOMBSTONE(n) ((n)->key == HM_STR_TOMBSTONE)
212             #define HM_SLOT_IS_LIVE(n) (!HM_SLOT_IS_EMPTY(n) && !HM_SLOT_IS_TOMBSTONE(n))
213             #endif
214              
215             /* ---- Init nodes ---- */
216              
217 1249           static inline void HM_FN(init_nodes)(HM_NODE_TYPE* nodes, size_t capacity) {
  18            
  16            
  11            
  11            
  85            
  82            
  90            
  91            
  82            
  360            
  121            
  84            
  80            
  118            
218             size_t i;
219 34814625 100         for (i = 0; i < capacity; i++) {
  306 100          
  272 100          
  187 100          
  187 100          
  2719957 100          
  2719906 100          
  2720042 100          
  2720059 100          
  2719906 100          
  10591896 100          
  4292953 100          
  2719940 100          
  1605792 100          
  2003222            
220             #ifdef HM_KEY_IS_INT
221 23933440           nodes[i].key = HM_EMPTY_KEY;
  256            
  176            
  176            
  2719824            
  2719824            
  10591536            
  4292832            
  1605712            
  2003104            
222             #else
223 10879936           nodes[i].key = NULL;
  288            
  2719872            
  2719952            
  2719968            
  2719856            
224 10879936           nodes[i].key_len = 0;
  288            
  2719872            
  2719952            
  2719968            
  2719856            
225 10879936           nodes[i].key_hash = 0;
  288            
  2719872            
  2719952            
  2719968            
  2719856            
226             #endif
227             #ifdef HM_VALUE_IS_STR
228 9765312           nodes[i].value = NULL;
  2719824            
  2719952            
  2719824            
  1605712            
229 9765312           nodes[i].val_len = 0;
  2719824            
  2719952            
  2719824            
  1605712            
230             #elif defined(HM_VALUE_IS_SV)
231 896           nodes[i].value = NULL;
  288            
  256            
  176            
  176            
232             #else
233 25047168           nodes[i].value = 0;
  2719872            
  2719968            
  10591536            
  4292832            
  2719856            
  2003104            
234             #endif
235             }
236 1249           }
  18            
  16            
  11            
  11            
  85            
  82            
  90            
  91            
  82            
  360            
  121            
  84            
  80            
  118            
237              
238             /* ---- Free resources for a single node ---- */
239              
240 1052763           static inline void HM_FN(free_node)(HM_MAP_TYPE* map, HM_NODE_TYPE* node) {
  27            
  24            
  19            
  19            
  80024            
  80012            
  80025            
  80032            
  80013            
  282523            
  150004            
  80024            
  60012            
  80005            
241             (void)map;
242             #ifndef HM_KEY_IS_INT
243 320132 50         if (node->key != NULL && node->key != HM_STR_TOMBSTONE) {
  27 50          
  80024 50          
  80025 50          
  80032 50          
  80024 50          
    50          
    50          
    50          
    50          
244 320132           free(node->key);
  27            
  80024            
  80025            
  80032            
  80024            
245 320132           node->key = NULL;
  27            
  80024            
  80025            
  80032            
  80024            
246             }
247             #endif
248             #ifdef HM_VALUE_IS_STR
249 300062 50         if (node->value != NULL) {
  80012 50          
  80025 50          
  80013 50          
  60012            
250 300062           free(node->value);
  80012            
  80025            
  80013            
  60012            
251 300062           node->value = NULL;
  80012            
  80025            
  80013            
  60012            
252             }
253             #elif defined(HM_VALUE_IS_SV)
254 89 50         if (node->value != NULL && map->free_value_fn) {
  27 50          
  24 50          
  19 50          
  19 50          
    50          
    50          
    50          
255 89           map->free_value_fn(node->value);
  27            
  24            
  19            
  19            
256 89           node->value = NULL;
  27            
  24            
  19            
  19            
257             }
258             #endif
259             (void)node;
260 1052763           }
  27            
  24            
  19            
  19            
  80024            
  80012            
  80025            
  80032            
  80013            
  282523            
  150004            
  80024            
  60012            
  80005            
261              
262             /* ---- LRU helpers ---- */
263              
264 2629           static inline void HM_FN(lru_unlink)(HM_MAP_TYPE* map, uint32_t idx) {
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  3            
  1            
  2612            
  1            
  3            
  1            
  1            
265 2629           uint32_t p = map->lru_prev[idx];
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  3            
  1            
  2612            
  1            
  3            
  1            
  1            
266 2629           uint32_t n = map->lru_next[idx];
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  3            
  1            
  2612            
  1            
  3            
  1            
  1            
267 2629 50         if (p != HM_LRU_NONE) map->lru_next[p] = n;
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  3 50          
  1 100          
  2612 50          
  1 50          
  3 50          
  1 50          
  1            
268 2           else map->lru_head = n;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
  0            
  0            
269 2629 50         if (n != HM_LRU_NONE) map->lru_prev[n] = p;
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 100          
  3 50          
  1 100          
  2612 50          
  1 100          
  3 50          
  1 50          
  1            
270 2618           else map->lru_tail = p;
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  2            
  1            
  2603            
  1            
  2            
  1            
  1            
271 2629           map->lru_prev[idx] = HM_LRU_NONE;
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  3            
  1            
  2612            
  1            
  3            
  1            
  1            
272 2629           map->lru_next[idx] = HM_LRU_NONE;
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  3            
  1            
  2612            
  1            
  3            
  1            
  1            
273 2629           }
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  3            
  1            
  2612            
  1            
  3            
  1            
  1            
274              
275 2837           static inline void HM_FN(lru_push_front)(HM_MAP_TYPE* map, uint32_t idx) {
  4            
  4            
  4            
  4            
  3            
  3            
  4            
  6            
  3            
  2785            
  4            
  6            
  3            
  4            
276 2837           map->lru_prev[idx] = HM_LRU_NONE;
  4            
  4            
  4            
  4            
  3            
  3            
  4            
  6            
  3            
  2785            
  4            
  6            
  3            
  4            
277 2837           map->lru_next[idx] = map->lru_head;
  4            
  4            
  4            
  4            
  3            
  3            
  4            
  6            
  3            
  2785            
  4            
  6            
  3            
  4            
278 2837 100         if (map->lru_head != HM_LRU_NONE)
  4 100          
  4 100          
  4 100          
  4 100          
  3 100          
  3 100          
  4 100          
  6 100          
  3 100          
  2785 100          
  4 100          
  6 100          
  3 100          
  4            
279 2805           map->lru_prev[map->lru_head] = idx;
  3            
  3            
  3            
  3            
  2            
  2            
  3            
  5            
  2            
  2766            
  3            
  5            
  2            
  3            
280             else
281 32           map->lru_tail = idx;
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  19            
  1            
  1            
  1            
  1            
282 2837           map->lru_head = idx;
  4            
  4            
  4            
  4            
  3            
  3            
  4            
  6            
  3            
  2785            
  4            
  6            
  3            
  4            
283 2837           }
  4            
  4            
  4            
  4            
  3            
  3            
  4            
  6            
  3            
  2785            
  4            
  6            
  3            
  4            
284              
285 127           static inline void HM_FN(lru_promote)(HM_MAP_TYPE* map, uint32_t idx) {
  1            
  1            
  1            
  1            
  0            
  0            
  1            
  2            
  1            
  117            
  0            
  2            
  0            
  0            
286 127 50         if (map->lru_head == idx) return;
  1 50          
  1 50          
  1 50          
  1 0          
  0 0          
  0 50          
  1 50          
  2 50          
  1 100          
  117 0          
  0 50          
  2 0          
  0 0          
  0            
287 115           HM_FN(lru_unlink)(map, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  111            
  0            
  2            
  0            
  0            
288 115           HM_FN(lru_push_front)(map, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  111            
  0            
  2            
  0            
  0            
289             }
290              
291             /* Tombstone a node at a known index (used by LRU eviction and TTL expiry) */
292 1052573           static void HM_FN(tombstone_at)(HM_MAP_TYPE* map, size_t index) {
  5            
  5            
  4            
  4            
  80003            
  80002            
  80005            
  80005            
  80002            
  282523            
  150004            
  80004            
  60002            
  80005            
293 1052573           HM_FN(free_node)(map, &map->nodes[index]);
  5            
  5            
  4            
  4            
  80003            
  80002            
  80005            
  80005            
  80002            
  282523            
  150004            
  80004            
  60002            
  80005            
294             #ifdef HM_KEY_IS_INT
295 732551           map->nodes[index].key = HM_TOMBSTONE_KEY;
  5            
  4            
  4            
  80002            
  80002            
  282523            
  150004            
  60002            
  80005            
296             #else
297 320022           map->nodes[index].key = HM_STR_TOMBSTONE;
  5            
  80003            
  80005            
  80005            
  80004            
298 320022           map->nodes[index].key_len = 0;
  5            
  80003            
  80005            
  80005            
  80004            
299 320022           map->nodes[index].key_hash = 0;
  5            
  80003            
  80005            
  80005            
  80004            
300             #endif
301             #ifdef HM_VALUE_IS_STR
302 300011           map->nodes[index].val_len = 0;
  80002            
  80005            
  80002            
  60002            
303             #endif
304 1052573 100         if (map->expires_at) map->expires_at[index] = 0;
  5 100          
  5 100          
  4 100          
  4 50          
  80003 50          
  80002 100          
  80005 100          
  80005 50          
  80002 100          
  282523 100          
  150004 100          
  80004 50          
  60002 100          
  80005            
305 1052573           map->size--;
  5            
  5            
  4            
  4            
  80003            
  80002            
  80005            
  80005            
  80002            
  282523            
  150004            
  80004            
  60002            
  80005            
306 1052573           map->tombstones++;
  5            
  5            
  4            
  4            
  80003            
  80002            
  80005            
  80005            
  80002            
  282523            
  150004            
  80004            
  60002            
  80005            
307 1052573           }
  5            
  5            
  4            
  4            
  80003            
  80002            
  80005            
  80005            
  80002            
  282523            
  150004            
  80004            
  60002            
  80005            
308              
309             /* Evict the LRU tail entry */
310 2507           static void HM_FN(lru_evict_one)(HM_MAP_TYPE* map) {
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  2494            
  1            
  1            
  1            
  1            
311 2507           uint32_t victim = map->lru_tail;
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  2494            
  1            
  1            
  1            
  1            
312 2507 50         if (victim == HM_LRU_NONE) return;
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  2494 50          
  1 50          
  1 50          
  1 50          
  1            
313 2507           HM_FN(lru_unlink)(map, victim);
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  2494            
  1            
  1            
  1            
  1            
314 2507           HM_FN(tombstone_at)(map, (size_t)victim);
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  2494            
  1            
  1            
  1            
  1            
315             }
316              
317             /* Forward declaration (needed by expire_at) */
318             static bool HM_FN(compact)(HM_MAP_TYPE* map);
319              
320             /* Expire a TTL'd entry at a known index, with compact check */
321 24           static void HM_FN(expire_at)(HM_MAP_TYPE* map, size_t index) {
  2            
  2            
  1            
  1            
  0            
  0            
  2            
  1            
  0            
  12            
  1            
  1            
  0            
  1            
322 24 50         if (map->lru_prev) HM_FN(lru_unlink)(map, (uint32_t)index);
  2 50          
  2 50          
  1 50          
  1 0          
  0 0          
  0 50          
  2 50          
  1 0          
  0 100          
  12 50          
  1 50          
  1 0          
  0 50          
  1            
323 24           HM_FN(tombstone_at)(map, index);
  2            
  2            
  1            
  1            
  0            
  0            
  2            
  1            
  0            
  12            
  1            
  1            
  0            
  1            
324 24 50         if (map->tombstones > map->capacity / 4 ||
  2 50          
  2 50          
  1 50          
  1 0          
  0 0          
  0 50          
  2 50          
  1 0          
  0 50          
  12 50          
  1 50          
  1 0          
  0 50          
  1            
325 24 100         (map->size > 0 && map->tombstones > map->size)) {
  2 50          
  2 100          
  1 50          
  1 50          
  0 0          
  0 50          
  2 0          
  1 0          
  0 0          
  12 0          
  1 0          
  1 100          
  0 50          
  1 50          
    0          
    0          
    0          
    100          
    50          
    50          
    0          
    50          
    0          
    0          
    0          
    50          
    0          
326 0           HM_FN(compact)(map);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
327             }
328 24           }
  2            
  2            
  1            
  1            
  0            
  0            
  2            
  1            
  0            
  12            
  1            
  1            
  0            
  1            
329              
330             /* ---- Create / Destroy ---- */
331              
332 227           static HM_MAP_TYPE* HM_FN(create)(size_t max_size, uint32_t default_ttl) {
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
333 227           HM_MAP_TYPE* map = (HM_MAP_TYPE*)malloc(sizeof(HM_MAP_TYPE));
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
334 227 50         if (!map) return NULL;
  17 50          
  15 50          
  10 50          
  10 50          
  12 50          
  8 50          
  16 50          
  18 50          
  9 50          
  61 50          
  17 50          
  11 50          
  8 50          
  15            
335              
336 227           map->capacity = HM_INITIAL_CAPACITY;
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
337 227           map->mask = HM_INITIAL_CAPACITY - 1;
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
338 227           map->size = 0;
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
339 227           map->tombstones = 0;
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
340 227           map->max_size = max_size;
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
341 227           map->default_ttl = default_ttl;
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
342 227           map->lru_head = HM_LRU_NONE;
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
343 227           map->lru_tail = HM_LRU_NONE;
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
344 227           map->lru_prev = NULL;
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
345 227           map->lru_next = NULL;
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
346 227           map->iter_pos = 0;
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
347             #ifdef HM_VALUE_IS_SV
348 52           map->free_value_fn = NULL;
  17            
  15            
  10            
  10            
349             #endif
350 227           map->expires_at = NULL;
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
351              
352 227           map->nodes = (HM_NODE_TYPE*)malloc(map->capacity * sizeof(HM_NODE_TYPE));
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
353 227 50         if (!map->nodes) { free(map); return NULL; }
  17 50          
  15 50          
  10 50          
  10 50          
  12 50          
  8 50          
  16 50          
  18 50          
  9 50          
  61 50          
  17 50          
  11 50          
  8 50          
  15            
354 227           HM_FN(init_nodes)(map->nodes, map->capacity);
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
355              
356 227 100         if (max_size > 0) {
  17 100          
  15 100          
  10 100          
  10 100          
  12 100          
  8 100          
  16 100          
  18 100          
  9 100          
  61 100          
  17 100          
  11 100          
  8 100          
  15            
357 32           map->lru_prev = (uint32_t*)malloc(map->capacity * sizeof(uint32_t));
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  19            
  1            
  1            
  1            
  1            
358 32           map->lru_next = (uint32_t*)malloc(map->capacity * sizeof(uint32_t));
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  19            
  1            
  1            
  1            
  1            
359 32 50         if (!map->lru_prev || !map->lru_next) {
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  19 50          
  1 50          
  1 50          
  1 50          
  1 50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
360 0           free(map->lru_prev); free(map->lru_next);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
361 0           free(map->nodes); free(map);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
362 0           return NULL;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
363             }
364 32           memset(map->lru_prev, 0xFF, map->capacity * sizeof(uint32_t));
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  19            
  1            
  1            
  1            
  1            
365 32           memset(map->lru_next, 0xFF, map->capacity * sizeof(uint32_t));
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  1            
  19            
  1            
  1            
  1            
  1            
366             }
367              
368 227 100         if (default_ttl > 0) {
  17 100          
  15 100          
  10 100          
  10 50          
  12 50          
  8 100          
  16 100          
  18 50          
  9 100          
  61 50          
  17 100          
  11 50          
  8 100          
  15            
369 23           map->expires_at = (uint32_t*)calloc(map->capacity, sizeof(uint32_t));
  1            
  1            
  1            
  1            
  0            
  0            
  1            
  1            
  0            
  15            
  0            
  1            
  0            
  1            
370 23 50         if (!map->expires_at) {
  1 50          
  1 50          
  1 50          
  1 0          
  0 0          
  0 50          
  1 50          
  1 0          
  0 50          
  15 0          
  0 50          
  1 0          
  0 50          
  1            
371 0           free(map->lru_prev); free(map->lru_next);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
372 0           free(map->nodes); free(map);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
373 0           return NULL;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
374             }
375             }
376              
377 227           return map;
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
378             }
379              
380 227           static void HM_FN(destroy)(HM_MAP_TYPE* map) {
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
381 227 50         if (!map) return;
  17 50          
  15 50          
  10 50          
  10 50          
  12 50          
  8 50          
  16 50          
  18 50          
  9 50          
  61 50          
  17 50          
  11 50          
  8 50          
  15            
382             #if !defined(HM_KEY_IS_INT) || defined(HM_VALUE_IS_STR) || defined(HM_VALUE_IS_SV)
383             {
384             size_t i;
385 968710 100         for (i = 0; i < map->capacity; i++) {
  289 100          
  255 100          
  170 100          
  170 100          
  147628 100          
  147560 100          
  147696 100          
  147730 100          
  147577 100          
  147611 100          
  82024            
386 968576 100         if (HM_SLOT_IS_LIVE(&map->nodes[i])) {
  272 100          
  240 100          
  160 100          
  160 100          
  147616 100          
  147552 100          
  147680 100          
  147712 100          
  147568 100          
  147600 100          
  82016 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
387 183           HM_FN(free_node)(map, &map->nodes[i]);
  21            
  18            
  14            
  14            
  21            
  9            
  19            
  27            
  11            
  20            
  9            
388             }
389             }
390             }
391             #endif
392 227           free(map->lru_prev);
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
393 227           free(map->lru_next);
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
394 227           free(map->expires_at);
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
395 227           free(map->nodes);
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
396 227           free(map);
  17            
  15            
  10            
  10            
  12            
  8            
  16            
  18            
  9            
  61            
  17            
  11            
  8            
  15            
397             }
398              
399             /* ---- clear: remove all entries without destroying the map ---- */
400              
401 13           static void HM_FN(clear)(HM_MAP_TYPE* map) {
  1            
  1            
  1            
  1            
  0            
  1            
  1            
  0            
  0            
  4            
  1            
  0            
  1            
  1            
402 13 50         if (!map) return;
  1 50          
  1 50          
  1 50          
  1 0          
  0 50          
  1 50          
  1 0          
  0 0          
  0 50          
  4 50          
  1 0          
  0 50          
  1 50          
  1            
403             #if !defined(HM_KEY_IS_INT) || defined(HM_VALUE_IS_STR) || defined(HM_VALUE_IS_SV)
404             {
405             size_t i;
406 119 100         for (i = 0; i < map->capacity; i++) {
  17 100          
  17 100          
  17 100          
  17 0          
  0 100          
  17 100          
  17 0          
  0 0          
  0 0          
  0 100          
  17            
407 112 100         if (HM_SLOT_IS_LIVE(&map->nodes[i]))
  16 50          
  16 100          
  16 50          
  16 100          
  0 50          
  16 100          
  16 50          
  0 0          
  0 0          
  0 100          
  16 50          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
408 7           HM_FN(free_node)(map, &map->nodes[i]);
  1            
  1            
  1            
  1            
  0            
  1            
  1            
  0            
  0            
  0            
  1            
409             }
410             }
411             #endif
412 13           HM_FN(init_nodes)(map->nodes, map->capacity);
  1            
  1            
  1            
  1            
  0            
  1            
  1            
  0            
  0            
  4            
  1            
  0            
  1            
  1            
413 13           map->size = 0;
  1            
  1            
  1            
  1            
  0            
  1            
  1            
  0            
  0            
  4            
  1            
  0            
  1            
  1            
414 13           map->tombstones = 0;
  1            
  1            
  1            
  1            
  0            
  1            
  1            
  0            
  0            
  4            
  1            
  0            
  1            
  1            
415 13           map->iter_pos = 0;
  1            
  1            
  1            
  1            
  0            
  1            
  1            
  0            
  0            
  4            
  1            
  0            
  1            
  1            
416 13 50         if (map->lru_prev) {
  1 50          
  1 50          
  1 50          
  1 0          
  0 50          
  1 50          
  1 0          
  0 0          
  0 100          
  4 50          
  1 0          
  0 50          
  1 50          
  1            
417 1           memset(map->lru_prev, 0xFF, map->capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
418 1           memset(map->lru_next, 0xFF, map->capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
419 1           map->lru_head = HM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
420 1           map->lru_tail = HM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
421             }
422 13 50         if (map->expires_at)
  1 50          
  1 50          
  1 50          
  1 0          
  0 50          
  1 50          
  1 0          
  0 0          
  0 100          
  4 50          
  1 0          
  0 50          
  1 50          
  1            
423 1           memset(map->expires_at, 0, map->capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
424             }
425              
426             /* ---- Rehash: unified resize (grow=true) and compact (grow=false) ---- */
427              
428 1009           static bool HM_FN(rehash)(HM_MAP_TYPE* map, bool grow) {
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
429 1009           size_t old_capacity = map->capacity;
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
430 1009           HM_NODE_TYPE* old_nodes = map->nodes;
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
431              
432 1009 0         size_t new_capacity = grow ? old_capacity * 2 : old_capacity;
  0 0          
  0 0          
  0 0          
  0 100          
  73 100          
  73 100          
  73 100          
  73 100          
  73 100          
  295 100          
  103 100          
  73 100          
  71 100          
  102            
433 1009           size_t new_mask = new_capacity - 1;
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
434 1009           HM_NODE_TYPE* new_nodes = (HM_NODE_TYPE*)malloc(new_capacity * sizeof(HM_NODE_TYPE));
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
435 1009 0         if (!new_nodes) return false;
  0 0          
  0 0          
  0 0          
  0 50          
  73 50          
  73 50          
  73 50          
  73 50          
  73 50          
  295 50          
  103 50          
  73 50          
  71 50          
  102            
436              
437             /* Allocate new LRU arrays if active */
438 1009           uint32_t* new_lru_prev = NULL;
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
439 1009           uint32_t* new_lru_next = NULL;
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
440 1009           uint32_t* old_to_new = NULL;
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
441 1009 0         if (map->lru_prev) {
  0 0          
  0 0          
  0 0          
  0 50          
  73 50          
  73 50          
  73 50          
  73 50          
  73 100          
  295 50          
  103 50          
  73 50          
  71 50          
  102            
442 150           new_lru_prev = (uint32_t*)malloc(new_capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
443 150           new_lru_next = (uint32_t*)malloc(new_capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
444 150           old_to_new = (uint32_t*)malloc(old_capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
445 150 0         if (!new_lru_prev || !new_lru_next || !old_to_new) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  150 0          
  0 0          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
446 0           free(new_nodes); free(new_lru_prev); free(new_lru_next); free(old_to_new);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
447 0           return false;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
448             }
449 150           memset(new_lru_prev, 0xFF, new_capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
450 150           memset(new_lru_next, 0xFF, new_capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
451 150           memset(old_to_new, 0xFF, old_capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
452             }
453              
454             /* Allocate new TTL array if active */
455 1009           uint32_t* new_expires_at = NULL;
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
456 1009 0         if (map->expires_at) {
  0 0          
  0 0          
  0 0          
  0 50          
  73 50          
  73 50          
  73 50          
  73 50          
  73 50          
  295 50          
  103 50          
  73 50          
  71 50          
  102            
457 0           new_expires_at = (uint32_t*)calloc(new_capacity, sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
458 0 0         if (!new_expires_at) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
459 0           free(new_nodes); free(new_lru_prev); free(new_lru_next); free(old_to_new);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
460 0           return false;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
461             }
462             }
463              
464 1009           HM_FN(init_nodes)(new_nodes, new_capacity);
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
465              
466             /* Copy live entries */
467             {
468             size_t i;
469 33186577 0         for (i = 0; i < old_capacity; i++) {
  0 0          
  0 0          
  0 0          
  0 100          
  2572329 100          
  2572329 100          
  2572329 100          
  2572329 100          
  2572329 100          
  10180983 100          
  4128839 100          
  2572329 100          
  1523751 100          
  1919030            
470 33185568 0         if (HM_SLOT_IS_LIVE(&old_nodes[i])) {
  0 0          
  0 0          
  0 0          
  0 0          
  2572256 0          
  2572256 0          
  2572256 0          
  2572256 100          
  2572256 100          
  10180688 100          
  4128736 100          
  2572256 100          
  1523680 100          
  1918928 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
471             #ifdef HM_KEY_IS_INT
472 1556740           size_t index = hm_hash_int64((int64_t)old_nodes[i].key) & new_mask;
  0            
  0            
  0            
  195917            
  195917            
  595452            
  290820            
  126765            
  151869            
473 1881426 0         while (new_nodes[index].key != HM_EMPTY_KEY)
  0 0          
  0 0          
  0 100          
  239063 100          
  239063 100          
  712792 100          
  352083 100          
  154339 100          
  184086            
474 324686           index = (index + 1) & new_mask;
  0            
  0            
  0            
  43146            
  43146            
  117340            
  61263            
  27574            
  32217            
475             #else
476 783668           size_t index = (size_t)old_nodes[i].key_hash & new_mask;
  0            
  195917            
  195917            
  195917            
  195917            
477 954580 0         while (new_nodes[index].key != NULL)
  0 100          
  238645 100          
  238645 100          
  238645 100          
  238645            
478 170912           index = (index + 1) & new_mask;
  0            
  42728            
  42728            
  42728            
  42728            
479             #endif
480 2340408           new_nodes[index] = old_nodes[i];
  0            
  0            
  0            
  0            
  195917            
  195917            
  195917            
  195917            
  195917            
  595452            
  290820            
  195917            
  126765            
  151869            
481 2340408 0         if (old_to_new) old_to_new[i] = (uint32_t)index;
  0 0          
  0 0          
  0 0          
  0 50          
  195917 50          
  195917 50          
  195917 50          
  195917 50          
  195917 100          
  595452 50          
  290820 50          
  195917 50          
  126765 50          
  151869            
482 2340408 0         if (new_expires_at)
  0 0          
  0 0          
  0 0          
  0 50          
  195917 50          
  195917 50          
  195917 50          
  195917 50          
  195917 50          
  595452 50          
  290820 50          
  195917 50          
  126765 50          
  151869            
483 0           new_expires_at[index] = map->expires_at[i];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
484             }
485             }
486             }
487              
488             /* Rebuild LRU linked list preserving order */
489 1009 0         if (map->lru_prev && map->lru_head != HM_LRU_NONE) {
  0 0          
  0 0          
  0 0          
  0 0          
  73 0          
  73 0          
  73 0          
  73 50          
  73 0          
  295 50          
  103 0          
  73 50          
  71 0          
  102 50          
    0          
    50          
    0          
    100          
    50          
    50          
    0          
    50          
    0          
    50          
    0          
    50          
    0          
490 150           uint32_t old_idx = map->lru_head;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
491 150           uint32_t prev_new = HM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
492 150           uint32_t new_head = HM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
493 150           uint32_t new_tail = HM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
494              
495 3166 0         while (old_idx != HM_LRU_NONE) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  3166 0          
  0 0          
  0 0          
  0 0          
  0            
496 3016           uint32_t next_old = map->lru_next[old_idx];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3016            
  0            
  0            
  0            
  0            
497 3016           uint32_t ni = old_to_new[old_idx];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3016            
  0            
  0            
  0            
  0            
498 3016 0         if (ni != HM_LRU_NONE) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3016 0          
  0 0          
  0 0          
  0 0          
  0            
499 3016           new_lru_prev[ni] = prev_new;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3016            
  0            
  0            
  0            
  0            
500 3016           new_lru_next[ni] = HM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3016            
  0            
  0            
  0            
  0            
501 3016 0         if (prev_new != HM_LRU_NONE) new_lru_next[prev_new] = ni;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  3016 0          
  0 0          
  0 0          
  0 0          
  0            
502 150           else new_head = ni;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
503 3016           new_tail = ni;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3016            
  0            
  0            
  0            
  0            
504 3016           prev_new = ni;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3016            
  0            
  0            
  0            
  0            
505             }
506 3016           old_idx = next_old;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3016            
  0            
  0            
  0            
  0            
507             }
508              
509 150           free(map->lru_prev); free(map->lru_next);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
510 150           map->lru_prev = new_lru_prev;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
511 150           map->lru_next = new_lru_next;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
512 150           map->lru_head = new_head;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
513 150           map->lru_tail = new_tail;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  150            
  0            
  0            
  0            
  0            
514 859 0         } else if (map->lru_prev) {
  0 0          
  0 0          
  0 0          
  0 50          
  73 50          
  73 50          
  73 50          
  73 50          
  73 50          
  145 50          
  103 50          
  73 50          
  71 50          
  102            
515 0           free(map->lru_prev); free(map->lru_next);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
516 0           map->lru_prev = new_lru_prev;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
517 0           map->lru_next = new_lru_next;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
518             }
519 1009           free(old_to_new);
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
520              
521 1009 0         if (map->expires_at) {
  0 0          
  0 0          
  0 0          
  0 50          
  73 50          
  73 50          
  73 50          
  73 50          
  73 50          
  295 50          
  103 50          
  73 50          
  71 50          
  102            
522 0           free(map->expires_at);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
523 0           map->expires_at = new_expires_at;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
524             }
525              
526 1009           free(old_nodes);
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
527 1009           map->nodes = new_nodes;
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
528 1009           map->capacity = new_capacity;
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
529 1009           map->mask = new_mask;
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
530 1009           map->tombstones = 0;
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
531 1009           map->iter_pos = 0;
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
532 1009           return true;
  0            
  0            
  0            
  0            
  73            
  73            
  73            
  73            
  73            
  295            
  103            
  73            
  71            
  102            
533             }
534              
535 255           static bool HM_FN(resize)(HM_MAP_TYPE* map) {
  0            
  0            
  0            
  0            
  23            
  23            
  23            
  23            
  23            
  42            
  24            
  23            
  22            
  29            
536 255           return HM_FN(rehash)(map, true);
  0            
  0            
  0            
  0            
  23            
  23            
  23            
  23            
  23            
  42            
  24            
  23            
  22            
  29            
537             }
538              
539 754           static bool HM_FN(compact)(HM_MAP_TYPE* map) {
  0            
  0            
  0            
  0            
  50            
  50            
  50            
  50            
  50            
  253            
  79            
  50            
  49            
  73            
540 754           return HM_FN(rehash)(map, false);
  0            
  0            
  0            
  0            
  50            
  50            
  50            
  50            
  50            
  253            
  79            
  50            
  49            
  73            
541             }
542              
543             /* ---- find_node: find existing key or return empty/capacity (not found) ---- */
544              
545             #ifdef HM_KEY_IS_INT
546              
547 1060381           static inline size_t HM_FN(find_node)(const HM_MAP_TYPE* map, HM_INT_TYPE key) {
  24            
  18            
  18            
  130011            
  130014            
  400212            
  200032            
  90012            
  110040            
548 1060381           size_t index = hm_hash_int64((int64_t)key) & map->mask;
  24            
  18            
  18            
  130011            
  130014            
  400212            
  200032            
  90012            
  110040            
549 1060381           const size_t original_index = index;
  24            
  18            
  18            
  130011            
  130014            
  400212            
  200032            
  90012            
  110040            
550 1060381           const HM_NODE_TYPE* nodes = map->nodes;
  24            
  18            
  18            
  130011            
  130014            
  400212            
  200032            
  90012            
  110040            
551              
552             do {
553 1229531           HM_INT_TYPE k = nodes[index].key;
  30            
  24            
  24            
  152547            
  152550            
  453305            
  230882            
  108956            
  131213            
554 1229531 100         if (k == key) return index;
  30 100          
  24 100          
  24 100          
  152547 100          
  152550 100          
  453305 100          
  230882 100          
  108956 100          
  131213            
555 169229 100         if (k == HM_EMPTY_KEY) return index;
  11 100          
  10 100          
  10 100          
  22540 100          
  22541 100          
  53121 100          
  30861 100          
  18949 100          
  21186            
556 169150           index = (index + 1) & map->mask;
  6            
  6            
  6            
  22536            
  22536            
  53093            
  30850            
  18944            
  21173            
557 169150 50         } while (index != original_index);
  6 50          
  6 50          
  6 50          
  22536 50          
  22536 50          
  53093 50          
  30850 50          
  18944 50          
  21173            
558              
559 0           return map->capacity;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
560             }
561              
562             #else /* string keys */
563              
564 540161           static inline size_t HM_FN(find_node)(const HM_MAP_TYPE* map,
  28            
  130033            
  130023            
  150043            
  130034            
565             const char* key, uint32_t key_len, uint32_t key_hash) {
566 540161           size_t index = (size_t)key_hash & map->mask;
  28            
  130033            
  130023            
  150043            
  130034            
567 540161           const size_t original_index = index;
  28            
  130033            
  130023            
  150043            
  130034            
568 540161           const HM_NODE_TYPE* nodes = map->nodes;
  28            
  130033            
  130023            
  150043            
  130034            
569              
570             do {
571 628750 100         if (nodes[index].key == NULL) return index; /* empty */
  32 100          
  152179 100          
  152167 100          
  172191 100          
  152181            
572 628704 100         if (nodes[index].key != HM_STR_TOMBSTONE &&
  27 100          
  152169 100          
  152161 100          
  172176 100          
  152171            
573 601717 100         nodes[index].key_hash == key_hash &&
  24 100          
  145424 100          
  145416 100          
  165429 100          
  145424            
574 540115 50         HM_UNPACK_LEN(nodes[index].key_len) == key_len &&
  23 50          
  130023 50          
  130017 50          
  150028 50          
  130024            
575 540115 50         memcmp(nodes[index].key, key, key_len) == 0) {
  23 50          
  130023 50          
  130017 50          
  150028 50          
  130024            
576 540115           return index; /* found */
  23            
  130023            
  130017            
  150028            
  130024            
577             }
578 88589           index = (index + 1) & map->mask;
  4            
  22146            
  22144            
  22148            
  22147            
579 88589 50         } while (index != original_index);
  4 50          
  22146 50          
  22144 50          
  22148 50          
  22147            
580              
581 0           return map->capacity;
  0            
  0            
  0            
  0            
  0            
582             }
583              
584             #endif
585              
586             /* ---- find_slot_for_insert ---- */
587              
588             #ifdef HM_KEY_IS_INT
589              
590 836395           static inline size_t HM_FN(find_slot_for_insert)(HM_MAP_TYPE* map, HM_INT_TYPE key, bool* found) {
  27            
  22            
  22            
  80014            
  80015            
  385232            
  150024            
  60014            
  81025            
591 836395           size_t index = hm_hash_int64((int64_t)key) & map->mask;
  27            
  22            
  22            
  80014            
  80015            
  385232            
  150024            
  60014            
  81025            
592 836395           const size_t original_index = index;
  27            
  22            
  22            
  80014            
  80015            
  385232            
  150024            
  60014            
  81025            
593 836395           HM_NODE_TYPE* nodes = map->nodes;
  27            
  22            
  22            
  80014            
  80015            
  385232            
  150024            
  60014            
  81025            
594 836395           size_t first_tombstone = map->capacity;
  27            
  22            
  22            
  80014            
  80015            
  385232            
  150024            
  60014            
  81025            
595              
596             do {
597 2164128           HM_INT_TYPE k = nodes[index].key;
  27            
  22            
  22            
  250793            
  250794            
  893022            
  390102            
  169907            
  209439            
598 2164128 100         if (k == key) {
  27 100          
  22 100          
  22 100          
  250793 100          
  250794 100          
  893022 100          
  390102 100          
  169907 100          
  209439            
599 17           *found = true;
  2            
  2            
  2            
  1            
  1            
  6            
  1            
  1            
  1            
600 17           return index;
  2            
  2            
  2            
  1            
  1            
  6            
  1            
  1            
  1            
601             }
602 2164111 50         if (k == HM_EMPTY_KEY) {
  25 50          
  20 50          
  20 100          
  250792 100          
  250793 100          
  893016 100          
  390101 100          
  169906 100          
  209438            
603 836378           *found = false;
  25            
  20            
  20            
  80013            
  80014            
  385226            
  150023            
  60013            
  81024            
604 836378 50         return (first_tombstone < map->capacity) ? first_tombstone : index;
  25 50          
  20 50          
  20 100          
  80013 100          
  80014 100          
  385226 100          
  150023 100          
  60013 100          
  81024            
605             }
606 1327733 0         if (k == HM_TOMBSTONE_KEY && first_tombstone >= map->capacity) {
  0 0          
  0 0          
  0 0          
  170779 0          
  170779 0          
  507790 100          
  240078 50          
  109893 100          
  128414 50          
    100          
    100          
    100          
    50          
    100          
    50          
    100          
    50          
607 1412           first_tombstone = index;
  0            
  0            
  0            
  2            
  2            
  1398            
  4            
  2            
  4            
608             }
609 1327733           index = (index + 1) & map->mask;
  0            
  0            
  0            
  170779            
  170779            
  507790            
  240078            
  109893            
  128414            
610 1327733 0         } while (index != original_index);
  0 0          
  0 0          
  0 50          
  170779 50          
  170779 50          
  507790 50          
  240078 50          
  109893 50          
  128414            
611              
612 0           *found = false;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
613 0           return first_tombstone;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
614             }
615              
616             #else /* string keys */
617              
618 320123           static inline size_t HM_FN(find_slot_for_insert)(HM_MAP_TYPE* map,
  30            
  80021            
  80028            
  80024            
  80020            
619             const char* key, uint32_t key_len,
620             uint32_t key_hash, bool* found) {
621 320123           size_t index = (size_t)key_hash & map->mask;
  30            
  80021            
  80028            
  80024            
  80020            
622 320123           const size_t original_index = index;
  30            
  80021            
  80028            
  80024            
  80020            
623 320123           HM_NODE_TYPE* nodes = map->nodes;
  30            
  80021            
  80028            
  80024            
  80020            
624 320123           size_t first_tombstone = map->capacity;
  30            
  80021            
  80028            
  80024            
  80020            
625              
626             do {
627 995784 100         if (nodes[index].key == NULL) {
  31 100          
  248936 100          
  248944 100          
  248939 100          
  248934            
628 320116           *found = false;
  28            
  80020            
  80026            
  80023            
  80019            
629 320116 100         return (first_tombstone < map->capacity) ? first_tombstone : index;
  28 100          
  80020 100          
  80026 100          
  80023 100          
  80019            
630             }
631 675668 100         if (nodes[index].key == HM_STR_TOMBSTONE) {
  3 100          
  168916 100          
  168918 100          
  168916 100          
  168915            
632 12 50         if (first_tombstone >= map->capacity) first_tombstone = index;
  1 50          
  3 50          
  3 50          
  3 50          
  2            
633 675656 50         } else if (nodes[index].key_hash == key_hash &&
  2 100          
  168913 100          
  168915 100          
  168913 100          
  168913            
634 7 50         HM_UNPACK_LEN(nodes[index].key_len) == key_len &&
  2 50          
  1 50          
  2 50          
  1 50          
  1            
635 7 50         memcmp(nodes[index].key, key, key_len) == 0) {
  2 50          
  1 50          
  2 50          
  1 50          
  1            
636 7           *found = true;
  2            
  1            
  2            
  1            
  1            
637 7           return index;
  2            
  1            
  2            
  1            
  1            
638             }
639 675661           index = (index + 1) & map->mask;
  1            
  168915            
  168916            
  168915            
  168914            
640 675661 50         } while (index != original_index);
  1 50          
  168915 50          
  168916 50          
  168915 50          
  168914            
641              
642 0           *found = false;
  0            
  0            
  0            
  0            
  0            
643 0           return first_tombstone;
  0            
  0            
  0            
  0            
  0            
644             }
645              
646             #endif
647              
648             /* ---- put ---- */
649              
650             #ifdef HM_KEY_IS_INT
651              
652 833914           static bool HM_FN(put)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  28            
  23            
  23            
  80015            
  80016            
  382741            
  150026            
  60015            
  81027            
653             #ifdef HM_VALUE_IS_STR
654             const char* value, uint32_t val_len, bool val_utf8,
655             #elif defined(HM_VALUE_IS_SV)
656             void* value,
657             #else
658             HM_INT_TYPE value,
659             #endif
660             uint32_t entry_ttl) {
661 833914 50         if (!map || HM_IS_RESERVED_KEY(key)) return false;
  28 100          
  23 100          
  23 50          
  80015 100          
  80016 100          
  382741 50          
  150026 100          
  60015 100          
  81027 50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
662              
663 833894 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  26 50          
  21 50          
  21 100          
  80013 100          
  80014 100          
  382739 100          
  150023 100          
  60013 100          
  81024            
664 308 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
  23 0          
  23 0          
  187 50          
  24 0          
  22 50          
  29 0          
    100          
    100          
    50          
    0          
    50          
    0          
    50          
    0          
665 145 0         if (!HM_FN(compact)(map)) return false;
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  145 0          
  0 0          
  0 0          
  0            
666             } else {
667 163 0         if (!HM_FN(resize)(map)) return false;
  0 0          
  0 0          
  0 50          
  23 50          
  23 50          
  42 50          
  24 50          
  22 50          
  29            
668             }
669             }
670              
671             bool found;
672 833894           size_t index = HM_FN(find_slot_for_insert)(map, key, &found);
  26            
  21            
  21            
  80013            
  80014            
  382739            
  150023            
  60013            
  81024            
673 833894 50         if (index >= map->capacity) return false;
  26 50          
  21 50          
  21 50          
  80013 50          
  80014 50          
  382739 50          
  150023 50          
  60013 50          
  81024            
674              
675             /* LRU eviction: only on new insert at capacity */
676 833894 100         if (!found && map->max_size > 0 && map->size >= map->max_size) {
  26 100          
  21 100          
  21 100          
  80013 100          
  80014 100          
  382739 100          
  150023 100          
  60013 100          
  81024 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
677 2501           HM_FN(lru_evict_one)(map);
  1            
  1            
  1            
  1            
  1            
  2493            
  1            
  1            
  1            
678             /* Re-probe after eviction to find optimal insertion slot */
679 2501           index = HM_FN(find_slot_for_insert)(map, key, &found);
  1            
  1            
  1            
  1            
  1            
  2493            
  1            
  1            
  1            
680 2501 50         if (index >= map->capacity) return false;
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  2493 50          
  1 50          
  1 50          
  1            
681             }
682              
683             #ifdef HM_VALUE_IS_STR
684             /* Pre-allocate value before modifying map state */
685 220040           char* new_val = NULL;
  80013            
  80014            
  60013            
686 220040           uint32_t new_val_len = 0;
  80013            
  80014            
  60013            
687 220040 50         if (value && val_len > 0) {
  80013 100          
  80014 50          
  60013 100          
    50          
    50          
688 220038           new_val = (char*)malloc(val_len);
  80012            
  80013            
  60013            
689 220038 50         if (!new_val) return false;
  80012 50          
  80013 50          
  60013            
690 220038           memcpy(new_val, value, val_len);
  80012            
  80013            
  60013            
691 220038 100         new_val_len = HM_PACK_LEN(val_len, val_utf8);
  80012 100          
  80013 100          
  60013            
692 2 50         } else if (value) {
  1 50          
  1 0          
  0            
693 2           new_val = (char*)malloc(1);
  1            
  1            
  0            
694 2 50         if (!new_val) return false;
  1 50          
  1 0          
  0            
695 2           new_val[0] = '\0';
  1            
  1            
  0            
696 2 50         new_val_len = HM_PACK_LEN(0, val_utf8);
  1 50          
  1 0          
  0            
697             }
698             #endif
699              
700 833894 100         if (found) {
  26 100          
  21 100          
  21 100          
  80013 100          
  80014 100          
  382739 100          
  150023 100          
  60013 100          
  81024            
701             #ifdef HM_VALUE_IS_STR
702 3 50         if (map->nodes[index].value) free(map->nodes[index].value);
  1 50          
  1 50          
  1            
703             #elif defined(HM_VALUE_IS_SV)
704 6 50         if (map->nodes[index].value && map->free_value_fn)
  2 50          
  2 50          
  2 50          
    50          
    50          
705 6           map->free_value_fn(map->nodes[index].value);
  2            
  2            
  2            
706             #endif
707             } else {
708 833877 50         if (map->nodes[index].key == HM_TOMBSTONE_KEY) {
  24 50          
  19 50          
  19 100          
  80012 100          
  80013 100          
  382733 100          
  150022 100          
  60012 100          
  81023            
709 753           map->tombstones--;
  0            
  0            
  0            
  2            
  2            
  739            
  4            
  2            
  4            
710             }
711 833877           map->size++;
  24            
  19            
  19            
  80012            
  80013            
  382733            
  150022            
  60012            
  81023            
712 833877           map->nodes[index].key = key;
  24            
  19            
  19            
  80012            
  80013            
  382733            
  150022            
  60012            
  81023            
713             }
714              
715             #ifdef HM_VALUE_IS_STR
716 220040           map->nodes[index].value = new_val;
  80013            
  80014            
  60013            
717 220040           map->nodes[index].val_len = new_val_len;
  80013            
  80014            
  60013            
718             #else
719 613854           map->nodes[index].value = value;
  26            
  21            
  21            
  382739            
  150023            
  81024            
720             #endif
721              
722             /* LRU maintenance */
723 833894 100         if (map->lru_prev) {
  26 100          
  21 100          
  21 100          
  80013 100          
  80014 100          
  382739 100          
  150023 100          
  60013 100          
  81024            
724 2705 50         if (found) HM_FN(lru_promote)(map, (uint32_t)index);
  4 50          
  4 50          
  4 50          
  3 50          
  3 100          
  2676 50          
  4 50          
  3 50          
  4            
725 2702           else HM_FN(lru_push_front)(map, (uint32_t)index);
  4            
  4            
  4            
  3            
  3            
  2673            
  4            
  3            
  4            
726             }
727             /* TTL maintenance — lazy allocation on first per-key TTL */
728 833894 100         if (entry_ttl > 0 && !map->expires_at)
  26 50          
  21 50          
  21 0          
  80013 50          
  80014 0          
  382739 50          
  150023 0          
  60013 50          
  81024 0          
    100          
    100          
    100          
    50          
    50          
    0          
    50          
    0          
729 8           map->expires_at = (uint32_t*)calloc(map->capacity, sizeof(uint32_t));
  1            
  0            
  0            
  0            
  0            
  6            
  1            
  0            
  0            
730 833894 100         if (map->expires_at) {
  26 100          
  21 100          
  21 50          
  80013 50          
  80014 100          
  382739 100          
  150023 50          
  60013 100          
  81024            
731 42 100         uint32_t ttl = entry_ttl > 0 ? entry_ttl : map->default_ttl;
  3 50          
  1 50          
  1 0          
  0 0          
  0 100          
  35 50          
  1 0          
  0 50          
  1            
732 42 50         if (ttl > 0)
  3 50          
  1 50          
  1 0          
  0 0          
  0 100          
  35 50          
  1 0          
  0 50          
  1            
733 38           map->expires_at[index] = (uint32_t)time(NULL) + ttl;
  3            
  1            
  1            
  0            
  0            
  31            
  1            
  0            
  1            
734             else
735 4           map->expires_at[index] = 0;
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
  0            
736             }
737              
738 833894           return true;
  26            
  21            
  21            
  80013            
  80014            
  382739            
  150023            
  60013            
  81024            
739             }
740              
741             #else /* string keys */
742              
743 320118           static bool HM_FN(put)(HM_MAP_TYPE* map,
  29            
  80020            
  80027            
  80023            
  80019            
744             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
745             #ifdef HM_VALUE_IS_STR
746             const char* value, uint32_t val_len, bool val_utf8,
747             #elif defined(HM_VALUE_IS_SV)
748             void* value,
749             #else
750             HM_INT_TYPE value,
751             #endif
752             uint32_t entry_ttl) {
753 320118 50         if (!map || !key) return false;
  29 50          
  80020 50          
  80027 50          
  80023 50          
  80019 50          
    50          
    50          
    50          
    50          
754              
755 320118 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  29 100          
  80020 100          
  80027 100          
  80023 100          
  80019            
756 92 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  23 50          
  23 0          
  23 50          
  23 0          
    50          
    0          
    50          
    0          
757 0 0         if (!HM_FN(compact)(map)) return false;
  0 0          
  0 0          
  0 0          
  0 0          
  0            
758             } else {
759 92 0         if (!HM_FN(resize)(map)) return false;
  0 50          
  23 50          
  23 50          
  23 50          
  23            
760             }
761             }
762              
763             bool found;
764 320118           size_t index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, &found);
  29            
  80020            
  80027            
  80023            
  80019            
765 320118 50         if (index >= map->capacity) return false;
  29 50          
  80020 50          
  80027 50          
  80023 50          
  80019            
766              
767             /* LRU eviction: only on new insert at capacity */
768 320118 100         if (!found && map->max_size > 0 && map->size >= map->max_size) {
  29 100          
  80020 100          
  80027 100          
  80023 100          
  80019 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
769 5           HM_FN(lru_evict_one)(map);
  1            
  1            
  1            
  1            
  1            
770             /* Re-probe after eviction to find optimal insertion slot */
771 5           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, &found);
  1            
  1            
  1            
  1            
  1            
772 5 50         if (index >= map->capacity) return false;
  1 50          
  1 50          
  1 50          
  1 50          
  1            
773             }
774              
775             #ifdef HM_VALUE_IS_STR
776             /* Pre-allocate value before modifying map state */
777 80027           char* new_val = NULL;
  80027            
778 80027           uint32_t new_val_len = 0;
  80027            
779 80027 50         if (value && val_len > 0) {
  80027 100          
780 80026           new_val = (char*)malloc(val_len);
  80026            
781 80026 50         if (!new_val) return false;
  80026            
782 80026           memcpy(new_val, value, val_len);
  80026            
783 80026 100         new_val_len = HM_PACK_LEN(val_len, val_utf8);
  80026            
784 1 50         } else if (value) {
  1            
785 1           new_val = (char*)malloc(1);
  1            
786 1 50         if (!new_val) return false;
  1            
787 1           new_val[0] = '\0';
  1            
788 1 50         new_val_len = HM_PACK_LEN(0, val_utf8);
  1            
789             }
790             #endif
791              
792 320118 100         if (found) {
  29 100          
  80020 100          
  80027 100          
  80023 100          
  80019            
793             #ifdef HM_VALUE_IS_STR
794 2 50         if (map->nodes[index].value) free(map->nodes[index].value);
  2            
795             #elif defined(HM_VALUE_IS_SV)
796 2 50         if (map->nodes[index].value && map->free_value_fn)
  2 50          
797 2           map->free_value_fn(map->nodes[index].value);
  2            
798             #endif
799 7 50         map->nodes[index].key_len = HM_PACK_LEN(key_len, key_utf8);
  2 50          
  1 50          
  2 50          
  1 50          
  1            
800             } else {
801 320111           char* new_key = (char*)malloc(key_len + 1);
  27            
  80019            
  80025            
  80022            
  80018            
802 320111 50         if (!new_key) {
  27 50          
  80019 50          
  80025 50          
  80022 50          
  80018            
803             #ifdef HM_VALUE_IS_STR
804 0           free(new_val);
  0            
805             #endif
806 0           return false;
  0            
  0            
  0            
  0            
  0            
807             }
808 320111           memcpy(new_key, key, key_len);
  27            
  80019            
  80025            
  80022            
  80018            
809 320111           new_key[key_len] = '\0';
  27            
  80019            
  80025            
  80022            
  80018            
810 320111 100         if (HM_SLOT_IS_TOMBSTONE(&map->nodes[index])) {
  27 100          
  80019 100          
  80025 100          
  80022 100          
  80018            
811 12           map->tombstones--;
  1            
  3            
  3            
  3            
  2            
812             }
813 320111           map->size++;
  27            
  80019            
  80025            
  80022            
  80018            
814 320111           map->nodes[index].key = new_key;
  27            
  80019            
  80025            
  80022            
  80018            
815 320111 100         map->nodes[index].key_len = HM_PACK_LEN(key_len, key_utf8);
  27 100          
  80019 100          
  80025 100          
  80022 100          
  80018            
816 320111           map->nodes[index].key_hash = key_hash;
  27            
  80019            
  80025            
  80022            
  80018            
817             }
818              
819             #ifdef HM_VALUE_IS_STR
820 80027           map->nodes[index].value = new_val;
  80027            
821 80027           map->nodes[index].val_len = new_val_len;
  80027            
822             #else
823 240091           map->nodes[index].value = value;
  29            
  80020            
  80023            
  80019            
824             #endif
825              
826             /* LRU maintenance */
827 320118 100         if (map->lru_prev) {
  29 100          
  80020 100          
  80027 100          
  80023 100          
  80019            
828 19 50         if (found) HM_FN(lru_promote)(map, (uint32_t)index);
  4 50          
  3 50          
  4 50          
  4 50          
  4            
829 19           else HM_FN(lru_push_front)(map, (uint32_t)index);
  4            
  3            
  4            
  4            
  4            
830             }
831             /* TTL maintenance — lazy allocation on first per-key TTL */
832 320118 100         if (entry_ttl > 0 && !map->expires_at)
  29 50          
  80020 50          
  80027 0          
  80023 100          
  80019 50          
    50          
    0          
    50          
    0          
833 3           map->expires_at = (uint32_t*)calloc(map->capacity, sizeof(uint32_t));
  1            
  0            
  2            
  0            
  0            
834 320118 100         if (map->expires_at) {
  29 50          
  80020 100          
  80027 100          
  80023 100          
  80019            
835 10 100         uint32_t ttl = entry_ttl > 0 ? entry_ttl : map->default_ttl;
  3 0          
  0 100          
  5 50          
  1 50          
  1            
836 10 50         if (ttl > 0)
  3 0          
  0 100          
  5 50          
  1 50          
  1            
837 8           map->expires_at[index] = (uint32_t)time(NULL) + ttl;
  3            
  0            
  3            
  1            
  1            
838             else
839 2           map->expires_at[index] = 0;
  0            
  0            
  2            
  0            
  0            
840             }
841              
842 320118           return true;
  29            
  80020            
  80027            
  80023            
  80019            
843             }
844              
845             #endif /* HM_KEY_IS_INT */
846              
847             /* ---- get ---- */
848              
849             #ifdef HM_KEY_IS_INT
850              
851             #ifdef HM_VALUE_IS_STR
852 130027           static bool HM_FN(get)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  50008            
  50011            
  30008            
853             const char** out_value, uint32_t* out_len, bool* out_utf8) {
854 130027 50         if (!map || HM_IS_RESERVED_KEY(key)) return false;
  50008 100          
  50011 50          
  30008 50          
    100          
    50          
    50          
    100          
    50          
855              
856 130024           size_t index = HM_FN(find_node)(map, key);
  50007            
  50010            
  30007            
857 130024 50         if (index >= map->capacity || map->nodes[index].key == HM_EMPTY_KEY) return false;
  50007 100          
  50010 50          
  30007 100          
    50          
    100          
858              
859             /* TTL check */
860 130016 50         if (map->expires_at && map->expires_at[index] &&
  50005 0          
  50007 50          
  30004 0          
    50          
    0          
861 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0            
862 0           HM_FN(expire_at)(map, index);
  0            
  0            
  0            
863 0           return false;
  0            
  0            
  0            
864             }
865              
866 130016 50         if (map->lru_prev) HM_FN(lru_promote)(map, (uint32_t)index);
  50005 100          
  50007 50          
  30004            
867              
868 130016           *out_value = map->nodes[index].value;
  50005            
  50007            
  30004            
869 130016           *out_len = HM_UNPACK_LEN(map->nodes[index].val_len);
  50005            
  50007            
  30004            
870 130016           *out_utf8 = HM_UNPACK_UTF8(map->nodes[index].val_len);
  50005            
  50007            
  30004            
871 130016           return true;
  50005            
  50007            
  30004            
872             }
873             #elif defined(HM_VALUE_IS_SV)
874 42           static bool HM_FN(get)(HM_MAP_TYPE* map, HM_INT_TYPE key, void** out_value) {
  18            
  12            
  12            
875 42 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  18 50          
  12 100          
  12 50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
876              
877 41           size_t index = HM_FN(find_node)(map, key);
  17            
  12            
  12            
878 41 50         if (index >= map->capacity || map->nodes[index].key == HM_EMPTY_KEY) return false;
  17 100          
  12 50          
  12 100          
    50          
    100          
879              
880             /* TTL check */
881 34 100         if (map->expires_at && map->expires_at[index] &&
  14 50          
  10 100          
  10 50          
    100          
    50          
882 4 50         (uint32_t)time(NULL) >= map->expires_at[index]) {
  2 50          
  1 50          
  1            
883 4           HM_FN(expire_at)(map, index);
  2            
  1            
  1            
884 4           return false;
  2            
  1            
  1            
885             }
886              
887 30 100         if (map->lru_prev) HM_FN(lru_promote)(map, (uint32_t)index);
  12 100          
  9 100          
  9            
888              
889 30           *out_value = map->nodes[index].value;
  12            
  9            
  9            
890 30           return true;
  12            
  9            
  9            
891             }
892             #else
893 180189           static bool HM_FN(get)(HM_MAP_TYPE* map, HM_INT_TYPE key, HM_INT_TYPE* out_value) {
  100162            
  50012            
  30015            
894 180189 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  100162 50          
  50012 100          
  30015 50          
    50          
    50          
    100          
    50          
    50          
    50          
    100          
    50          
895              
896 180184           size_t index = HM_FN(find_node)(map, key);
  100161            
  50010            
  30013            
897 180184 50         if (index >= map->capacity || map->nodes[index].key == HM_EMPTY_KEY) return false;
  100161 100          
  50010 50          
  30013 100          
    50          
    100          
898              
899             /* TTL check */
900 180160 100         if (map->expires_at && map->expires_at[index] &&
  100146 100          
  50006 100          
  30008 50          
    100          
    50          
901 23 100         (uint32_t)time(NULL) >= map->expires_at[index]) {
  19 100          
  2 100          
  2            
902 12           HM_FN(expire_at)(map, index);
  10            
  1            
  1            
903 12           return false;
  10            
  1            
  1            
904             }
905              
906 180148 100         if (map->lru_prev) HM_FN(lru_promote)(map, (uint32_t)index);
  100136 50          
  50005 50          
  30007            
907              
908 180148           *out_value = map->nodes[index].value;
  100136            
  50005            
  30007            
909 180148           return true;
  100136            
  50005            
  30007            
910             }
911             #endif
912              
913             #else /* string keys */
914              
915             #ifdef HM_VALUE_IS_STR
916 50018           static bool HM_FN(get)(HM_MAP_TYPE* map,
917             const char* key, uint32_t key_len, uint32_t key_hash,
918             const char** out_value, uint32_t* out_len, bool* out_utf8) {
919 50018 50         if (!map || !key) return false;
    50          
920              
921 50018           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
922 50018 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
    100          
923              
924             /* TTL check */
925 50014 100         if (map->expires_at && map->expires_at[index] &&
    100          
926 3 100         (uint32_t)time(NULL) >= map->expires_at[index]) {
927 2           HM_FN(expire_at)(map, index);
928 2           return false;
929             }
930              
931 50012 100         if (map->lru_prev) HM_FN(lru_promote)(map, (uint32_t)index);
932              
933 50012           *out_value = map->nodes[index].value;
934 50012           *out_len = HM_UNPACK_LEN(map->nodes[index].val_len);
935 50012           *out_utf8 = HM_UNPACK_UTF8(map->nodes[index].val_len);
936 50012           return true;
937             }
938             #elif defined(HM_VALUE_IS_SV)
939 20           static bool HM_FN(get)(HM_MAP_TYPE* map,
940             const char* key, uint32_t key_len, uint32_t key_hash,
941             void** out_value) {
942 20 50         if (!map || !key || !out_value) return false;
    50          
    50          
943              
944 20           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
945 20 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
    100          
946              
947             /* TTL check */
948 17 100         if (map->expires_at && map->expires_at[index] &&
    50          
949 2 50         (uint32_t)time(NULL) >= map->expires_at[index]) {
950 2           HM_FN(expire_at)(map, index);
951 2           return false;
952             }
953              
954 15 100         if (map->lru_prev) HM_FN(lru_promote)(map, (uint32_t)index);
955              
956 15           *out_value = map->nodes[index].value;
957 15           return true;
958             }
959             #else
960 150031           static bool HM_FN(get)(HM_MAP_TYPE* map,
  50009            
  50014            
  50008            
961             const char* key, uint32_t key_len, uint32_t key_hash,
962             HM_INT_TYPE* out_value) {
963 150031 50         if (!map || !key || !out_value) return false;
  50009 50          
  50014 50          
  50008 50          
    50          
    50          
    50          
    50          
    50          
964              
965 150031           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  50009            
  50014            
  50008            
966 150031 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
  50009 100          
  50014 50          
  50008 100          
    50          
    100          
967              
968             /* TTL check */
969 150023 50         if (map->expires_at && map->expires_at[index] &&
  50006 0          
  50011 100          
  50006 50          
    100          
    50          
970 2 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 50          
  1 50          
  1            
971 2           HM_FN(expire_at)(map, index);
  0            
  1            
  1            
972 2           return false;
  0            
  1            
  1            
973             }
974              
975 150021 50         if (map->lru_prev) HM_FN(lru_promote)(map, (uint32_t)index);
  50006 100          
  50010 100          
  50005            
976              
977 150021           *out_value = map->nodes[index].value;
  50006            
  50010            
  50005            
978 150021           return true;
  50006            
  50010            
  50005            
979             }
980             #endif
981              
982             #endif /* HM_KEY_IS_INT */
983              
984             /* ---- exists ---- */
985              
986             #ifdef HM_KEY_IS_INT
987              
988 37           static bool HM_FN(exists)(HM_MAP_TYPE* map, HM_INT_TYPE key) {
  4            
  3            
  3            
  3            
  3            
  10            
  3            
  4            
  4            
989 37 50         if (!map || HM_IS_RESERVED_KEY(key)) return false;
  4 50          
  3 50          
  3 50          
  3 50          
  3 50          
  10 50          
  3 50          
  4 50          
  4 50          
    100          
    50          
    50          
    100          
    50          
    50          
    100          
    50          
    50          
    100          
    50          
    50          
    100          
    50          
    50          
    100          
    50          
990 31           size_t index = HM_FN(find_node)(map, key);
  4            
  3            
  3            
  2            
  2            
  9            
  2            
  3            
  3            
991 31 50         if (index >= map->capacity || map->nodes[index].key == HM_EMPTY_KEY) return false;
  4 100          
  3 50          
  3 100          
  2 50          
  2 100          
  9 50          
  2 100          
  3 50          
  3 100          
    50          
    100          
    50          
    100          
    50          
    100          
    50          
    100          
992              
993             /* TTL check */
994 20 50         if (map->expires_at && map->expires_at[index] &&
  3 0          
  2 50          
  2 0          
  1 50          
  1 0          
  6 50          
  1 0          
  2 50          
  2 0          
    100          
    50          
    50          
    0          
    50          
    0          
    50          
    0          
995 1 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0            
996 0           HM_FN(expire_at)(map, index);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
997 0           return false;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
998             }
999 20           return true;
  3            
  2            
  2            
  1            
  1            
  6            
  1            
  2            
  2            
1000             }
1001              
1002             #else
1003              
1004 15           static bool HM_FN(exists)(HM_MAP_TYPE* map,
  5            
  2            
  2            
  2            
  4            
1005             const char* key, uint32_t key_len, uint32_t key_hash) {
1006 15 50         if (!map || !key) return false;
  5 50          
  2 50          
  2 50          
  2 50          
  4 50          
    50          
    50          
    50          
    50          
1007 15           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  5            
  2            
  2            
  2            
  4            
1008 15 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
  5 100          
  2 50          
  2 100          
  2 50          
  4 100          
    50          
    100          
    50          
    100          
1009              
1010             /* TTL check */
1011 10 50         if (map->expires_at && map->expires_at[index] &&
  4 0          
  1 50          
  1 0          
  1 50          
  3 0          
    50          
    0          
    50          
    0          
1012 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0 0          
  0 0          
  0            
1013 0           HM_FN(expire_at)(map, index);
  0            
  0            
  0            
  0            
  0            
1014 0           return false;
  0            
  0            
  0            
  0            
  0            
1015             }
1016 10           return true;
  4            
  1            
  1            
  1            
  3            
1017             }
1018              
1019             #endif
1020              
1021             /* ---- remove ---- */
1022              
1023             #ifdef HM_KEY_IS_INT
1024              
1025 730050           static bool HM_FN(remove)(HM_MAP_TYPE* map, HM_INT_TYPE key) {
  3            
  3            
  3            
  80004            
  80004            
  280020            
  150004            
  60004            
  80005            
1026 730050 50         if (!map || HM_IS_RESERVED_KEY(key)) return false;
  3 50          
  3 50          
  3 50          
  80004 50          
  80004 50          
  280020 50          
  150004 50          
  60004 50          
  80005 50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    50          
    50          
    100          
    50          
    50          
    100          
    100          
    50          
    100          
    50          
1027              
1028 730041           size_t index = HM_FN(find_node)(map, key);
  3            
  3            
  3            
  80002            
  80002            
  280019            
  150003            
  60002            
  80004            
1029 730041 50         if (index >= map->capacity || map->nodes[index].key != key) return false;
  3 100          
  3 50          
  3 100          
  80002 50          
  80002 100          
  280019 50          
  150003 100          
  60002 50          
  80004 100          
    50          
    100          
    50          
    100          
    50          
    100          
    50          
    100          
1030              
1031             /* TTL check: treat expired entry as already gone */
1032 730032 50         if (map->expires_at && map->expires_at[index] &&
  2 0          
  2 50          
  2 0          
  80001 50          
  80001 0          
  280018 50          
  150002 0          
  60001 50          
  80003 0          
    100          
    50          
    50          
    0          
    50          
    0          
    50          
    0          
1033 2 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  2 0          
  0 0          
  0 0          
  0            
1034 1           HM_FN(expire_at)(map, index);
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
1035 1           return false;
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
1036             }
1037              
1038 730031 50         if (map->lru_prev) HM_FN(lru_unlink)(map, (uint32_t)index);
  2 50          
  2 50          
  2 50          
  80001 50          
  80001 100          
  280017 50          
  150002 50          
  60001 50          
  80003            
1039 730031           HM_FN(tombstone_at)(map, index);
  2            
  2            
  2            
  80001            
  80001            
  280017            
  150002            
  60001            
  80003            
1040 730031 50         if (map->tombstones > map->capacity / 4 ||
  2 50          
  2 50          
  2 100          
  80001 100          
  80001 100          
  280017 100          
  150002 100          
  60001 100          
  80003            
1041 730008 100         (map->size > 0 && map->tombstones > map->size)) {
  2 50          
  2 100          
  2 50          
  79998 100          
  79998 50          
  280013 100          
  149997 100          
  59998 100          
  79998 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
1042 409           HM_FN(compact)(map);
  0            
  0            
  0            
  50            
  50            
  108            
  79            
  49            
  73            
1043             }
1044 730031           return true;
  2            
  2            
  2            
  80001            
  80001            
  280017            
  150002            
  60001            
  80003            
1045             }
1046              
1047             #else /* string keys */
1048              
1049 320016           static bool HM_FN(remove)(HM_MAP_TYPE* map,
  3            
  80003            
  80003            
  80004            
  80003            
1050             const char* key, uint32_t key_len, uint32_t key_hash) {
1051 320016 50         if (!map || !key) return false;
  3 50          
  80003 50          
  80003 50          
  80004 50          
  80003 50          
    50          
    50          
    50          
    50          
1052              
1053 320016           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  3            
  80003            
  80003            
  80004            
  80003            
1054 320016 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
  3 100          
  80003 50          
  80003 100          
  80004 50          
  80003 100          
    50          
    100          
    50          
    100          
1055              
1056             /* TTL check: treat expired entry as already gone */
1057 320011 50         if (map->expires_at && map->expires_at[index] &&
  2 0          
  80002 50          
  80002 0          
  80003 50          
  80002 0          
    50          
    0          
    50          
    0          
1058 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0 0          
  0 0          
  0            
1059 0           HM_FN(expire_at)(map, index);
  0            
  0            
  0            
  0            
  0            
1060 0           return false;
  0            
  0            
  0            
  0            
  0            
1061             }
1062              
1063 320011 50         if (map->lru_prev) HM_FN(lru_unlink)(map, (uint32_t)index);
  2 50          
  80002 50          
  80002 50          
  80003 50          
  80002            
1064 320011           HM_FN(tombstone_at)(map, index);
  2            
  80002            
  80002            
  80003            
  80002            
1065 320011 50         if (map->tombstones > map->capacity / 4 ||
  2 100          
  80002 100          
  80002 100          
  80003 100          
  80002            
1066 319999 100         (map->size > 0 && map->tombstones > map->size)) {
  2 50          
  79999 100          
  79999 100          
  80000 100          
  79999 100          
    100          
    100          
    100          
    100          
1067 200           HM_FN(compact)(map);
  0            
  50            
  50            
  50            
  50            
1068             }
1069 320011           return true;
  2            
  80002            
  80002            
  80003            
  80002            
1070             }
1071              
1072             #endif
1073              
1074             /* ---- Counter operations (int values only) ---- */
1075              
1076             #ifdef HM_HAS_COUNTERS
1077              
1078             #ifdef HM_KEY_IS_INT
1079              
1080 21           static inline size_t HM_FN(find_or_allocate)(HM_MAP_TYPE* map, HM_INT_TYPE key) {
  10            
  5            
  6            
1081 21           size_t index = hm_hash_int64((int64_t)key) & map->mask;
  10            
  5            
  6            
1082 21           const size_t original_index = index;
  10            
  5            
  6            
1083 21           HM_NODE_TYPE* nodes = map->nodes;
  10            
  5            
  6            
1084 21           size_t first_tombstone = map->capacity;
  10            
  5            
  6            
1085              
1086             do {
1087 25           HM_INT_TYPE k = nodes[index].key;
  11            
  6            
  8            
1088 25 50         if (k == key) return index;
  11 50          
  6 50          
  8            
1089 25 100         if (k == HM_EMPTY_KEY) {
  11 100          
  6 100          
  8            
1090 21 100         size_t target = (first_tombstone < map->capacity) ? first_tombstone : index;
  10 100          
  5 100          
  6            
1091 21 100         if (nodes[target].key == HM_TOMBSTONE_KEY) map->tombstones--;
  10 100          
  5 100          
  6            
1092 21           nodes[target].key = key;
  10            
  5            
  6            
1093 21           nodes[target].value = 0;
  10            
  5            
  6            
1094 21           map->size++;
  10            
  5            
  6            
1095 21           return target;
  10            
  5            
  6            
1096             }
1097 4 50         if (k == HM_TOMBSTONE_KEY && first_tombstone >= map->capacity) {
  1 50          
  1 50          
  2 50          
    50          
    50          
1098 4           first_tombstone = index;
  1            
  1            
  2            
1099             }
1100 4           index = (index + 1) & map->mask;
  1            
  1            
  2            
1101 4 50         } while (index != original_index);
  1 50          
  1 50          
  2            
1102              
1103 0 0         if (first_tombstone < map->capacity) {
  0 0          
  0 0          
  0            
1104 0           map->tombstones--;
  0            
  0            
  0            
1105 0           nodes[first_tombstone].key = key;
  0            
  0            
  0            
1106 0           nodes[first_tombstone].value = 0;
  0            
  0            
  0            
1107 0           map->size++;
  0            
  0            
  0            
1108 0           return first_tombstone;
  0            
  0            
  0            
1109             }
1110 0           return map->capacity;
  0            
  0            
  0            
1111             }
1112              
1113 15026           static bool HM_FN(increment)(HM_MAP_TYPE* map, HM_INT_TYPE key, HM_INT_TYPE* out_value) {
  15011            
  7            
  8            
1114 15026 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  15011 50          
  7 100          
  8 50          
    50          
    50          
    100          
    50          
    50          
    50          
    100          
    50          
1115              
1116 15023           size_t index = HM_FN(find_node)(map, key);
  15010            
  6            
  7            
1117 15023 50         if (index < map->capacity && map->nodes[index].key == key) {
  15010 100          
  6 50          
  7 100          
    50          
    100          
1118             /* TTL check */
1119 15014 100         if (map->expires_at && map->expires_at[index] &&
  15006 50          
  4 50          
  4 0          
    50          
    0          
1120 2 100         (uint32_t)time(NULL) >= map->expires_at[index]) {
  2 0          
  0 0          
  0            
1121 1           HM_FN(expire_at)(map, index);
  1            
  0            
  0            
1122 1           goto new_key;
  1            
  0            
  0            
1123             }
1124 15013 100         if (map->nodes[index].value == HM_INT_MAX) return false;
  15005 100          
  4 100          
  4            
1125 15010           *out_value = ++map->nodes[index].value;
  15004            
  3            
  3            
1126 15010 100         if (map->lru_prev) HM_FN(lru_promote)(map, (uint32_t)index);
  15004 50          
  3 50          
  3            
1127 15010 100         if (map->expires_at && map->default_ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  15004 50          
  3 50          
  3 0          
    50          
    0          
1128 15010           return true;
  15004            
  3            
  3            
1129             }
1130              
1131 9           new_key:
  4            
  2            
  3            
1132 10 100         if (map->max_size > 0 && map->size >= map->max_size)
  5 50          
  2 50          
  3 0          
    50          
    0          
1133 1           HM_FN(lru_evict_one)(map);
  1            
  0            
  0            
1134              
1135 10 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  5 50          
  2 50          
  3            
1136 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1137 0 0         if (!HM_FN(compact)(map)) return false;
  0 0          
  0 0          
  0            
1138             } else {
1139 0 0         if (!HM_FN(resize)(map)) return false;
  0 0          
  0 0          
  0            
1140             }
1141             }
1142              
1143 10           index = HM_FN(find_or_allocate)(map, key);
  5            
  2            
  3            
1144 10 50         if (index >= map->capacity) return false;
  5 50          
  2 50          
  3            
1145 10           *out_value = ++map->nodes[index].value;
  5            
  2            
  3            
1146 10 100         if (map->lru_prev) HM_FN(lru_push_front)(map, (uint32_t)index);
  5 50          
  2 50          
  3            
1147 10 100         if (map->expires_at && map->default_ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  5 50          
  2 50          
  3 0          
    100          
    50          
1148 10           return true;
  5            
  2            
  3            
1149             }
1150              
1151 23           static bool HM_FN(increment_by)(HM_MAP_TYPE* map, HM_INT_TYPE key, HM_INT_TYPE delta, HM_INT_TYPE* out_value) {
  8            
  7            
  8            
1152 23 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  8 50          
  7 50          
  8 50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
1153              
1154 23           size_t index = HM_FN(find_node)(map, key);
  8            
  7            
  8            
1155 23 50         if (index < map->capacity && map->nodes[index].key == key) {
  8 100          
  7 50          
  8 100          
    50          
    100          
1156             /* TTL check */
1157 16 50         if (map->expires_at && map->expires_at[index] &&
  5 0          
  5 50          
  6 0          
    50          
    0          
1158 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0            
1159 0           HM_FN(expire_at)(map, index);
  0            
  0            
  0            
1160 0           goto new_key;
  0            
  0            
  0            
1161             }
1162 16           HM_INT_TYPE val = map->nodes[index].value;
  5            
  5            
  6            
1163 16 100         if (delta > 0 && val > HM_INT_MAX - delta) return false;
  5 100          
  5 100          
  6 100          
    100          
    100          
1164 13 100         if (delta < 0) {
  4 100          
  4 100          
  5            
1165 6 50         if (delta == HM_INT_MIN) { if (val < 0) return false; }
  2 0          
  2 50          
  2 0          
    50          
    0          
1166 6 100         else { if (val < HM_INT_MIN - delta) return false; }
  2 100          
  2 100          
  2            
1167             }
1168 10           map->nodes[index].value += delta;
  3            
  3            
  4            
1169 10           *out_value = map->nodes[index].value;
  3            
  3            
  4            
1170 10 50         if (map->lru_prev) HM_FN(lru_promote)(map, (uint32_t)index);
  3 50          
  3 50          
  4            
1171 10 50         if (map->expires_at && map->default_ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  3 0          
  3 50          
  4 0          
    50          
    0          
1172 10           return true;
  3            
  3            
  4            
1173             }
1174              
1175 7           new_key:
  3            
  2            
  2            
1176 7 50         if (map->max_size > 0 && map->size >= map->max_size)
  3 0          
  2 50          
  2 0          
    50          
    0          
1177 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1178              
1179 7 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  3 50          
  2 50          
  2            
1180 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1181 0 0         if (!HM_FN(compact)(map)) return false;
  0 0          
  0 0          
  0            
1182             } else {
1183 0 0         if (!HM_FN(resize)(map)) return false;
  0 0          
  0 0          
  0            
1184             }
1185             }
1186              
1187 7           index = HM_FN(find_or_allocate)(map, key);
  3            
  2            
  2            
1188 7 50         if (index >= map->capacity) return false;
  3 50          
  2 50          
  2            
1189 7           map->nodes[index].value = delta;
  3            
  2            
  2            
1190 7           *out_value = delta;
  3            
  2            
  2            
1191 7 50         if (map->lru_prev) HM_FN(lru_push_front)(map, (uint32_t)index);
  3 50          
  2 50          
  2            
1192 7 50         if (map->expires_at && map->default_ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  3 0          
  2 50          
  2 0          
    50          
    0          
1193 7           return true;
  3            
  2            
  2            
1194             }
1195              
1196 5017           static bool HM_FN(decrement)(HM_MAP_TYPE* map, HM_INT_TYPE key, HM_INT_TYPE* out_value) {
  5006            
  5            
  6            
1197 5017 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  5006 50          
  5 100          
  6 50          
    50          
    50          
    100          
    50          
    50          
    50          
    100          
    50          
1198              
1199 5014           size_t index = HM_FN(find_node)(map, key);
  5005            
  4            
  5            
1200 5014 50         if (index < map->capacity && map->nodes[index].key == key) {
  5005 100          
  4 50          
  5 100          
    50          
    100          
1201             /* TTL check */
1202 5010 50         if (map->expires_at && map->expires_at[index] &&
  5003 0          
  3 50          
  4 0          
    50          
    0          
1203 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0            
1204 0           HM_FN(expire_at)(map, index);
  0            
  0            
  0            
1205 0           goto new_key;
  0            
  0            
  0            
1206             }
1207 5010 100         if (map->nodes[index].value == HM_INT_MIN) return false;
  5003 100          
  3 100          
  4            
1208 5007           *out_value = --map->nodes[index].value;
  5002            
  2            
  3            
1209 5007 50         if (map->lru_prev) HM_FN(lru_promote)(map, (uint32_t)index);
  5002 50          
  2 50          
  3            
1210 5007 50         if (map->expires_at && map->default_ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  5002 0          
  2 50          
  3 0          
    50          
    0          
1211 5007           return true;
  5002            
  2            
  3            
1212             }
1213              
1214 4           new_key:
  2            
  1            
  1            
1215 4 50         if (map->max_size > 0 && map->size >= map->max_size)
  2 0          
  1 50          
  1 0          
    50          
    0          
1216 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1217              
1218 4 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  2 50          
  1 50          
  1            
1219 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1220 0 0         if (!HM_FN(compact)(map)) return false;
  0 0          
  0 0          
  0            
1221             } else {
1222 0 0         if (!HM_FN(resize)(map)) return false;
  0 0          
  0 0          
  0            
1223             }
1224             }
1225              
1226 4           index = HM_FN(find_or_allocate)(map, key);
  2            
  1            
  1            
1227 4 50         if (index >= map->capacity) return false;
  2 50          
  1 50          
  1            
1228 4           *out_value = --map->nodes[index].value;
  2            
  1            
  1            
1229 4 50         if (map->lru_prev) HM_FN(lru_push_front)(map, (uint32_t)index);
  2 50          
  1 50          
  1            
1230 4 50         if (map->expires_at && map->default_ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  2 0          
  1 50          
  1 0          
    50          
    0          
1231 4           return true;
  2            
  1            
  1            
1232             }
1233              
1234             #else /* string keys + counters */
1235              
1236 21           static inline size_t HM_FN(find_or_allocate)(HM_MAP_TYPE* map,
  5            
  10            
  6            
1237             const char* key, uint32_t key_len,
1238             uint32_t key_hash, bool key_utf8) {
1239 21           size_t index = (size_t)key_hash & map->mask;
  5            
  10            
  6            
1240 21           const size_t original_index = index;
  5            
  10            
  6            
1241 21           HM_NODE_TYPE* nodes = map->nodes;
  5            
  10            
  6            
1242 21           size_t first_tombstone = map->capacity;
  5            
  10            
  6            
1243              
1244             do {
1245 28 100         if (nodes[index].key == NULL) {
  6 100          
  13 100          
  9            
1246 21 50         size_t target = (first_tombstone < map->capacity) ? first_tombstone : index;
  5 100          
  10 100          
  6            
1247 21           char* new_key = (char*)malloc(key_len + 1);
  5            
  10            
  6            
1248 21 50         if (!new_key) return map->capacity;
  5 50          
  10 50          
  6            
1249 21           memcpy(new_key, key, key_len);
  5            
  10            
  6            
1250 21           new_key[key_len] = '\0';
  5            
  10            
  6            
1251 21 50         if (HM_SLOT_IS_TOMBSTONE(&nodes[target])) map->tombstones--;
  5 100          
  10 100          
  6            
1252 21           nodes[target].key = new_key;
  5            
  10            
  6            
1253 21 50         nodes[target].key_len = HM_PACK_LEN(key_len, key_utf8);
  5 50          
  10 50          
  6            
1254 21           nodes[target].key_hash = key_hash;
  5            
  10            
  6            
1255 21           nodes[target].value = 0;
  5            
  10            
  6            
1256 21           map->size++;
  5            
  10            
  6            
1257 21           return target;
  5            
  10            
  6            
1258             }
1259 7 50         if (nodes[index].key == HM_STR_TOMBSTONE) {
  1 100          
  3 100          
  3            
1260 4 0         if (first_tombstone >= map->capacity) first_tombstone = index;
  0 50          
  2 50          
  2            
1261 3 50         } else if (nodes[index].key_hash == key_hash &&
  1 50          
  1 50          
  1            
1262 0 0         HM_UNPACK_LEN(nodes[index].key_len) == key_len &&
  0 0          
  0 0          
  0            
1263 0 0         memcmp(nodes[index].key, key, key_len) == 0) {
  0 0          
  0 0          
  0            
1264 0           return index;
  0            
  0            
  0            
1265             }
1266 7           index = (index + 1) & map->mask;
  1            
  3            
  3            
1267 7 50         } while (index != original_index);
  1 50          
  3 50          
  3            
1268              
1269 0 0         if (first_tombstone < map->capacity) {
  0 0          
  0 0          
  0            
1270 0           char* new_key = (char*)malloc(key_len + 1);
  0            
  0            
  0            
1271 0 0         if (!new_key) return map->capacity;
  0 0          
  0 0          
  0            
1272 0           memcpy(new_key, key, key_len);
  0            
  0            
  0            
1273 0           new_key[key_len] = '\0';
  0            
  0            
  0            
1274 0           map->tombstones--;
  0            
  0            
  0            
1275 0           nodes[first_tombstone].key = new_key;
  0            
  0            
  0            
1276 0 0         nodes[first_tombstone].key_len = HM_PACK_LEN(key_len, key_utf8);
  0 0          
  0 0          
  0            
1277 0           nodes[first_tombstone].key_hash = key_hash;
  0            
  0            
  0            
1278 0           nodes[first_tombstone].value = 0;
  0            
  0            
  0            
1279 0           map->size++;
  0            
  0            
  0            
1280 0           return first_tombstone;
  0            
  0            
  0            
1281             }
1282 0           return map->capacity;
  0            
  0            
  0            
1283             }
1284              
1285 15031           static bool HM_FN(increment)(HM_MAP_TYPE* map,
  10            
  15013            
  8            
1286             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
1287             HM_INT_TYPE* out_value) {
1288 15031 50         if (!map || !key || !out_value) return false;
  10 50          
  15013 50          
  8 50          
    50          
    50          
    50          
    50          
    50          
1289              
1290 15031           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  10            
  15013            
  8            
1291 15031 50         if (index < map->capacity && map->nodes[index].key != NULL &&
  10 100          
  15013 50          
  8 100          
    50          
    100          
1292 15019 50         map->nodes[index].key != HM_STR_TOMBSTONE) {
  8 50          
  15007 50          
  4            
1293             /* TTL check */
1294 15019 50         if (map->expires_at && map->expires_at[index] &&
  8 0          
  15007 50          
  4 0          
    50          
    0          
1295 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0            
1296 0           HM_FN(expire_at)(map, index);
  0            
  0            
  0            
1297 0           goto new_key;
  0            
  0            
  0            
1298             }
1299 15019 100         if (map->nodes[index].value == HM_INT_MAX) return false;
  8 100          
  15007 100          
  4            
1300 15016           *out_value = ++map->nodes[index].value;
  7            
  15006            
  3            
1301 15016 50         if (map->lru_prev) HM_FN(lru_promote)(map, (uint32_t)index);
  7 100          
  15006 100          
  3            
1302 15016 50         if (map->expires_at && map->default_ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  7 0          
  15006 50          
  3 0          
    50          
    0          
1303 15016           return true;
  7            
  15006            
  3            
1304             }
1305              
1306 12           new_key:
  2            
  6            
  4            
1307 12 50         if (map->max_size > 0 && map->size >= map->max_size)
  2 0          
  6 50          
  4 0          
    50          
    0          
1308 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1309              
1310 12 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  2 50          
  6 50          
  4            
1311 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1312 0 0         if (!HM_FN(compact)(map)) return false;
  0 0          
  0 0          
  0            
1313             } else {
1314 0 0         if (!HM_FN(resize)(map)) return false;
  0 0          
  0 0          
  0            
1315             }
1316             }
1317              
1318 12           index = HM_FN(find_or_allocate)(map, key, key_len, key_hash, key_utf8);
  2            
  6            
  4            
1319 12 50         if (index >= map->capacity) return false;
  2 50          
  6 50          
  4            
1320 12           *out_value = ++map->nodes[index].value;
  2            
  6            
  4            
1321 12 50         if (map->lru_prev) HM_FN(lru_push_front)(map, (uint32_t)index);
  2 50          
  6 50          
  4            
1322 12 50         if (map->expires_at && map->default_ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  2 0          
  6 100          
  4 50          
    100          
    50          
1323 12           return true;
  2            
  6            
  4            
1324             }
1325              
1326 18           static bool HM_FN(increment_by)(HM_MAP_TYPE* map,
  6            
  6            
  6            
1327             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
1328             HM_INT_TYPE delta, HM_INT_TYPE* out_value) {
1329 18 50         if (!map || !key || !out_value) return false;
  6 50          
  6 50          
  6 50          
    50          
    50          
    50          
    50          
    50          
1330              
1331 18           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  6            
  6            
  6            
1332 18 50         if (index < map->capacity && map->nodes[index].key != NULL &&
  6 100          
  6 50          
  6 100          
    50          
    100          
1333 13 50         map->nodes[index].key != HM_STR_TOMBSTONE) {
  4 50          
  4 50          
  5            
1334             /* TTL check */
1335 13 50         if (map->expires_at && map->expires_at[index] &&
  4 0          
  4 50          
  5 0          
    50          
    0          
1336 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0            
1337 0           HM_FN(expire_at)(map, index);
  0            
  0            
  0            
1338 0           goto new_key;
  0            
  0            
  0            
1339             }
1340 13           HM_INT_TYPE val = map->nodes[index].value;
  4            
  4            
  5            
1341 13 100         if (delta > 0 && val > HM_INT_MAX - delta) return false;
  4 50          
  4 100          
  5 50          
    100          
    100          
1342 10 100         if (delta < 0) {
  3 100          
  3 100          
  4            
1343 6 50         if (delta == HM_INT_MIN) { if (val < 0) return false; }
  2 0          
  2 50          
  2 0          
    50          
    0          
1344 6 100         else { if (val < HM_INT_MIN - delta) return false; }
  2 100          
  2 100          
  2            
1345             }
1346 7           map->nodes[index].value += delta;
  2            
  2            
  3            
1347 7           *out_value = map->nodes[index].value;
  2            
  2            
  3            
1348 7 50         if (map->lru_prev) HM_FN(lru_promote)(map, (uint32_t)index);
  2 50          
  2 50          
  3            
1349 7 50         if (map->expires_at && map->default_ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  2 0          
  2 50          
  3 0          
    50          
    0          
1350 7           return true;
  2            
  2            
  3            
1351             }
1352              
1353 5           new_key:
  2            
  2            
  1            
1354 5 50         if (map->max_size > 0 && map->size >= map->max_size)
  2 0          
  2 50          
  1 0          
    50          
    0          
1355 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1356              
1357 5 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  2 50          
  2 50          
  1            
1358 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1359 0 0         if (!HM_FN(compact)(map)) return false;
  0 0          
  0 0          
  0            
1360             } else {
1361 0 0         if (!HM_FN(resize)(map)) return false;
  0 0          
  0 0          
  0            
1362             }
1363             }
1364              
1365 5           index = HM_FN(find_or_allocate)(map, key, key_len, key_hash, key_utf8);
  2            
  2            
  1            
1366 5 50         if (index >= map->capacity) return false;
  2 50          
  2 50          
  1            
1367 5           map->nodes[index].value = delta;
  2            
  2            
  1            
1368 5           *out_value = delta;
  2            
  2            
  1            
1369 5 50         if (map->lru_prev) HM_FN(lru_push_front)(map, (uint32_t)index);
  2 50          
  2 50          
  1            
1370 5 50         if (map->expires_at && map->default_ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  2 0          
  2 50          
  1 0          
    50          
    0          
1371 5           return true;
  2            
  2            
  1            
1372             }
1373              
1374 5012           static bool HM_FN(decrement)(HM_MAP_TYPE* map,
1375             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
1376             HM_INT_TYPE* out_value) {
1377 5012           if (!map || !key || !out_value) return false;
1378              
1379 5012           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
1380 5012           if (index < map->capacity && map->nodes[index].key != NULL &&
1381 5008           map->nodes[index].key != HM_STR_TOMBSTONE) {
1382             /* TTL check */
1383 5008           if (map->expires_at && map->expires_at[index] &&
1384 0           (uint32_t)time(NULL) >= map->expires_at[index]) {
1385 0           HM_FN(expire_at)(map, index);
1386 0           goto new_key;
1387             }
1388 5008           if (map->nodes[index].value == HM_INT_MIN) return false;
1389 5005           *out_value = --map->nodes[index].value;
1390 5005           if (map->lru_prev) HM_FN(lru_promote)(map, (uint32_t)index);
1391 5005           if (map->expires_at && map->default_ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
1392 5005           return true;
1393             }
1394              
1395 4           new_key:
1396 4           if (map->max_size > 0 && map->size >= map->max_size)
1397 0           HM_FN(lru_evict_one)(map);
1398              
1399 4           if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
1400 0           if (map->max_size > 0 && map->tombstones > 0) {
1401 0           if (!HM_FN(compact)(map)) return false;
1402             } else {
1403 0           if (!HM_FN(resize)(map)) return false;
1404             }
1405             }
1406              
1407 4           index = HM_FN(find_or_allocate)(map, key, key_len, key_hash, key_utf8);
1408 4           if (index >= map->capacity) return false;
1409 4           *out_value = --map->nodes[index].value;
1410 4           if (map->lru_prev) HM_FN(lru_push_front)(map, (uint32_t)index);
1411 4           if (map->expires_at && map->default_ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
1412 4           return true;
1413             }
1414              
1415             #endif /* HM_KEY_IS_INT for counters */
1416              
1417             #endif /* HM_HAS_COUNTERS */
1418              
1419             /* ---- Cleanup macros for next inclusion ---- */
1420              
1421             #undef HM_SLOT_IS_EMPTY
1422             #undef HM_SLOT_IS_TOMBSTONE
1423             #undef HM_SLOT_IS_LIVE
1424             #ifdef HM_KEY_IS_INT
1425             #undef HM_EMPTY_KEY
1426             #undef HM_TOMBSTONE_KEY
1427             #undef HM_IS_RESERVED_KEY
1428             #endif
1429              
1430             #undef HM_PREFIX
1431             #undef HM_NODE_TYPE
1432             #undef HM_MAP_TYPE
1433             #undef HM_FN
1434             #undef HM_PASTE
1435             #undef HM_PASTE2
1436              
1437             #ifdef HM_KEY_IS_INT
1438             #undef HM_KEY_IS_INT
1439             #endif
1440             #ifdef HM_VALUE_IS_STR
1441             #undef HM_VALUE_IS_STR
1442             #endif
1443             #ifdef HM_VALUE_IS_SV
1444             #undef HM_VALUE_IS_SV
1445             #endif
1446             #ifdef HM_HAS_COUNTERS
1447             #undef HM_HAS_COUNTERS
1448             #endif
1449              
1450             #undef HM_INT_TYPE
1451             #undef HM_INT_MIN
1452             #undef HM_INT_MAX