File Coverage

hashmap_generic.h
Criterion Covered Total %
statement 4815 6127 78.5
branch 2762 5032 54.8
condition n/a
subroutine n/a
pod n/a
total 7577 11159 67.9


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             /* ---- Branch prediction ---- */
48              
49             #ifndef HM_LIKELY
50             #if defined(__GNUC__) || defined(__clang__)
51             #define HM_LIKELY(x) __builtin_expect(!!(x), 1)
52             #define HM_UNLIKELY(x) __builtin_expect(!!(x), 0)
53             #else
54             #define HM_LIKELY(x) (x)
55             #define HM_UNLIKELY(x) (x)
56             #endif
57             #endif
58              
59             /* ---- Constants ---- */
60              
61             #ifndef HM_INITIAL_CAPACITY
62             #define HM_INITIAL_CAPACITY 16
63             #endif
64              
65             #ifdef HM_KEY_IS_INT
66             #define HM_EMPTY_KEY HM_INT_MIN
67             #define HM_TOMBSTONE_KEY (HM_INT_MIN + 1)
68             #define HM_IS_RESERVED_KEY(k) ((k) == HM_EMPTY_KEY || (k) == HM_TOMBSTONE_KEY)
69             #endif
70              
71             /* ---- UTF-8 flag packing in uint32_t length ---- */
72              
73             #ifndef HM_UTF8_MACROS_DEFINED
74             #define HM_UTF8_MACROS_DEFINED
75             #define HM_UTF8_FLAG ((uint32_t)0x80000000U)
76             #define HM_LEN_MASK ((uint32_t)0x7FFFFFFFU)
77             #define HM_PACK_LEN(len, is_utf8) ((uint32_t)(len) | ((is_utf8) ? HM_UTF8_FLAG : 0))
78             #define HM_UNPACK_LEN(packed) ((uint32_t)((packed) & HM_LEN_MASK))
79             #define HM_UNPACK_UTF8(packed) (((packed) & HM_UTF8_FLAG) != 0)
80             #endif
81              
82             /* ---- LRU sentinel ---- */
83              
84             #ifndef HM_LRU_NONE_DEFINED
85             #define HM_LRU_NONE_DEFINED
86             #define HM_LRU_NONE UINT32_MAX
87             #endif
88              
89             /* ---- Hash functions (xxHash v0.8.3) ---- */
90              
91             #ifndef HM_HASH_FUNCTIONS_DEFINED
92             #define HM_HASH_FUNCTIONS_DEFINED
93              
94             #define XXH_INLINE_ALL
95             #include "xxhash.h"
96              
97 4342712           static inline size_t hm_hash_int64(int64_t key) {
98 4342712           return (size_t)XXH3_64bits(&key, sizeof(key));
99             }
100              
101 1075789           static inline uint32_t hm_hash_string(const char* data, uint32_t len) {
102 1075789           return (uint32_t)XXH3_64bits(data, len);
103             }
104              
105             #endif /* HM_HASH_FUNCTIONS_DEFINED */
106              
107             /* ---- Tombstone marker for string keys ---- */
108              
109             #ifndef HM_KEY_IS_INT
110             #ifndef HM_STR_TOMBSTONE_DEFINED
111             #define HM_STR_TOMBSTONE_DEFINED
112             static char hm_str_tombstone_marker;
113             #endif
114             #define HM_STR_TOMBSTONE (&hm_str_tombstone_marker)
115             #endif
116              
117             /* ---- Node struct ---- */
118              
119             typedef struct {
120             #ifdef HM_KEY_IS_INT
121             HM_INT_TYPE key;
122             #else
123             char* key; /* NULL = empty, HM_STR_TOMBSTONE = deleted */
124             uint32_t key_len; /* high bit = UTF-8 flag */
125             uint32_t key_hash;
126             #endif
127             #ifdef HM_VALUE_IS_STR
128             char* value;
129             uint32_t val_len; /* high bit = UTF-8 flag */
130             #elif defined(HM_VALUE_IS_SV)
131             void* value; /* opaque SV* — refcounted by caller */
132             #else
133             HM_INT_TYPE value;
134             #endif
135             } HM_NODE_TYPE;
136              
137             typedef struct {
138             HM_NODE_TYPE* nodes;
139             size_t capacity;
140             size_t size;
141             size_t tombstones;
142             size_t mask;
143             /* LRU fields (active only when max_size > 0) */
144             size_t max_size;
145             uint32_t lru_head;
146             uint32_t lru_tail;
147             uint32_t* lru_prev;
148             uint32_t* lru_next;
149             /* TTL fields (active only when default_ttl > 0) */
150             uint32_t default_ttl;
151             uint32_t* expires_at;
152             /* Iterator state for each() */
153             size_t iter_pos;
154             #ifdef HM_VALUE_IS_SV
155             void (*free_value_fn)(void*); /* SvREFCNT_dec callback */
156             #endif
157             } HM_MAP_TYPE;
158              
159             /* ---- Slot state macros ---- */
160              
161             #ifdef HM_KEY_IS_INT
162             #define HM_SLOT_IS_EMPTY(n) ((n)->key == HM_EMPTY_KEY)
163             #define HM_SLOT_IS_TOMBSTONE(n) ((n)->key == HM_TOMBSTONE_KEY)
164             #define HM_SLOT_IS_LIVE(n) (!HM_SLOT_IS_EMPTY(n) && !HM_SLOT_IS_TOMBSTONE(n))
165             #else
166             #define HM_SLOT_IS_EMPTY(n) ((n)->key == NULL)
167             #define HM_SLOT_IS_TOMBSTONE(n) ((n)->key == HM_STR_TOMBSTONE)
168             #define HM_SLOT_IS_LIVE(n) (!HM_SLOT_IS_EMPTY(n) && !HM_SLOT_IS_TOMBSTONE(n))
169             #endif
170              
171             /* ---- Init nodes ---- */
172              
173 2641           static inline void HM_FN(init_nodes)(HM_NODE_TYPE* nodes, size_t capacity) {
  308            
  301            
  45            
  51            
  98            
  163            
  359            
  108            
  226            
  460            
  135            
  95            
  160            
  132            
174             #ifdef HM_KEY_IS_INT
175             /* Integer keys use sentinel values — must init per-element */
176             size_t i;
177 29767241 100         for (i = 0; i < capacity; i++) {
  2733837 100          
  983437 100          
  2098131 100          
  2722595 100          
  2726674 100          
  10595564 100          
  4294503 100          
  1607728 100          
  2004772            
178 29765568           nodes[i].key = HM_EMPTY_KEY;
  2733536            
  983392            
  2098080            
  2722432            
  2726448            
  10595104            
  4294368            
  1607568            
  2004640            
179             #ifdef HM_VALUE_IS_STR
180 7056448           nodes[i].value = NULL;
  2722432            
  2726448            
  1607568            
181 7056448           nodes[i].val_len = 0;
  2722432            
  2726448            
  1607568            
182             #elif defined(HM_VALUE_IS_SV)
183 5815008           nodes[i].value = NULL;
  2733536            
  983392            
  2098080            
184             #else
185 16894112           nodes[i].value = 0;
  10595104            
  4294368            
  2004640            
186             #endif
187             }
188             #else
189             /* String keys: NULL=empty, all-zero works for pointers and lengths */
190 968           memset(nodes, 0, capacity * sizeof(HM_NODE_TYPE));
  308            
  98            
  359            
  108            
  95            
191             #endif
192 2641           }
  308            
  301            
  45            
  51            
  98            
  163            
  359            
  108            
  226            
  460            
  135            
  95            
  160            
  132            
193              
194             /* ---- Free resources for a single node ---- */
195              
196 1301256           static inline void HM_FN(free_node)(HM_MAP_TYPE* map, HM_NODE_TYPE* node) {
  81577            
  82013            
  30065            
  50125            
  80225            
  80413            
  82419            
  80240            
  81022            
  282708            
  150004            
  80125            
  60313            
  80007            
197             (void)map;
198             #ifndef HM_KEY_IS_INT
199 404586 50         if (node->key != NULL && node->key != HM_STR_TOMBSTONE) {
  81577 50          
  80225 50          
  82419 50          
  80240 50          
  80125 50          
    50          
    50          
    50          
    50          
200 404586           free(node->key);
  81577            
  80225            
  82419            
  80240            
  80125            
201 404586           node->key = NULL;
  81577            
  80225            
  82419            
  80240            
  80125            
202             }
203             #endif
204             #ifdef HM_VALUE_IS_STR
205 304167 50         if (node->value != NULL) {
  80413 50          
  82419 50          
  81022 50          
  60313            
206 304167           free(node->value);
  80413            
  82419            
  81022            
  60313            
207 304167           node->value = NULL;
  80413            
  82419            
  81022            
  60313            
208             }
209             #elif defined(HM_VALUE_IS_SV)
210 243780 50         if (node->value != NULL && map->free_value_fn) {
  81577 50          
  82013 50          
  30065 50          
  50125 50          
    50          
    50          
    50          
211 243780           map->free_value_fn(node->value);
  81577            
  82013            
  30065            
  50125            
212 243780           node->value = NULL;
  81577            
  82013            
  30065            
  50125            
213             }
214             #endif
215             (void)node;
216 1301256           }
  81577            
  82013            
  30065            
  50125            
  80225            
  80413            
  82419            
  80240            
  81022            
  282708            
  150004            
  80125            
  60313            
  80007            
217              
218             /* ---- LRU helpers ---- */
219              
220 4216           static inline void HM_FN(lru_unlink)(HM_MAP_TYPE* map, uint32_t idx) {
  342            
  185            
  2            
  2            
  1            
  181            
  341            
  3            
  181            
  2792            
  1            
  3            
  181            
  1            
221 4216           uint32_t p = map->lru_prev[idx];
  342            
  185            
  2            
  2            
  1            
  181            
  341            
  3            
  181            
  2792            
  1            
  3            
  181            
  1            
222 4216           uint32_t n = map->lru_next[idx];
  342            
  185            
  2            
  2            
  1            
  181            
  341            
  3            
  181            
  2792            
  1            
  3            
  181            
  1            
223 4216 50         if (p != HM_LRU_NONE) map->lru_next[p] = n;
  342 50          
  185 50          
  2 50          
  2 50          
  1 50          
  181 50          
  341 50          
  3 50          
  181 100          
  2792 50          
  1 50          
  3 50          
  181 50          
  1            
224 2           else map->lru_head = n;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
  0            
  0            
225 4216 50         if (n != HM_LRU_NONE) map->lru_prev[n] = p;
  342 100          
  185 50          
  2 50          
  2 50          
  1 50          
  181 50          
  341 100          
  3 50          
  181 100          
  2792 50          
  1 100          
  3 50          
  181 50          
  1            
226 4204           else map->lru_tail = p;
  342            
  184            
  2            
  2            
  1            
  181            
  341            
  2            
  181            
  2783            
  1            
  2            
  181            
  1            
227 4216           map->lru_prev[idx] = HM_LRU_NONE;
  342            
  185            
  2            
  2            
  1            
  181            
  341            
  3            
  181            
  2792            
  1            
  3            
  181            
  1            
228 4216           map->lru_next[idx] = HM_LRU_NONE;
  342            
  185            
  2            
  2            
  1            
  181            
  341            
  3            
  181            
  2792            
  1            
  3            
  181            
  1            
229 4216           }
  342            
  185            
  2            
  2            
  1            
  181            
  341            
  3            
  181            
  2792            
  1            
  3            
  181            
  1            
230              
231 4656           static inline void HM_FN(lru_push_front)(HM_MAP_TYPE* map, uint32_t idx) {
  408            
  211            
  8            
  8            
  3            
  203            
  404            
  6            
  203            
  2985            
  4            
  6            
  203            
  4            
232 4656           map->lru_prev[idx] = HM_LRU_NONE;
  408            
  211            
  8            
  8            
  3            
  203            
  404            
  6            
  203            
  2985            
  4            
  6            
  203            
  4            
233 4656           map->lru_next[idx] = map->lru_head;
  408            
  211            
  8            
  8            
  3            
  203            
  404            
  6            
  203            
  2985            
  4            
  6            
  203            
  4            
234 4656 100         if (map->lru_head != HM_LRU_NONE)
  408 100          
  211 100          
  8 100          
  8 100          
  3 100          
  203 100          
  404 100          
  6 100          
  203 100          
  2985 100          
  4 100          
  6 100          
  203 100          
  4            
235 4602           map->lru_prev[map->lru_head] = idx;
  402            
  207            
  6            
  6            
  2            
  200            
  399            
  5            
  200            
  2964            
  3            
  5            
  200            
  3            
236             else
237 54           map->lru_tail = idx;
  6            
  4            
  2            
  2            
  1            
  3            
  5            
  1            
  3            
  21            
  1            
  1            
  3            
  1            
238 4656           map->lru_head = idx;
  408            
  211            
  8            
  8            
  3            
  203            
  404            
  6            
  203            
  2985            
  4            
  6            
  203            
  4            
239 4656           }
  408            
  211            
  8            
  8            
  3            
  203            
  404            
  6            
  203            
  2985            
  4            
  6            
  203            
  4            
240              
241 133           static inline void HM_FN(lru_promote)(HM_MAP_TYPE* map, uint32_t idx) {
  2            
  4            
  2            
  2            
  0            
  0            
  1            
  2            
  1            
  117            
  0            
  2            
  0            
  0            
242 133 50         if (map->lru_head == idx) return;
  2 100          
  4 50          
  2 50          
  2 0          
  0 0          
  0 50          
  1 50          
  2 50          
  1 100          
  117 0          
  0 50          
  2 0          
  0 0          
  0            
243 117           HM_FN(lru_unlink)(map, idx);
  0            
  2            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  111            
  0            
  2            
  0            
  0            
244 117           HM_FN(lru_push_front)(map, idx);
  0            
  2            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  111            
  0            
  2            
  0            
  0            
245             }
246              
247             /* Tombstone a node at a known index (used by LRU eviction and TTL expiry) */
248 1295797           static void HM_FN(tombstone_at)(HM_MAP_TYPE* map, size_t index) {
  80849            
  80690            
  30008            
  50008            
  80006            
  80184            
  80850            
  80007            
  80285            
  282708            
  150004            
  80007            
  60184            
  80007            
249 1295797           HM_FN(free_node)(map, &map->nodes[index]);
  80849            
  80690            
  30008            
  50008            
  80006            
  80184            
  80850            
  80007            
  80285            
  282708            
  150004            
  80007            
  60184            
  80007            
250             #ifdef HM_KEY_IS_INT
251 894078           map->nodes[index].key = HM_TOMBSTONE_KEY;
  80690            
  30008            
  50008            
  80184            
  80285            
  282708            
  150004            
  60184            
  80007            
252             #else
253 401719           map->nodes[index].key = HM_STR_TOMBSTONE;
  80849            
  80006            
  80850            
  80007            
  80007            
254 401719           map->nodes[index].key_len = 0;
  80849            
  80006            
  80850            
  80007            
  80007            
255 401719           map->nodes[index].key_hash = 0;
  80849            
  80006            
  80850            
  80007            
  80007            
256             #endif
257             #ifdef HM_VALUE_IS_STR
258 301503           map->nodes[index].val_len = 0;
  80184            
  80850            
  80285            
  60184            
259             #endif
260 1295797 100         if (map->expires_at) map->expires_at[index] = 0;
  80849 100          
  80690 100          
  30008 100          
  50008 100          
  80006 50          
  80184 100          
  80850 100          
  80007 100          
  80285 100          
  282708 100          
  150004 100          
  80007 50          
  60184 100          
  80007            
261 1295797           map->size--;
  80849            
  80690            
  30008            
  50008            
  80006            
  80184            
  80850            
  80007            
  80285            
  282708            
  150004            
  80007            
  60184            
  80007            
262 1295797           map->tombstones++;
  80849            
  80690            
  30008            
  50008            
  80006            
  80184            
  80850            
  80007            
  80285            
  282708            
  150004            
  80007            
  60184            
  80007            
263 1295797           }
  80849            
  80690            
  30008            
  50008            
  80006            
  80184            
  80850            
  80007            
  80285            
  282708            
  150004            
  80007            
  60184            
  80007            
264              
265             /* Evict the LRU tail entry */
266 4092           static void HM_FN(lru_evict_one)(HM_MAP_TYPE* map) {
  342            
  183            
  2            
  2            
  1            
  181            
  341            
  1            
  181            
  2674            
  1            
  1            
  181            
  1            
267 4092           uint32_t victim = map->lru_tail;
  342            
  183            
  2            
  2            
  1            
  181            
  341            
  1            
  181            
  2674            
  1            
  1            
  181            
  1            
268 4092 50         if (victim == HM_LRU_NONE) return;
  342 50          
  183 50          
  2 50          
  2 50          
  1 50          
  181 50          
  341 50          
  1 50          
  181 50          
  2674 50          
  1 50          
  1 50          
  181 50          
  1            
269 4092           HM_FN(lru_unlink)(map, victim);
  342            
  183            
  2            
  2            
  1            
  181            
  341            
  1            
  181            
  2674            
  1            
  1            
  181            
  1            
270 4092           HM_FN(tombstone_at)(map, (size_t)victim);
  342            
  183            
  2            
  2            
  1            
  181            
  341            
  1            
  181            
  2674            
  1            
  1            
  181            
  1            
271             }
272              
273             /* Forward declaration (needed by expire_at) */
274             static bool HM_FN(compact)(HM_MAP_TYPE* map);
275              
276             /* Expire a TTL'd entry at a known index.
277             * may_compact: true for write paths (put/remove/incr/get_or_set),
278             * false for read paths (get/exists) to avoid resetting
279             * iter_pos and invalidating get_direct pointers. */
280 34           static void HM_FN(expire_at)(HM_MAP_TYPE* map, size_t index, bool may_compact) {
  3            
  3            
  2            
  2            
  1            
  0            
  2            
  1            
  1            
  15            
  1            
  2            
  0            
  1            
281 34 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_unlink)(map, (uint32_t)index);
  3 50          
  3 50          
  2 50          
  2 50          
  1 0          
  0 50          
  2 50          
  1 50          
  1 100          
  15 50          
  1 50          
  2 0          
  0 50          
  1            
282 34           HM_FN(tombstone_at)(map, index);
  3            
  3            
  2            
  2            
  1            
  0            
  2            
  1            
  1            
  15            
  1            
  2            
  0            
  1            
283 34 100         if (may_compact &&
  3 100          
  3 100          
  2 100          
  2 50          
  1 0          
  0 50          
  2 50          
  1 50          
  1 100          
  15 50          
  1 50          
  2 0          
  0 50          
  1            
284 7 50         (map->tombstones > map->capacity / 4 ||
  1 50          
  1 50          
  1 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0 0          
  0 0          
  0            
285 7 50         (map->size > 0 && map->tombstones > map->size))) {
  1 0          
  1 50          
  1 0          
  1 50          
  0 0          
  0 50          
  0 0          
  0 0          
  0 0          
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
286 0           HM_FN(compact)(map);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
287             }
288 34           }
  3            
  3            
  2            
  2            
  1            
  0            
  2            
  1            
  1            
  15            
  1            
  2            
  0            
  1            
289              
290             /* ---- Create / Destroy ---- */
291              
292 406           static HM_MAP_TYPE* HM_FN(create)(size_t max_size, uint32_t default_ttl) {
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
293 406           HM_MAP_TYPE* map = (HM_MAP_TYPE*)malloc(sizeof(HM_MAP_TYPE));
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
294 406 50         if (!map) return NULL;
  44 50          
  47 50          
  15 50          
  15 50          
  15 50          
  13 50          
  57 50          
  25 50          
  33 50          
  74 50          
  21 50          
  14 50          
  14 50          
  19            
295              
296 406           map->capacity = HM_INITIAL_CAPACITY;
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
297 406           map->mask = HM_INITIAL_CAPACITY - 1;
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
298 406           map->size = 0;
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
299 406           map->tombstones = 0;
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
300 406           map->max_size = max_size;
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
301 406           map->default_ttl = default_ttl;
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
302 406           map->lru_head = HM_LRU_NONE;
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
303 406           map->lru_tail = HM_LRU_NONE;
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
304 406           map->lru_prev = NULL;
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
305 406           map->lru_next = NULL;
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
306 406           map->iter_pos = 0;
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
307             #ifdef HM_VALUE_IS_SV
308 121           map->free_value_fn = NULL;
  44            
  47            
  15            
  15            
309             #endif
310 406           map->expires_at = NULL;
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
311              
312 406           map->nodes = (HM_NODE_TYPE*)malloc(map->capacity * sizeof(HM_NODE_TYPE));
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
313 406 50         if (!map->nodes) { free(map); return NULL; }
  44 50          
  47 50          
  15 50          
  15 50          
  15 50          
  13 50          
  57 50          
  25 50          
  33 50          
  74 50          
  21 50          
  14 50          
  14 50          
  19            
314 406           HM_FN(init_nodes)(map->nodes, map->capacity);
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
315              
316 406 100         if (max_size > 0) {
  44 100          
  47 100          
  15 100          
  15 100          
  15 100          
  13 100          
  57 100          
  25 100          
  33 100          
  74 100          
  21 100          
  14 100          
  14 100          
  19            
317 54           map->lru_prev = (uint32_t*)malloc(map->capacity * sizeof(uint32_t));
  6            
  4            
  2            
  2            
  1            
  3            
  5            
  1            
  3            
  21            
  1            
  1            
  3            
  1            
318 54           map->lru_next = (uint32_t*)malloc(map->capacity * sizeof(uint32_t));
  6            
  4            
  2            
  2            
  1            
  3            
  5            
  1            
  3            
  21            
  1            
  1            
  3            
  1            
319 54 50         if (!map->lru_prev || !map->lru_next) {
  6 50          
  4 50          
  2 50          
  2 50          
  1 50          
  3 50          
  5 50          
  1 50          
  3 50          
  21 50          
  1 50          
  1 50          
  3 50          
  1 50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
320 0           free(map->lru_prev); free(map->lru_next);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
321 0           free(map->nodes); free(map);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
322 0           return NULL;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
323             }
324 54           memset(map->lru_prev, 0xFF, map->capacity * sizeof(uint32_t));
  6            
  4            
  2            
  2            
  1            
  3            
  5            
  1            
  3            
  21            
  1            
  1            
  3            
  1            
325 54           memset(map->lru_next, 0xFF, map->capacity * sizeof(uint32_t));
  6            
  4            
  2            
  2            
  1            
  3            
  5            
  1            
  3            
  21            
  1            
  1            
  3            
  1            
326             }
327              
328 406 100         if (default_ttl > 0) {
  44 100          
  47 100          
  15 100          
  15 50          
  15 50          
  13 100          
  57 100          
  25 100          
  33 100          
  74 50          
  21 100          
  14 50          
  14 100          
  19            
329 39           map->expires_at = (uint32_t*)calloc(map->capacity, sizeof(uint32_t));
  4            
  4            
  2            
  2            
  0            
  0            
  5            
  1            
  1            
  18            
  0            
  1            
  0            
  1            
330 39 50         if (!map->expires_at) {
  4 50          
  4 50          
  2 50          
  2 0          
  0 0          
  0 50          
  5 50          
  1 50          
  1 50          
  18 0          
  0 50          
  1 0          
  0 50          
  1            
331 0           free(map->lru_prev); free(map->lru_next);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
332 0           free(map->nodes); free(map);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
333 0           return NULL;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
334             }
335             }
336              
337 406           return map;
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
338             }
339              
340 406           static void HM_FN(destroy)(HM_MAP_TYPE* map) {
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
341 406 50         if (!map) return;
  44 50          
  47 50          
  15 50          
  15 50          
  15 50          
  13 50          
  57 50          
  25 50          
  33 50          
  74 50          
  21 50          
  14 50          
  14 50          
  19            
342             #if !defined(HM_KEY_IS_INT) || defined(HM_VALUE_IS_STR) || defined(HM_VALUE_IS_SV)
343             {
344             size_t i;
345 1475076 100         for (i = 0; i < map->capacity; i++) {
  149804 100          
  151295 100          
  65807 100          
  131535 100          
  148159 100          
  148125 100          
  151977 100          
  148329 100          
  149809 100          
  147886 100          
  82350            
346 1474784 100         if (HM_SLOT_IS_LIVE(&map->nodes[i])) {
  149760 100          
  151248 100          
  65792 100          
  131520 100          
  148144 100          
  148112 100          
  151920 100          
  148304 100          
  149776 100          
  147872 100          
  82336 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
347 3436           HM_FN(free_node)(map, &map->nodes[i]);
  629            
  1024            
  18            
  18            
  21            
  30            
  1072            
  35            
  539            
  20            
  30            
348             }
349             }
350             }
351             #endif
352 406           free(map->lru_prev);
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
353 406           free(map->lru_next);
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
354 406           free(map->expires_at);
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
355 406           free(map->nodes);
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
356 406           free(map);
  44            
  47            
  15            
  15            
  15            
  13            
  57            
  25            
  33            
  74            
  21            
  14            
  14            
  19            
357             }
358              
359             /* ---- clear: remove all entries without destroying the map ---- */
360              
361 53           static void HM_FN(clear)(HM_MAP_TYPE* map) {
  3            
  7            
  3            
  3            
  2            
  3            
  9            
  2            
  2            
  8            
  3            
  2            
  3            
  3            
362 53 50         if (!map) return;
  3 50          
  7 50          
  3 50          
  3 50          
  2 50          
  3 50          
  9 50          
  2 50          
  2 50          
  8 50          
  3 50          
  2 50          
  3 50          
  3            
363             #if !defined(HM_KEY_IS_INT) || defined(HM_VALUE_IS_STR) || defined(HM_VALUE_IS_SV)
364             {
365             size_t i;
366 5335 100         for (i = 0; i < map->capacity; i++) {
  275 100          
  791 100          
  83 100          
  275 100          
  514 100          
  531 100          
  1305 100          
  514 100          
  514 100          
  258 100          
  275            
367 5296 100         if (HM_SLOT_IS_LIVE(&map->nodes[i]))
  272 100          
  784 100          
  80 100          
  272 100          
  512 100          
  528 100          
  1296 100          
  512 100          
  512 100          
  256 100          
  272 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
368 2023           HM_FN(free_node)(map, &map->nodes[i]);
  99            
  299            
  39            
  99            
  198            
  199            
  497            
  198            
  198            
  98            
  99            
369             }
370             }
371             #endif
372 53           HM_FN(init_nodes)(map->nodes, map->capacity);
  3            
  7            
  3            
  3            
  2            
  3            
  9            
  2            
  2            
  8            
  3            
  2            
  3            
  3            
373 53           map->size = 0;
  3            
  7            
  3            
  3            
  2            
  3            
  9            
  2            
  2            
  8            
  3            
  2            
  3            
  3            
374 53           map->tombstones = 0;
  3            
  7            
  3            
  3            
  2            
  3            
  9            
  2            
  2            
  8            
  3            
  2            
  3            
  3            
375 53           map->iter_pos = 0;
  3            
  7            
  3            
  3            
  2            
  3            
  9            
  2            
  2            
  8            
  3            
  2            
  3            
  3            
376 53 50         if (map->lru_prev) {
  3 50          
  7 50          
  3 50          
  3 50          
  2 50          
  3 50          
  9 50          
  2 50          
  2 100          
  8 50          
  3 50          
  2 50          
  3 50          
  3            
377 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            
378 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            
379 1           map->lru_head = HM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
380 1           map->lru_tail = HM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
381             }
382 53 50         if (map->expires_at)
  3 100          
  7 50          
  3 50          
  3 50          
  2 50          
  3 100          
  9 50          
  2 50          
  2 100          
  8 50          
  3 50          
  2 50          
  3 50          
  3            
383 11           memset(map->expires_at, 0, map->capacity * sizeof(uint32_t));
  0            
  4            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
  3            
  0            
  0            
  0            
  0            
384             }
385              
386             /* ---- Rehash: unified resize (grow=true) and compact (grow=false) ---- */
387              
388 2182           static bool HM_FN(rehash)(HM_MAP_TYPE* map, bool grow) {
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
389 2182           size_t old_capacity = map->capacity;
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
390 2182           HM_NODE_TYPE* old_nodes = map->nodes;
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
391              
392 2182 100         size_t new_capacity = grow ? old_capacity * 2 : old_capacity;
  261 100          
  247 100          
  27 100          
  33 100          
  81 100          
  147 100          
  293 100          
  81 100          
  191 100          
  378 100          
  111 100          
  79 100          
  143 100          
  110            
393 2182           size_t new_mask = new_capacity - 1;
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
394 2182           HM_NODE_TYPE* new_nodes = (HM_NODE_TYPE*)malloc(new_capacity * sizeof(HM_NODE_TYPE));
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
395 2182 50         if (!new_nodes) return false;
  261 50          
  247 50          
  27 50          
  33 50          
  81 50          
  147 50          
  293 50          
  81 50          
  191 50          
  378 50          
  111 50          
  79 50          
  143 50          
  110            
396              
397             /* Allocate new LRU arrays if active */
398 2182           uint32_t* new_lru_prev = NULL;
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
399 2182           uint32_t* new_lru_next = NULL;
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
400 2182           uint32_t* old_to_new = NULL;
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
401 2182 100         if (map->lru_prev) {
  261 100          
  247 50          
  27 50          
  33 50          
  81 100          
  147 100          
  293 50          
  81 100          
  191 100          
  378 50          
  111 50          
  79 100          
  143 50          
  110            
402 689 50         if (new_capacity > (size_t)(uint32_t)-2) {
  104 50          
  66 0          
  0 0          
  0 0          
  0 50          
  66 50          
  104 0          
  0 50          
  66 50          
  217 0          
  0 0          
  0 50          
  66 0          
  0            
403 0           free(new_nodes);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
404 0           return false; /* capacity would overflow uint32_t LRU indices */
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
405             }
406 689           new_lru_prev = (uint32_t*)malloc(new_capacity * sizeof(uint32_t));
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
407 689           new_lru_next = (uint32_t*)malloc(new_capacity * sizeof(uint32_t));
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
408 689           old_to_new = (uint32_t*)malloc(old_capacity * sizeof(uint32_t));
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
409 689 50         if (!new_lru_prev || !new_lru_next || !old_to_new) {
  104 50          
  66 50          
  0 50          
  0 50          
  0 50          
  66 0          
  104 0          
  0 0          
  66 0          
  217 0          
  0 0          
  0 0          
  66 0          
  0 0          
    50          
    50          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    0          
410 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            
411 0           return false;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
412             }
413 689           memset(new_lru_prev, 0xFF, new_capacity * sizeof(uint32_t));
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
414 689           memset(new_lru_next, 0xFF, new_capacity * sizeof(uint32_t));
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
415 689           memset(old_to_new, 0xFF, old_capacity * sizeof(uint32_t));
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
416             }
417              
418             /* Allocate new TTL array if active */
419 2182           uint32_t* new_expires_at = NULL;
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
420 2182 100         if (map->expires_at) {
  261 100          
  247 50          
  27 50          
  33 50          
  81 50          
  147 100          
  293 50          
  81 50          
  191 100          
  378 50          
  111 50          
  79 50          
  143 50          
  110            
421 100           new_expires_at = (uint32_t*)calloc(new_capacity, sizeof(uint32_t));
  34            
  12            
  0            
  0            
  0            
  0            
  46            
  0            
  0            
  8            
  0            
  0            
  0            
  0            
422 100 50         if (!new_expires_at) {
  34 50          
  12 0          
  0 0          
  0 0          
  0 0          
  0 50          
  46 0          
  0 0          
  0 50          
  8 0          
  0 0          
  0 0          
  0 0          
  0            
423 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            
424 0           return false;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
425             }
426             }
427              
428 2182           HM_FN(init_nodes)(new_nodes, new_capacity);
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
429              
430             /* Copy live entries */
431             {
432             size_t i;
433 41255766 100         for (i = 0; i < old_capacity; i++) {
  2581413 100          
  2581751 100          
  917547 100          
  1966321 100          
  2572817 100          
  2573939 100          
  2583397 100          
  2572817 100          
  2576351 100          
  10182906 100          
  4129327 100          
  2572559 100          
  1525103 100          
  1919518            
434 41253584 100         if (HM_SLOT_IS_LIVE(&old_nodes[i])) {
  2581152 100          
  2581504 100          
  917520 100          
  1966288 100          
  2572736 100          
  2573792 100          
  2583104 100          
  2572736 100          
  2576160 100          
  10182528 100          
  4129216 100          
  2572480 100          
  1524960 100          
  1919408 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
435             #ifdef HM_KEY_IS_INT
436 1989543           size_t index = hm_hash_int64((int64_t)old_nodes[i].key) & new_mask;
  199301            
  79143            
  148439            
  196937            
  198029            
  596692            
  291180            
  127593            
  152229            
437 2414491 100         while (new_nodes[index].key != HM_EMPTY_KEY)
  243826 100          
  96829 100          
  181600 100          
  240778 100          
  242246 100          
  715468 100          
  352603 100          
  155981 100          
  185160            
438 424948           index = (index + 1) & new_mask;
  44525            
  17686            
  33161            
  43841            
  44217            
  118776            
  61423            
  28388            
  32931            
439             #else
440 987905           size_t index = (size_t)old_nodes[i].key_hash & new_mask;
  198901            
  196277            
  200365            
  196277            
  196085            
441 1211499 100         while (new_nodes[index].key != NULL)
  243873 100          
  240737 100          
  245647 100          
  240737 100          
  240505            
442 223594           index = (index + 1) & new_mask;
  44972            
  44460            
  45282            
  44460            
  44420            
443             #endif
444 2977448           new_nodes[index] = old_nodes[i];
  198901            
  199301            
  79143            
  148439            
  196277            
  196937            
  200365            
  196277            
  198029            
  596692            
  291180            
  196085            
  127593            
  152229            
445 2977448 100         if (old_to_new) old_to_new[i] = (uint32_t)index;
  198901 100          
  199301 50          
  79143 50          
  148439 50          
  196277 100          
  196937 100          
  200365 50          
  196277 100          
  198029 100          
  596692 50          
  291180 50          
  196085 100          
  127593 50          
  152229            
446 2977448 100         if (new_expires_at)
  198901 100          
  199301 50          
  79143 50          
  148439 50          
  196277 50          
  196937 100          
  200365 50          
  196277 50          
  198029 100          
  596692 50          
  291180 50          
  196085 50          
  127593 50          
  152229            
447 2204           new_expires_at[index] = map->expires_at[i];
  664            
  336            
  0            
  0            
  0            
  0            
  1000            
  0            
  0            
  204            
  0            
  0            
  0            
  0            
448             }
449             }
450             }
451              
452             /* Rebuild LRU linked list preserving order */
453 2182 100         if (map->lru_prev && map->lru_head != HM_LRU_NONE) {
  261 50          
  247 100          
  27 50          
  33 50          
  81 0          
  147 50          
  293 0          
  81 50          
  191 0          
  378 100          
  111 50          
  79 100          
  143 50          
  110 50          
    0          
    100          
    50          
    100          
    50          
    50          
    0          
    50          
    0          
    100          
    50          
    50          
    0          
454 689           uint32_t old_idx = map->lru_head;
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
455 689           uint32_t prev_new = HM_LRU_NONE;
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
456 689           uint32_t new_head = HM_LRU_NONE;
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
457 689           uint32_t new_tail = HM_LRU_NONE;
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
458              
459 9749 100         while (old_idx != HM_LRU_NONE) {
  1468 100          
  726 0          
  0 0          
  0 0          
  0 100          
  726 100          
  1468 0          
  0 100          
  726 100          
  3909 0          
  0 0          
  0 100          
  726 0          
  0            
460 9060           uint32_t next_old = map->lru_next[old_idx];
  1364            
  660            
  0            
  0            
  0            
  660            
  1364            
  0            
  660            
  3692            
  0            
  0            
  660            
  0            
461 9060           uint32_t ni = old_to_new[old_idx];
  1364            
  660            
  0            
  0            
  0            
  660            
  1364            
  0            
  660            
  3692            
  0            
  0            
  660            
  0            
462 9060 50         if (ni != HM_LRU_NONE) {
  1364 50          
  660 0          
  0 0          
  0 0          
  0 50          
  660 50          
  1364 0          
  0 50          
  660 50          
  3692 0          
  0 0          
  0 50          
  660 0          
  0            
463 9060           new_lru_prev[ni] = prev_new;
  1364            
  660            
  0            
  0            
  0            
  660            
  1364            
  0            
  660            
  3692            
  0            
  0            
  660            
  0            
464 9060           new_lru_next[ni] = HM_LRU_NONE;
  1364            
  660            
  0            
  0            
  0            
  660            
  1364            
  0            
  660            
  3692            
  0            
  0            
  660            
  0            
465 9060 100         if (prev_new != HM_LRU_NONE) new_lru_next[prev_new] = ni;
  1364 100          
  660 0          
  0 0          
  0 0          
  0 100          
  660 100          
  1364 0          
  0 100          
  660 100          
  3692 0          
  0 0          
  0 100          
  660 0          
  0            
466 689           else new_head = ni;
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
467 9060           new_tail = ni;
  1364            
  660            
  0            
  0            
  0            
  660            
  1364            
  0            
  660            
  3692            
  0            
  0            
  660            
  0            
468 9060           prev_new = ni;
  1364            
  660            
  0            
  0            
  0            
  660            
  1364            
  0            
  660            
  3692            
  0            
  0            
  660            
  0            
469             }
470 9060           old_idx = next_old;
  1364            
  660            
  0            
  0            
  0            
  660            
  1364            
  0            
  660            
  3692            
  0            
  0            
  660            
  0            
471             }
472              
473 689           free(map->lru_prev); free(map->lru_next);
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
474 689           map->lru_prev = new_lru_prev;
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
475 689           map->lru_next = new_lru_next;
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
476 689           map->lru_head = new_head;
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
477 689           map->lru_tail = new_tail;
  104            
  66            
  0            
  0            
  0            
  66            
  104            
  0            
  66            
  217            
  0            
  0            
  66            
  0            
478 1493 50         } else if (map->lru_prev) {
  157 50          
  181 50          
  27 50          
  33 50          
  81 50          
  81 50          
  189 50          
  81 50          
  125 50          
  161 50          
  111 50          
  79 50          
  77 50          
  110            
479 0           free(map->lru_prev); free(map->lru_next);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
480 0           map->lru_prev = new_lru_prev;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
481 0           map->lru_next = new_lru_next;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
482             }
483 2182           free(old_to_new);
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
484              
485 2182 100         if (map->expires_at) {
  261 100          
  247 50          
  27 50          
  33 50          
  81 50          
  147 100          
  293 50          
  81 50          
  191 100          
  378 50          
  111 50          
  79 50          
  143 50          
  110            
486 100           free(map->expires_at);
  34            
  12            
  0            
  0            
  0            
  0            
  46            
  0            
  0            
  8            
  0            
  0            
  0            
  0            
487 100           map->expires_at = new_expires_at;
  34            
  12            
  0            
  0            
  0            
  0            
  46            
  0            
  0            
  8            
  0            
  0            
  0            
  0            
488             }
489              
490 2182           free(old_nodes);
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
491 2182           map->nodes = new_nodes;
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
492 2182           map->capacity = new_capacity;
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
493 2182           map->mask = new_mask;
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
494 2182           map->tombstones = 0;
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
495 2182           map->iter_pos = 0;
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
496 2182           return true;
  261            
  247            
  27            
  33            
  81            
  147            
  293            
  81            
  191            
  378            
  111            
  79            
  143            
  110            
497             }
498              
499 638           static bool HM_FN(resize)(HM_MAP_TYPE* map) {
  69            
  91            
  14            
  19            
  31            
  31            
  101            
  31            
  67            
  58            
  32            
  29            
  28            
  37            
500 638           return HM_FN(rehash)(map, true);
  69            
  91            
  14            
  19            
  31            
  31            
  101            
  31            
  67            
  58            
  32            
  29            
  28            
  37            
501             }
502              
503 1544           static bool HM_FN(compact)(HM_MAP_TYPE* map) {
  192            
  156            
  13            
  14            
  50            
  116            
  192            
  50            
  124            
  320            
  79            
  50            
  115            
  73            
504 1544           return HM_FN(rehash)(map, false);
  192            
  156            
  13            
  14            
  50            
  116            
  192            
  50            
  124            
  320            
  79            
  50            
  115            
  73            
505             }
506              
507             /* ---- find_node: find existing key or return empty/capacity (not found) ---- */
508              
509             #ifdef HM_KEY_IS_INT
510              
511 1351036           static inline size_t HM_FN(find_node)(const HM_MAP_TYPE* map, HM_INT_TYPE key) {
  130531            
  60024            
  100024            
  130016            
  130121            
  400219            
  200039            
  90017            
  110045            
512 1351036           size_t index = hm_hash_int64((int64_t)key) & map->mask;
  130531            
  60024            
  100024            
  130016            
  130121            
  400219            
  200039            
  90017            
  110045            
513 1351036           const size_t original_index = index;
  130531            
  60024            
  100024            
  130016            
  130121            
  400219            
  200039            
  90017            
  110045            
514 1351036           const HM_NODE_TYPE* nodes = map->nodes;
  130531            
  60024            
  100024            
  130016            
  130121            
  400219            
  200039            
  90017            
  110045            
515              
516             do {
517 1573199           HM_INT_TYPE k = nodes[index].key;
  152680            
  75350            
  118830            
  152088            
  152211            
  452865            
  229749            
  108609            
  130817            
518 1573199 100         if (k == key) return index;
  152680 100          
  75350 100          
  118830 100          
  152088 100          
  152211 100          
  452865 100          
  229749 100          
  108609 100          
  130817            
519 222241 100         if (k == HM_EMPTY_KEY) return index;
  22155 100          
  15331 100          
  18811 100          
  22076 100          
  22095 100          
  52672 100          
  29720 100          
  18597 100          
  20784            
520 222163           index = (index + 1) & map->mask;
  22149            
  15326            
  18806            
  22072            
  22090            
  52646            
  29710            
  18592            
  20772            
521 222163 50         } while (HM_LIKELY(index != original_index));
  22149 50          
  15326 50          
  18806 50          
  22072 50          
  22090 50          
  52646 50          
  29710 50          
  18592 50          
  20772            
522              
523 0           return map->capacity;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
524             }
525              
526             #else /* string keys */
527              
528 671202           static inline size_t HM_FN(find_node)(const HM_MAP_TYPE* map,
  130534            
  130038            
  130539            
  150051            
  130040            
529             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8) {
530 671202           size_t index = (size_t)key_hash & map->mask;
  130534            
  130038            
  130539            
  150051            
  130040            
531 671202           const size_t original_index = index;
  130534            
  130038            
  130539            
  150051            
  130040            
532 671202           const HM_NODE_TYPE* nodes = map->nodes;
  130534            
  130038            
  130539            
  150051            
  130040            
533              
534             do {
535 786804 100         if (nodes[index].key == NULL) return index; /* empty */
  153675 100          
  153145 100          
  153675 100          
  173161 100          
  153148            
536 786759 100         if (nodes[index].key != HM_STR_TOMBSTONE &&
  153670 100          
  153136 100          
  153668 100          
  173147 100          
  153138            
537 749511 100         nodes[index].key_hash == key_hash &&
  146203 100          
  145699 100          
  146201 100          
  165708 100          
  145700            
538 671157 100         nodes[index].key_len == HM_PACK_LEN(key_len, key_utf8) &&
  130529 50          
  130029 100          
  130532 50          
  150037 100          
  130030 50          
    100          
    50          
    100          
    50          
539 671157 50         memcmp(nodes[index].key, key, key_len) == 0) {
  130529 50          
  130029 50          
  130532 50          
  150037 50          
  130030            
540 671157           return index; /* found */
  130529            
  130029            
  130532            
  150037            
  130030            
541             }
542 115602           index = (index + 1) & map->mask;
  23141            
  23107            
  23136            
  23110            
  23108            
543 115602 50         } while (HM_LIKELY(index != original_index));
  23141 50          
  23107 50          
  23136 50          
  23110 50          
  23108            
544              
545 0           return map->capacity;
  0            
  0            
  0            
  0            
  0            
546             }
547              
548             #endif
549              
550             /* ---- find_slot_for_insert ---- */
551              
552             #ifdef HM_KEY_IS_INT
553              
554 1002112           static inline size_t HM_FN(find_slot_for_insert)(HM_MAP_TYPE* map, HM_INT_TYPE key, bool* found) {
  82204            
  30070            
  50130            
  80595            
  81211            
  385952            
  150227            
  60495            
  81228            
555 1002112           size_t index = hm_hash_int64((int64_t)key) & map->mask;
  82204            
  30070            
  50130            
  80595            
  81211            
  385952            
  150227            
  60495            
  81228            
556 1002112           const size_t original_index = index;
  82204            
  30070            
  50130            
  80595            
  81211            
  385952            
  150227            
  60495            
  81228            
557 1002112           HM_NODE_TYPE* nodes = map->nodes;
  82204            
  30070            
  50130            
  80595            
  81211            
  385952            
  150227            
  60495            
  81228            
558 1002112           size_t first_tombstone = map->capacity;
  82204            
  30070            
  50130            
  80595            
  81211            
  385952            
  150227            
  60495            
  81228            
559              
560             do {
561 2833432           HM_INT_TYPE k = nodes[index].key;
  270742            
  108691            
  193374            
  266882            
  268566            
  916014            
  405847            
  182163            
  221153            
562 2833432 100         if (k == key) {
  270742 100          
  108691 100          
  193374 100          
  266882 100          
  268566 100          
  916014 100          
  405847 100          
  182163 100          
  221153            
563 39           *found = true;
  8            
  3            
  3            
  1            
  8            
  11            
  2            
  1            
  2            
564 39           return index;
  8            
  3            
  3            
  1            
  8            
  11            
  2            
  1            
  2            
565             }
566 2833393 100         if (k == HM_EMPTY_KEY) {
  270734 100          
  108688 100          
  193371 100          
  266881 100          
  268558 100          
  916003 100          
  405845 100          
  182162 100          
  221151            
567 1002073           *found = false;
  82196            
  30067            
  50127            
  80594            
  81203            
  385941            
  150225            
  60494            
  81226            
568 1002073 100         return (first_tombstone < map->capacity) ? first_tombstone : index;
  82196 100          
  30067 100          
  50127 100          
  80594 100          
  81203 100          
  385941 100          
  150225 100          
  60494 100          
  81226            
569             }
570 1831320 100         if (k == HM_TOMBSTONE_KEY && first_tombstone >= map->capacity) {
  188538 100          
  78621 100          
  143244 50          
  186287 100          
  187355 50          
  530062 100          
  255620 100          
  121668 100          
  139925 100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    50          
571 1823           first_tombstone = index;
  71            
  1            
  1            
  54            
  54            
  1579            
  4            
  54            
  5            
572             }
573 1831320           index = (index + 1) & map->mask;
  188538            
  78621            
  143244            
  186287            
  187355            
  530062            
  255620            
  121668            
  139925            
574 1831320 50         } while (index != original_index);
  188538 50          
  78621 50          
  143244 50          
  186287 50          
  187355 50          
  530062 50          
  255620 50          
  121668 50          
  139925            
575              
576 0           *found = false;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
577 0           return first_tombstone;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
578             }
579              
580             #else /* string keys */
581              
582 405274           static inline size_t HM_FN(find_slot_for_insert)(HM_MAP_TYPE* map,
  81927            
  80222            
  82769            
  80235            
  80121            
583             const char* key, uint32_t key_len,
584             uint32_t key_hash, bool key_utf8, bool* found) {
585 405274           size_t index = (size_t)key_hash & map->mask;
  81927            
  80222            
  82769            
  80235            
  80121            
586 405274           const size_t original_index = index;
  81927            
  80222            
  82769            
  80235            
  80121            
587 405274           HM_NODE_TYPE* nodes = map->nodes;
  81927            
  80222            
  82769            
  80235            
  80121            
588 405274           size_t first_tombstone = map->capacity;
  81927            
  80222            
  82769            
  80235            
  80121            
589              
590             do {
591 1263545 100         if (nodes[index].key == NULL) {
  254377 100          
  250960 100          
  256655 100          
  250974 100          
  250579            
592 405251           *found = false;
  81919            
  80221            
  82760            
  80231            
  80120            
593 405251 100         return (first_tombstone < map->capacity) ? first_tombstone : index;
  81919 100          
  80221 100          
  82760 100          
  80231 100          
  80120            
594             }
595 858294 100         if (nodes[index].key == HM_STR_TOMBSTONE) {
  172458 100          
  170739 100          
  173895 100          
  170743 100          
  170459            
596 276 100         if (first_tombstone >= map->capacity) first_tombstone = index;
  135 50          
  2 100          
  135 50          
  2 50          
  2            
597 858018 100         } else if (nodes[index].key_hash == key_hash &&
  172323 100          
  170737 100          
  173760 100          
  170741 100          
  170457            
598 23 50         nodes[index].key_len == HM_PACK_LEN(key_len, key_utf8) &&
  8 50          
  1 50          
  9 50          
  4 50          
  1 50          
    50          
    50          
    50          
    50          
599 23 50         memcmp(nodes[index].key, key, key_len) == 0) {
  8 50          
  1 50          
  9 50          
  4 50          
  1            
600 23           *found = true;
  8            
  1            
  9            
  4            
  1            
601 23           return index;
  8            
  1            
  9            
  4            
  1            
602             }
603 858271           index = (index + 1) & map->mask;
  172450            
  170738            
  173886            
  170739            
  170458            
604 858271 50         } while (index != original_index);
  172450 50          
  170738 50          
  173886 50          
  170739 50          
  170458            
605              
606 0           *found = false;
  0            
  0            
  0            
  0            
  0            
607 0           return first_tombstone;
  0            
  0            
  0            
  0            
  0            
608             }
609              
610             #endif
611              
612             /* ---- put ---- */
613              
614             #ifdef HM_KEY_IS_INT
615              
616 998684           static bool HM_FN(put)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  82018            
  30067            
  50127            
  80416            
  81023            
  383265            
  150226            
  60315            
  81227            
617             #ifdef HM_VALUE_IS_STR
618             const char* value, uint32_t val_len, bool val_utf8,
619             #elif defined(HM_VALUE_IS_SV)
620             void* value,
621             #else
622             HM_INT_TYPE value,
623             #endif
624             uint32_t entry_ttl) {
625 998684 50         if (!map || HM_IS_RESERVED_KEY(key)) return false;
  82018 100          
  30067 100          
  50127 50          
  80416 100          
  81023 100          
  383265 50          
  150226 100          
  60315 100          
  81227 50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
626              
627 998666 100         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  82016 100          
  30065 100          
  50125 100          
  80414 100          
  81021 100          
  383263 100          
  150224 100          
  60313 100          
  81225            
628 853 100         if (map->max_size > 0 && map->tombstones > 0) {
  157 50          
  14 50          
  19 0          
  97 50          
  133 0          
  270 100          
  32 50          
  94 100          
  37 50          
    100          
    100          
    50          
    0          
    100          
    50          
    50          
    0          
629 476 50         if (!HM_FN(compact)(map)) return false;
  66 0          
  0 0          
  0 50          
  66 50          
  66 50          
  212 0          
  0 50          
  66 0          
  0            
630             } else {
631 377 50         if (!HM_FN(resize)(map)) return false;
  91 50          
  14 50          
  19 50          
  31 50          
  67 50          
  58 50          
  32 50          
  28 50          
  37            
632             }
633             }
634              
635             bool found;
636 998666           size_t index = HM_FN(find_slot_for_insert)(map, key, &found);
  82016            
  30065            
  50125            
  80414            
  81021            
  383263            
  150224            
  60313            
  81225            
637 998666 50         if (index >= map->capacity) return false;
  82016 50          
  30065 50          
  50125 50          
  80414 50          
  81021 50          
  383263 50          
  150224 50          
  60313 50          
  81225            
638              
639             /* LRU eviction: only on new insert at capacity */
640 998666 100         if (!found && map->max_size > 0 && map->size >= map->max_size) {
  82016 100          
  30065 100          
  50125 100          
  80414 100          
  81021 100          
  383263 100          
  150224 100          
  60313 100          
  81225 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
641 3404           HM_FN(lru_evict_one)(map);
  183            
  2            
  2            
  181            
  181            
  2672            
  1            
  181            
  1            
642             /* Re-probe after eviction to find optimal insertion slot */
643 3404           index = HM_FN(find_slot_for_insert)(map, key, &found);
  183            
  2            
  2            
  181            
  181            
  2672            
  1            
  181            
  1            
644 3404 50         if (index >= map->capacity) return false;
  183 50          
  2 50          
  2 50          
  181 50          
  181 50          
  2672 50          
  1 50          
  181 50          
  1            
645             }
646              
647             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
648 998666 100         if (HM_UNLIKELY(entry_ttl > 0 && !map->expires_at)) {
  82016 100          
  30065 50          
  50125 0          
  80414 50          
  81021 0          
  383263 50          
  150224 0          
  60313 50          
  81225 0          
    100          
    100          
    100          
    50          
    50          
    0          
    50          
    0          
649 12           map->expires_at = (uint32_t*)calloc(map->capacity, sizeof(uint32_t));
  3            
  0            
  0            
  0            
  0            
  8            
  1            
  0            
  0            
650 12 50         if (!map->expires_at) return false;
  3 0          
  0 0          
  0 0          
  0 0          
  0 50          
  8 50          
  1 0          
  0 0          
  0            
651             }
652              
653             #ifdef HM_VALUE_IS_STR
654             /* Pre-allocate value before modifying map state */
655 221748           char* new_val = NULL;
  80414            
  81021            
  60313            
656 221748           uint32_t new_val_len = 0;
  80414            
  81021            
  60313            
657 221748 50         if (value && val_len > 0) {
  80414 100          
  81021 50          
  60313 100          
    50          
    50          
658 221746           new_val = (char*)malloc(val_len + 1);
  80413            
  81020            
  60313            
659 221746 50         if (!new_val) return false;
  80413 50          
  81020 50          
  60313            
660 221746           memcpy(new_val, value, val_len);
  80413            
  81020            
  60313            
661 221746           new_val[val_len] = '\0';
  80413            
  81020            
  60313            
662 221746 100         new_val_len = HM_PACK_LEN(val_len, val_utf8);
  80413 100          
  81020 100          
  60313            
663 2 50         } else if (value) {
  1 50          
  1 0          
  0            
664 2           new_val = (char*)malloc(1);
  1            
  1            
  0            
665 2 50         if (!new_val) return false;
  1 50          
  1 0          
  0            
666 2           new_val[0] = '\0';
  1            
  1            
  0            
667 2 50         new_val_len = HM_PACK_LEN(0, val_utf8);
  1 50          
  1 0          
  0            
668             }
669             #endif
670              
671 998666 100         if (found) {
  82016 100          
  30065 100          
  50125 100          
  80414 100          
  81021 100          
  383263 100          
  150224 100          
  60313 100          
  81225            
672             #ifdef HM_VALUE_IS_STR
673 7 50         if (map->nodes[index].value) free(map->nodes[index].value);
  1 50          
  5 50          
  1            
674             #elif defined(HM_VALUE_IS_SV)
675 10 50         if (map->nodes[index].value && map->free_value_fn)
  6 50          
  2 50          
  2 50          
    50          
    50          
676 10           map->free_value_fn(map->nodes[index].value);
  6            
  2            
  2            
677             #endif
678             } else {
679 998641 100         if (map->nodes[index].key == HM_TOMBSTONE_KEY) {
  82010 50          
  30063 50          
  50123 100          
  80413 100          
  81016 100          
  383257 100          
  150223 100          
  60312 100          
  81224            
680 1057           map->tombstones--;
  62            
  0            
  0            
  46            
  46            
  848            
  4            
  46            
  5            
681             }
682 998641           map->size++;
  82010            
  30063            
  50123            
  80413            
  81016            
  383257            
  150223            
  60312            
  81224            
683 998641           map->nodes[index].key = key;
  82010            
  30063            
  50123            
  80413            
  81016            
  383257            
  150223            
  60312            
  81224            
684             }
685              
686             #ifdef HM_VALUE_IS_STR
687 221748           map->nodes[index].value = new_val;
  80414            
  81021            
  60313            
688 221748           map->nodes[index].val_len = new_val_len;
  80414            
  81021            
  60313            
689             #else
690 776918           map->nodes[index].value = value;
  82016            
  30065            
  50125            
  383263            
  150224            
  81225            
691             #endif
692              
693             /* LRU maintenance */
694 998666 100         if (HM_UNLIKELY(map->lru_prev)) {
  82016 100          
  30065 100          
  50125 100          
  80414 100          
  81021 100          
  383263 100          
  150224 100          
  60313 100          
  81225            
695 3714 50         if (found) HM_FN(lru_promote)(map, (uint32_t)index);
  209 50          
  8 50          
  8 50          
  203 50          
  203 100          
  2872 50          
  4 50          
  203 50          
  4            
696 3711           else HM_FN(lru_push_front)(map, (uint32_t)index);
  209            
  8            
  8            
  203            
  203            
  2869            
  4            
  203            
  4            
697             }
698             /* TTL maintenance */
699 998666 100         if (map->expires_at) {
  82016 100          
  30065 100          
  50125 50          
  80414 100          
  81021 100          
  383263 100          
  150224 50          
  60313 100          
  81225            
700 369 100         uint32_t ttl = entry_ttl > 0 ? entry_ttl : map->default_ttl;
  203 50          
  1 50          
  1 0          
  0 50          
  1 100          
  161 50          
  1 0          
  0 50          
  1            
701 369 50         if (ttl > 0)
  203 50          
  1 50          
  1 0          
  0 50          
  1 100          
  161 50          
  1 0          
  0 50          
  1            
702 366           map->expires_at[index] = (uint32_t)time(NULL) + ttl;
  203            
  1            
  1            
  0            
  1            
  158            
  1            
  0            
  1            
703             else
704 3           map->expires_at[index] = 0;
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
  0            
705             }
706              
707 998666           return true;
  82016            
  30065            
  50125            
  80414            
  81021            
  383263            
  150224            
  60313            
  81225            
708             }
709              
710             #else /* string keys */
711              
712 404565           static bool HM_FN(put)(HM_MAP_TYPE* map,
  81580            
  80220            
  82419            
  80226            
  80120            
713             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
714             #ifdef HM_VALUE_IS_STR
715             const char* value, uint32_t val_len, bool val_utf8,
716             #elif defined(HM_VALUE_IS_SV)
717             void* value,
718             #else
719             HM_INT_TYPE value,
720             #endif
721             uint32_t entry_ttl) {
722 404565 50         if (!map || !key) return false;
  81580 50          
  80220 50          
  82419 50          
  80226 50          
  80120 50          
    50          
    50          
    50          
    50          
723              
724 404565 100         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  81580 100          
  80220 100          
  82419 100          
  80226 100          
  80120            
725 465 100         if (map->max_size > 0 && map->tombstones > 0) {
  171 100          
  31 50          
  203 0          
  31 100          
  29 100          
    50          
    0          
    50          
    0          
726 204 50         if (!HM_FN(compact)(map)) return false;
  102 0          
  0 50          
  102 0          
  0 0          
  0            
727             } else {
728 261 50         if (!HM_FN(resize)(map)) return false;
  69 50          
  31 50          
  101 50          
  31 50          
  29            
729             }
730             }
731              
732             bool found;
733 404565           size_t index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, key_utf8, &found);
  81580            
  80220            
  82419            
  80226            
  80120            
734 404565 50         if (index >= map->capacity) return false;
  81580 50          
  80220 50          
  82419 50          
  80226 50          
  80120            
735              
736             /* LRU eviction: only on new insert at capacity */
737 404565 100         if (!found && map->max_size > 0 && map->size >= map->max_size) {
  81580 100          
  80220 100          
  82419 100          
  80226 100          
  80120 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
738 686           HM_FN(lru_evict_one)(map);
  342            
  1            
  341            
  1            
  1            
739             /* Re-probe after eviction to find optimal insertion slot */
740 686           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, key_utf8, &found);
  342            
  1            
  341            
  1            
  1            
741 686 50         if (index >= map->capacity) return false;
  342 50          
  1 50          
  341 50          
  1 50          
  1            
742             }
743              
744             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
745 404565 100         if (HM_UNLIKELY(entry_ttl > 0 && !map->expires_at)) {
  81580 50          
  80220 100          
  82419 50          
  80226 100          
  80120 100          
    50          
    0          
    100          
    50          
746 7           map->expires_at = (uint32_t*)calloc(map->capacity, sizeof(uint32_t));
  1            
  1            
  4            
  0            
  1            
747 7 50         if (!map->expires_at) return false;
  1 50          
  1 50          
  4 0          
  0 50          
  1            
748             }
749              
750             #ifdef HM_VALUE_IS_STR
751             /* Pre-allocate value before modifying map state */
752 82419           char* new_val = NULL;
  82419            
753 82419           uint32_t new_val_len = 0;
  82419            
754 82419 50         if (value && val_len > 0) {
  82419 100          
755 82418           new_val = (char*)malloc(val_len + 1);
  82418            
756 82418 50         if (!new_val) return false;
  82418            
757 82418           memcpy(new_val, value, val_len);
  82418            
758 82418           new_val[val_len] = '\0';
  82418            
759 82418 100         new_val_len = HM_PACK_LEN(val_len, val_utf8);
  82418            
760 1 50         } else if (value) {
  1            
761 1           new_val = (char*)malloc(1);
  1            
762 1 50         if (!new_val) return false;
  1            
763 1           new_val[0] = '\0';
  1            
764 1 50         new_val_len = HM_PACK_LEN(0, val_utf8);
  1            
765             }
766             #endif
767              
768 404565 100         if (found) {
  81580 100          
  80220 100          
  82419 100          
  80226 100          
  80120            
769             #ifdef HM_VALUE_IS_STR
770 6 50         if (map->nodes[index].value) free(map->nodes[index].value);
  6            
771             #elif defined(HM_VALUE_IS_SV)
772 6 50         if (map->nodes[index].value && map->free_value_fn)
  6 50          
773 6           map->free_value_fn(map->nodes[index].value);
  6            
774             #endif
775 15 50         map->nodes[index].key_len = HM_PACK_LEN(key_len, key_utf8);
  6 50          
  1 50          
  6 50          
  1 50          
  1            
776             } else {
777 404550           char* new_key = (char*)malloc(key_len + 1);
  81574            
  80219            
  82413            
  80225            
  80119            
778 404550 50         if (!new_key) {
  81574 50          
  80219 50          
  82413 50          
  80225 50          
  80119            
779             #ifdef HM_VALUE_IS_STR
780 0           free(new_val);
  0            
781             #endif
782 0           return false;
  0            
  0            
  0            
  0            
  0            
783             }
784 404550           memcpy(new_key, key, key_len);
  81574            
  80219            
  82413            
  80225            
  80119            
785 404550           new_key[key_len] = '\0';
  81574            
  80219            
  82413            
  80225            
  80119            
786 404550 100         if (HM_SLOT_IS_TOMBSTONE(&map->nodes[index])) {
  81574 100          
  80219 100          
  82413 100          
  80225 100          
  80119            
787 175           map->tombstones--;
  84            
  2            
  85            
  2            
  2            
788             }
789 404550           map->size++;
  81574            
  80219            
  82413            
  80225            
  80119            
790 404550           map->nodes[index].key = new_key;
  81574            
  80219            
  82413            
  80225            
  80119            
791 404550 100         map->nodes[index].key_len = HM_PACK_LEN(key_len, key_utf8);
  81574 100          
  80219 100          
  82413 100          
  80225 100          
  80119            
792 404550           map->nodes[index].key_hash = key_hash;
  81574            
  80219            
  82413            
  80225            
  80119            
793             }
794              
795             #ifdef HM_VALUE_IS_STR
796 82419           map->nodes[index].value = new_val;
  82419            
797 82419           map->nodes[index].val_len = new_val_len;
  82419            
798             #else
799 322146           map->nodes[index].value = value;
  81580            
  80220            
  80226            
  80120            
800             #endif
801              
802             /* LRU maintenance */
803 404565 100         if (HM_UNLIKELY(map->lru_prev)) {
  81580 100          
  80220 100          
  82419 100          
  80226 100          
  80120            
804 823 50         if (found) HM_FN(lru_promote)(map, (uint32_t)index);
  408 50          
  3 50          
  404 50          
  4 50          
  4            
805 823           else HM_FN(lru_push_front)(map, (uint32_t)index);
  408            
  3            
  404            
  4            
  4            
806             }
807             /* TTL maintenance */
808 404565 100         if (map->expires_at) {
  81580 100          
  80220 100          
  82419 100          
  80226 100          
  80120            
809 612 100         uint32_t ttl = entry_ttl > 0 ? entry_ttl : map->default_ttl;
  203 50          
  1 100          
  405 50          
  1 100          
  2            
810 612 50         if (ttl > 0)
  203 50          
  1 100          
  405 50          
  1 50          
  2            
811 610           map->expires_at[index] = (uint32_t)time(NULL) + ttl;
  203            
  1            
  403            
  1            
  2            
812             else
813 2           map->expires_at[index] = 0;
  0            
  0            
  2            
  0            
  0            
814             }
815              
816 404565           return true;
  81580            
  80220            
  82419            
  80226            
  80120            
817             }
818              
819             #endif /* HM_KEY_IS_INT */
820              
821             /* ---- get ---- */
822              
823             #ifdef HM_KEY_IS_INT
824              
825             #ifdef HM_VALUE_IS_STR
826 130038           static bool HM_FN(get)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  50011            
  50016            
  30011            
827             const char** out_value, uint32_t* out_len, bool* out_utf8) {
828 130038 50         if (!map || HM_IS_RESERVED_KEY(key)) return false;
  50011 100          
  50016 50          
  30011 50          
    100          
    50          
    50          
    100          
    50          
829              
830 130035           size_t index = HM_FN(find_node)(map, key);
  50010            
  50015            
  30010            
831 130035 50         if (index >= map->capacity || map->nodes[index].key == HM_EMPTY_KEY) return false;
  50010 100          
  50015 50          
  30010 100          
    50          
    100          
832              
833             /* TTL check */
834 130027 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  50008 0          
  50012 100          
  30007 50          
    50          
    0          
835 2 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 100          
  2 0          
  0            
836 1           HM_FN(expire_at)(map, index, false);
  0            
  1            
  0            
837 1           return false;
  0            
  1            
  0            
838             }
839              
840 130026 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  50008 100          
  50011 50          
  30007            
841              
842 130026           *out_value = map->nodes[index].value;
  50008            
  50011            
  30007            
843 130026           *out_len = HM_UNPACK_LEN(map->nodes[index].val_len);
  50008            
  50011            
  30007            
844 130026           *out_utf8 = HM_UNPACK_UTF8(map->nodes[index].val_len);
  50008            
  50011            
  30007            
845 130026           return true;
  50008            
  50011            
  30007            
846             }
847             #elif defined(HM_VALUE_IS_SV)
848 130055           static bool HM_FN(get)(HM_MAP_TYPE* map, HM_INT_TYPE key, void** out_value) {
  50023            
  30016            
  50016            
849 130055 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  50023 50          
  30016 100          
  50016 50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
850              
851 130054           size_t index = HM_FN(find_node)(map, key);
  50022            
  30016            
  50016            
852 130054 50         if (index >= map->capacity || map->nodes[index].key == HM_EMPTY_KEY) return false;
  50022 100          
  30016 50          
  50016 100          
    50          
    100          
853              
854             /* TTL check */
855 130044 100         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  50018 50          
  30013 100          
  50013 50          
    100          
    50          
856 4 50         (uint32_t)time(NULL) >= map->expires_at[index]) {
  2 50          
  1 50          
  1            
857 4           HM_FN(expire_at)(map, index, false);
  2            
  1            
  1            
858 4           return false;
  2            
  1            
  1            
859             }
860              
861 130040 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  50016 100          
  30012 100          
  50012            
862              
863 130040           *out_value = map->nodes[index].value;
  50016            
  30012            
  50012            
864 130040           return true;
  50016            
  30012            
  50012            
865             }
866             #else
867 180187           static bool HM_FN(get)(HM_MAP_TYPE* map, HM_INT_TYPE key, HM_INT_TYPE* out_value) {
  100164            
  50010            
  30013            
868 180187 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  100164 50          
  50010 100          
  30013 50          
    50          
    50          
    100          
    50          
    50          
    50          
    100          
    50          
869              
870 180184           size_t index = HM_FN(find_node)(map, key);
  100163            
  50009            
  30012            
871 180184 50         if (index >= map->capacity || map->nodes[index].key == HM_EMPTY_KEY) return false;
  100163 100          
  50009 50          
  30012 100          
    50          
    100          
872              
873             /* TTL check */
874 180164 100         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  100150 100          
  50006 100          
  30008 50          
    100          
    50          
875 26 100         (uint32_t)time(NULL) >= map->expires_at[index]) {
  22 100          
  2 100          
  2            
876 14           HM_FN(expire_at)(map, index, false);
  12            
  1            
  1            
877 14           return false;
  12            
  1            
  1            
878             }
879              
880 180150 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  100138 50          
  50005 50          
  30007            
881              
882 180150           *out_value = map->nodes[index].value;
  100138            
  50005            
  30007            
883 180150           return true;
  100138            
  50005            
  30007            
884             }
885             #endif
886              
887             #else /* string keys */
888              
889             #ifdef HM_VALUE_IS_STR
890 50029           static bool HM_FN(get)(HM_MAP_TYPE* map,
891             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
892             const char** out_value, uint32_t* out_len, bool* out_utf8) {
893 50029 50         if (!map || !key) return false;
    50          
894              
895 50029           size_t index = HM_FN(find_node)(map, key, key_len, key_hash, key_utf8);
896 50029 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
    100          
897              
898             /* TTL check */
899 50024 100         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
    100          
900 3 100         (uint32_t)time(NULL) >= map->expires_at[index]) {
901 2           HM_FN(expire_at)(map, index, false);
902 2           return false;
903             }
904              
905 50022 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
906              
907 50022           *out_value = map->nodes[index].value;
908 50022           *out_len = HM_UNPACK_LEN(map->nodes[index].val_len);
909 50022           *out_utf8 = HM_UNPACK_UTF8(map->nodes[index].val_len);
910 50022           return true;
911             }
912             #elif defined(HM_VALUE_IS_SV)
913 50024           static bool HM_FN(get)(HM_MAP_TYPE* map,
914             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
915             void** out_value) {
916 50024 50         if (!map || !key || !out_value) return false;
    50          
    50          
917              
918 50024           size_t index = HM_FN(find_node)(map, key, key_len, key_hash, key_utf8);
919 50024 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
    100          
920              
921             /* TTL check */
922 50021 100         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
    50          
923 2 50         (uint32_t)time(NULL) >= map->expires_at[index]) {
924 2           HM_FN(expire_at)(map, index, false);
925 2           return false;
926             }
927              
928 50019 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
929              
930 50019           *out_value = map->nodes[index].value;
931 50019           return true;
932             }
933             #else
934 150036           static bool HM_FN(get)(HM_MAP_TYPE* map,
  50010            
  50016            
  50010            
935             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
936             HM_INT_TYPE* out_value) {
937 150036 50         if (!map || !key || !out_value) return false;
  50010 50          
  50016 50          
  50010 50          
    50          
    50          
    50          
    50          
    50          
938              
939 150036           size_t index = HM_FN(find_node)(map, key, key_len, key_hash, key_utf8);
  50010            
  50016            
  50010            
940 150036 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
  50010 100          
  50016 50          
  50010 100          
    50          
    100          
941              
942             /* TTL check */
943 150030 100         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  50008 50          
  50014 100          
  50008 50          
    100          
    50          
944 6 100         (uint32_t)time(NULL) >= map->expires_at[index]) {
  2 50          
  1 100          
  3            
945 4           HM_FN(expire_at)(map, index, false);
  1            
  1            
  2            
946 4           return false;
  1            
  1            
  2            
947             }
948              
949 150026 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  50007 100          
  50013 100          
  50006            
950              
951 150026           *out_value = map->nodes[index].value;
  50007            
  50013            
  50006            
952 150026           return true;
  50007            
  50013            
  50006            
953             }
954             #endif
955              
956             #endif /* HM_KEY_IS_INT */
957              
958             /* ---- exists ---- */
959              
960             #ifdef HM_KEY_IS_INT
961              
962 37           static bool HM_FN(exists)(HM_MAP_TYPE* map, HM_INT_TYPE key) {
  4            
  3            
  3            
  3            
  3            
  10            
  3            
  4            
  4            
963 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          
964 31           size_t index = HM_FN(find_node)(map, key);
  4            
  3            
  3            
  2            
  2            
  9            
  2            
  3            
  3            
965 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          
966              
967             /* TTL check */
968 20 50         if (HM_UNLIKELY(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          
969 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            
970 0           HM_FN(expire_at)(map, index, false);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
971 0           return false;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
972             }
973 20           return true;
  3            
  2            
  2            
  1            
  1            
  6            
  1            
  2            
  2            
974             }
975              
976             #else
977              
978 15           static bool HM_FN(exists)(HM_MAP_TYPE* map,
  5            
  2            
  2            
  2            
  4            
979             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8) {
980 15 50         if (!map || !key) return false;
  5 50          
  2 50          
  2 50          
  2 50          
  4 50          
    50          
    50          
    50          
    50          
981 15           size_t index = HM_FN(find_node)(map, key, key_len, key_hash, key_utf8);
  5            
  2            
  2            
  2            
  4            
982 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          
983              
984             /* TTL check */
985 10 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  4 0          
  1 50          
  1 0          
  1 50          
  3 0          
    50          
    0          
    50          
    0          
986 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0 0          
  0 0          
  0            
987 0           HM_FN(expire_at)(map, index, false);
  0            
  0            
  0            
  0            
  0            
988 0           return false;
  0            
  0            
  0            
  0            
  0            
989             }
990 10           return true;
  4            
  1            
  1            
  1            
  3            
991             }
992              
993             #endif
994              
995             /* ---- remove ---- */
996              
997             #ifdef HM_KEY_IS_INT
998              
999 890666           static bool HM_FN(remove)(HM_MAP_TYPE* map, HM_INT_TYPE key) {
  80505            
  30005            
  50005            
  80006            
  80106            
  280022            
  150004            
  60006            
  80007            
1000 890666 50         if (!map || HM_IS_RESERVED_KEY(key)) return false;
  80505 50          
  30005 50          
  50005 50          
  80006 50          
  80106 50          
  280022 50          
  150004 50          
  60006 50          
  80007 50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    50          
    50          
    100          
    50          
    50          
    100          
    100          
    50          
    100          
    50          
1001              
1002 890657           size_t index = HM_FN(find_node)(map, key);
  80505            
  30005            
  50005            
  80004            
  80104            
  280021            
  150003            
  60004            
  80006            
1003 890657 50         if (index >= map->capacity || map->nodes[index].key != key) return false;
  80505 100          
  30005 50          
  50005 100          
  80004 50          
  80104 100          
  280021 50          
  150003 100          
  60004 50          
  80006 100          
    50          
    100          
    50          
    100          
    50          
    100          
    50          
    100          
1004              
1005             /* TTL check: treat expired entry as already gone */
1006 890648 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  80504 0          
  30004 50          
  50004 0          
  80003 50          
  80103 0          
  280020 50          
  150002 0          
  60003 50          
  80005 0          
    100          
    50          
    50          
    0          
    50          
    0          
    50          
    0          
1007 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            
1008 1           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
1009 1           return false;
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
1010             }
1011              
1012 890647 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_unlink)(map, (uint32_t)index);
  80504 50          
  30004 50          
  50004 50          
  80003 50          
  80103 100          
  280019 50          
  150002 50          
  60003 50          
  80005            
1013 890647           HM_FN(tombstone_at)(map, index);
  80504            
  30004            
  50004            
  80003            
  80103            
  280019            
  150002            
  60003            
  80005            
1014 890647 100         if (map->tombstones > map->capacity / 4 ||
  80504 50          
  30004 50          
  50004 100          
  80003 100          
  80103 100          
  280019 100          
  150002 100          
  60003 100          
  80005            
1015 890621 100         (map->size > 0 && map->tombstones > map->size)) {
  80501 100          
  30004 100          
  50004 100          
  80000 100          
  80100 100          
  280015 100          
  149997 100          
  60000 100          
  80000 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
1016 534           HM_FN(compact)(map);
  90            
  13            
  14            
  50            
  58            
  108            
  79            
  49            
  73            
1017             }
1018 890647           return true;
  80504            
  30004            
  50004            
  80003            
  80103            
  280019            
  150002            
  60003            
  80005            
1019             }
1020              
1021             #else /* string keys */
1022              
1023 401029           static bool HM_FN(remove)(HM_MAP_TYPE* map,
  80505            
  80005            
  80508            
  80006            
  80005            
1024             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8) {
1025 401029 50         if (!map || !key) return false;
  80505 50          
  80005 50          
  80508 50          
  80006 50          
  80005 50          
    50          
    50          
    50          
    50          
1026              
1027 401029           size_t index = HM_FN(find_node)(map, key, key_len, key_hash, key_utf8);
  80505            
  80005            
  80508            
  80006            
  80005            
1028 401029 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
  80505 100          
  80005 50          
  80508 100          
  80006 50          
  80005 100          
    50          
    100          
    50          
    100          
1029              
1030             /* TTL check: treat expired entry as already gone */
1031 401024 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  80504 0          
  80004 50          
  80507 0          
  80005 50          
  80004 0          
    50          
    0          
    50          
    0          
1032 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0 0          
  0 0          
  0            
1033 0           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
  0            
  0            
1034 0           return false;
  0            
  0            
  0            
  0            
  0            
1035             }
1036              
1037 401024 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_unlink)(map, (uint32_t)index);
  80504 50          
  80004 50          
  80507 50          
  80005 50          
  80004            
1038 401024           HM_FN(tombstone_at)(map, index);
  80504            
  80004            
  80507            
  80005            
  80004            
1039 401024 100         if (map->tombstones > map->capacity / 4 ||
  80504 100          
  80004 100          
  80507 100          
  80005 100          
  80004            
1040 401009 100         (map->size > 0 && map->tombstones > map->size)) {
  80501 100          
  80001 100          
  80504 100          
  80002 100          
  80001 100          
    100          
    100          
    100          
    100          
1041 330           HM_FN(compact)(map);
  90            
  50            
  90            
  50            
  50            
1042             }
1043 401024           return true;
  80504            
  80004            
  80507            
  80005            
  80004            
1044             }
1045              
1046             #endif
1047              
1048             /* ---- Counter operations (int values only) ---- */
1049              
1050             #ifdef HM_HAS_COUNTERS
1051              
1052             #ifdef HM_KEY_IS_INT
1053              
1054 21           static inline size_t HM_FN(find_or_allocate)(HM_MAP_TYPE* map, HM_INT_TYPE key) {
  10            
  5            
  6            
1055 21           size_t index = hm_hash_int64((int64_t)key) & map->mask;
  10            
  5            
  6            
1056 21           const size_t original_index = index;
  10            
  5            
  6            
1057 21           HM_NODE_TYPE* nodes = map->nodes;
  10            
  5            
  6            
1058 21           size_t first_tombstone = map->capacity;
  10            
  5            
  6            
1059              
1060             do {
1061 29           HM_INT_TYPE k = nodes[index].key;
  15            
  6            
  8            
1062 29 50         if (k == key) return index;
  15 50          
  6 50          
  8            
1063 29 100         if (k == HM_EMPTY_KEY) {
  15 100          
  6 100          
  8            
1064 21 100         size_t target = (first_tombstone < map->capacity) ? first_tombstone : index;
  10 100          
  5 100          
  6            
1065 21 100         if (nodes[target].key == HM_TOMBSTONE_KEY) map->tombstones--;
  10 100          
  5 100          
  6            
1066 21           nodes[target].key = key;
  10            
  5            
  6            
1067 21           nodes[target].value = 0;
  10            
  5            
  6            
1068 21           map->size++;
  10            
  5            
  6            
1069 21           return target;
  10            
  5            
  6            
1070             }
1071 8 100         if (k == HM_TOMBSTONE_KEY && first_tombstone >= map->capacity) {
  5 50          
  1 50          
  2 50          
    50          
    50          
1072 5           first_tombstone = index;
  2            
  1            
  2            
1073             }
1074 8           index = (index + 1) & map->mask;
  5            
  1            
  2            
1075 8 50         } while (index != original_index);
  5 50          
  1 50          
  2            
1076              
1077 0 0         if (first_tombstone < map->capacity) {
  0 0          
  0 0          
  0            
1078 0           map->tombstones--;
  0            
  0            
  0            
1079 0           nodes[first_tombstone].key = key;
  0            
  0            
  0            
1080 0           nodes[first_tombstone].value = 0;
  0            
  0            
  0            
1081 0           map->size++;
  0            
  0            
  0            
1082 0           return first_tombstone;
  0            
  0            
  0            
1083             }
1084 0           return map->capacity;
  0            
  0            
  0            
1085             }
1086              
1087 15035           static bool HM_FN(increment)(HM_MAP_TYPE* map, HM_INT_TYPE key, HM_INT_TYPE* out_value) {
  15014            
  9            
  12            
1088 15035 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  15014 50          
  9 100          
  12 50          
    50          
    50          
    100          
    50          
    50          
    50          
    100          
    50          
1089              
1090 15032           size_t index = HM_FN(find_node)(map, key);
  15013            
  8            
  11            
1091 15032 50         if (index < map->capacity && map->nodes[index].key == key) {
  15013 100          
  8 50          
  11 100          
    50          
    100          
1092             /* TTL check */
1093 15023 100         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  15009 50          
  6 50          
  8 0          
    50          
    0          
1094 3 100         (uint32_t)time(NULL) >= map->expires_at[index]) {
  3 0          
  0 0          
  0            
1095 1           HM_FN(expire_at)(map, index, true);
  1            
  0            
  0            
1096 1           goto new_key;
  1            
  0            
  0            
1097             }
1098 15022 100         if (map->nodes[index].value == HM_INT_MAX) return false;
  15008 100          
  6 100          
  8            
1099 15017           *out_value = ++map->nodes[index].value;
  15006            
  5            
  6            
1100 15017 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  15006 50          
  5 50          
  6            
1101 15017 100         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  15006 100          
  5 50          
  6 0          
    50          
    0          
1102 15017           return true;
  15006            
  5            
  6            
1103             }
1104              
1105 9           new_key:
  4            
  2            
  3            
1106 10 100         if (map->max_size > 0 && map->size >= map->max_size)
  5 50          
  2 50          
  3 0          
    50          
    0          
1107 1           HM_FN(lru_evict_one)(map);
  1            
  0            
  0            
1108              
1109 10 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  5 50          
  2 50          
  3            
1110 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1111 0 0         if (!HM_FN(compact)(map)) return false;
  0 0          
  0 0          
  0            
1112             } else {
1113 0 0         if (!HM_FN(resize)(map)) return false;
  0 0          
  0 0          
  0            
1114             }
1115             }
1116              
1117 10           index = HM_FN(find_or_allocate)(map, key);
  5            
  2            
  3            
1118 10 50         if (index >= map->capacity) return false;
  5 50          
  2 50          
  3            
1119 10           *out_value = ++map->nodes[index].value;
  5            
  2            
  3            
1120 10 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  5 50          
  2 50          
  3            
1121 10 100         if (HM_UNLIKELY(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          
1122 10           return true;
  5            
  2            
  3            
1123             }
1124              
1125 25           static bool HM_FN(increment_by)(HM_MAP_TYPE* map, HM_INT_TYPE key, HM_INT_TYPE delta, HM_INT_TYPE* out_value) {
  8            
  9            
  8            
1126 25 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  8 50          
  9 50          
  8 50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
1127              
1128 25           size_t index = HM_FN(find_node)(map, key);
  8            
  9            
  8            
1129 25 50         if (index < map->capacity && map->nodes[index].key == key) {
  8 100          
  9 50          
  8 100          
    50          
    100          
1130             /* TTL check */
1131 18 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  5 0          
  7 50          
  6 0          
    50          
    0          
1132 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0            
1133 0           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
1134 0           goto new_key;
  0            
  0            
  0            
1135             }
1136 18           HM_INT_TYPE val = map->nodes[index].value;
  5            
  7            
  6            
1137 18 100         if (delta > 0 && val > HM_INT_MAX - delta) return false;
  5 100          
  7 100          
  6 100          
    100          
    100          
1138 15 100         if (delta < 0) {
  4 100          
  6 100          
  5            
1139 6 50         if (delta == HM_INT_MIN) { if (val < 0) return false; }
  2 0          
  2 50          
  2 0          
    50          
    0          
1140 6 100         else { if (val < HM_INT_MIN - delta) return false; }
  2 100          
  2 100          
  2            
1141             }
1142 12           map->nodes[index].value += delta;
  3            
  5            
  4            
1143 12           *out_value = map->nodes[index].value;
  3            
  5            
  4            
1144 12 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  3 50          
  5 50          
  4            
1145 12 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  3 0          
  5 50          
  4 0          
    50          
    0          
1146 12           return true;
  3            
  5            
  4            
1147             }
1148              
1149 7           new_key:
  3            
  2            
  2            
1150 7 50         if (map->max_size > 0 && map->size >= map->max_size)
  3 0          
  2 50          
  2 0          
    50          
    0          
1151 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1152              
1153 7 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  3 50          
  2 50          
  2            
1154 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1155 0 0         if (!HM_FN(compact)(map)) return false;
  0 0          
  0 0          
  0            
1156             } else {
1157 0 0         if (!HM_FN(resize)(map)) return false;
  0 0          
  0 0          
  0            
1158             }
1159             }
1160              
1161 7           index = HM_FN(find_or_allocate)(map, key);
  3            
  2            
  2            
1162 7 50         if (index >= map->capacity) return false;
  3 50          
  2 50          
  2            
1163 7           map->nodes[index].value = delta;
  3            
  2            
  2            
1164 7           *out_value = delta;
  3            
  2            
  2            
1165 7 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  3 50          
  2 50          
  2            
1166 7 50         if (HM_UNLIKELY(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          
1167 7           return true;
  3            
  2            
  2            
1168             }
1169              
1170 5021           static bool HM_FN(decrement)(HM_MAP_TYPE* map, HM_INT_TYPE key, HM_INT_TYPE* out_value) {
  5006            
  9            
  6            
1171 5021 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  5006 50          
  9 100          
  6 50          
    50          
    50          
    100          
    50          
    50          
    50          
    100          
    50          
1172              
1173 5018           size_t index = HM_FN(find_node)(map, key);
  5005            
  8            
  5            
1174 5018 50         if (index < map->capacity && map->nodes[index].key == key) {
  5005 100          
  8 50          
  5 100          
    50          
    100          
1175             /* TTL check */
1176 5014 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  5003 0          
  7 50          
  4 0          
    50          
    0          
1177 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0            
1178 0           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
1179 0           goto new_key;
  0            
  0            
  0            
1180             }
1181 5014 100         if (map->nodes[index].value == HM_INT_MIN) return false;
  5003 100          
  7 100          
  4            
1182 5010           *out_value = --map->nodes[index].value;
  5002            
  5            
  3            
1183 5010 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  5002 50          
  5 50          
  3            
1184 5010 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  5002 0          
  5 50          
  3 0          
    50          
    0          
1185 5010           return true;
  5002            
  5            
  3            
1186             }
1187              
1188 4           new_key:
  2            
  1            
  1            
1189 4 50         if (map->max_size > 0 && map->size >= map->max_size)
  2 0          
  1 50          
  1 0          
    50          
    0          
1190 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1191              
1192 4 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  2 50          
  1 50          
  1            
1193 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1194 0 0         if (!HM_FN(compact)(map)) return false;
  0 0          
  0 0          
  0            
1195             } else {
1196 0 0         if (!HM_FN(resize)(map)) return false;
  0 0          
  0 0          
  0            
1197             }
1198             }
1199              
1200 4           index = HM_FN(find_or_allocate)(map, key);
  2            
  1            
  1            
1201 4 50         if (index >= map->capacity) return false;
  2 50          
  1 50          
  1            
1202 4           *out_value = --map->nodes[index].value;
  2            
  1            
  1            
1203 4 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  2 50          
  1 50          
  1            
1204 4 50         if (HM_UNLIKELY(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          
1205 4           return true;
  2            
  1            
  1            
1206             }
1207              
1208             #else /* string keys + counters */
1209              
1210 21           static inline size_t HM_FN(find_or_allocate)(HM_MAP_TYPE* map,
  5            
  10            
  6            
1211             const char* key, uint32_t key_len,
1212             uint32_t key_hash, bool key_utf8) {
1213 21           size_t index = (size_t)key_hash & map->mask;
  5            
  10            
  6            
1214 21           const size_t original_index = index;
  5            
  10            
  6            
1215 21           HM_NODE_TYPE* nodes = map->nodes;
  5            
  10            
  6            
1216 21           size_t first_tombstone = map->capacity;
  5            
  10            
  6            
1217              
1218             do {
1219 29 100         if (nodes[index].key == NULL) {
  7 100          
  14 100          
  8            
1220 21 100         size_t target = (first_tombstone < map->capacity) ? first_tombstone : index;
  5 100          
  10 100          
  6            
1221 21           char* new_key = (char*)malloc(key_len + 1);
  5            
  10            
  6            
1222 21 50         if (!new_key) return map->capacity;
  5 50          
  10 50          
  6            
1223 21           memcpy(new_key, key, key_len);
  5            
  10            
  6            
1224 21           new_key[key_len] = '\0';
  5            
  10            
  6            
1225 21 100         if (HM_SLOT_IS_TOMBSTONE(&nodes[target])) map->tombstones--;
  5 100          
  10 100          
  6            
1226 21           nodes[target].key = new_key;
  5            
  10            
  6            
1227 21 50         nodes[target].key_len = HM_PACK_LEN(key_len, key_utf8);
  5 50          
  10 50          
  6            
1228 21           nodes[target].key_hash = key_hash;
  5            
  10            
  6            
1229 21           nodes[target].value = 0;
  5            
  10            
  6            
1230 21           map->size++;
  5            
  10            
  6            
1231 21           return target;
  5            
  10            
  6            
1232             }
1233 8 100         if (nodes[index].key == HM_STR_TOMBSTONE) {
  2 100          
  4 50          
  2            
1234 6 50         if (first_tombstone >= map->capacity) first_tombstone = index;
  1 50          
  3 50          
  2            
1235 2 50         } else if (nodes[index].key_hash == key_hash &&
  1 50          
  1 0          
  0            
1236 0 0         nodes[index].key_len == HM_PACK_LEN(key_len, key_utf8) &&
  0 0          
  0 0          
  0 0          
    0          
    0          
1237 0 0         memcmp(nodes[index].key, key, key_len) == 0) {
  0 0          
  0 0          
  0            
1238 0           return index;
  0            
  0            
  0            
1239             }
1240 8           index = (index + 1) & map->mask;
  2            
  4            
  2            
1241 8 50         } while (index != original_index);
  2 50          
  4 50          
  2            
1242              
1243 0 0         if (first_tombstone < map->capacity) {
  0 0          
  0 0          
  0            
1244 0           char* new_key = (char*)malloc(key_len + 1);
  0            
  0            
  0            
1245 0 0         if (!new_key) return map->capacity;
  0 0          
  0 0          
  0            
1246 0           memcpy(new_key, key, key_len);
  0            
  0            
  0            
1247 0           new_key[key_len] = '\0';
  0            
  0            
  0            
1248 0           map->tombstones--;
  0            
  0            
  0            
1249 0           nodes[first_tombstone].key = new_key;
  0            
  0            
  0            
1250 0 0         nodes[first_tombstone].key_len = HM_PACK_LEN(key_len, key_utf8);
  0 0          
  0 0          
  0            
1251 0           nodes[first_tombstone].key_hash = key_hash;
  0            
  0            
  0            
1252 0           nodes[first_tombstone].value = 0;
  0            
  0            
  0            
1253 0           map->size++;
  0            
  0            
  0            
1254 0           return first_tombstone;
  0            
  0            
  0            
1255             }
1256 0           return map->capacity;
  0            
  0            
  0            
1257             }
1258              
1259 15037           static bool HM_FN(increment)(HM_MAP_TYPE* map,
  12            
  15015            
  10            
1260             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
1261             HM_INT_TYPE* out_value) {
1262 15037 50         if (!map || !key || !out_value) return false;
  12 50          
  15015 50          
  10 50          
    50          
    50          
    50          
    50          
    50          
1263              
1264 15037           size_t index = HM_FN(find_node)(map, key, key_len, key_hash, key_utf8);
  12            
  15015            
  10            
1265 15037 50         if (index < map->capacity && map->nodes[index].key != NULL &&
  12 100          
  15015 50          
  10 100          
    50          
    100          
1266 15025 50         map->nodes[index].key != HM_STR_TOMBSTONE) {
  10 50          
  15009 50          
  6            
1267             /* TTL check */
1268 15025 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  10 0          
  15009 50          
  6 0          
    50          
    0          
1269 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0            
1270 0           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
1271 0           goto new_key;
  0            
  0            
  0            
1272             }
1273 15025 100         if (map->nodes[index].value == HM_INT_MAX) return false;
  10 100          
  15009 100          
  6            
1274 15022           *out_value = ++map->nodes[index].value;
  9            
  15008            
  5            
1275 15022 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  9 100          
  15008 100          
  5            
1276 15022 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  9 0          
  15008 50          
  5 0          
    50          
    0          
1277 15022           return true;
  9            
  15008            
  5            
1278             }
1279              
1280 12           new_key:
  2            
  6            
  4            
1281 12 50         if (map->max_size > 0 && map->size >= map->max_size)
  2 0          
  6 50          
  4 0          
    50          
    0          
1282 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1283              
1284 12 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  2 50          
  6 50          
  4            
1285 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1286 0 0         if (!HM_FN(compact)(map)) return false;
  0 0          
  0 0          
  0            
1287             } else {
1288 0 0         if (!HM_FN(resize)(map)) return false;
  0 0          
  0 0          
  0            
1289             }
1290             }
1291              
1292 12           index = HM_FN(find_or_allocate)(map, key, key_len, key_hash, key_utf8);
  2            
  6            
  4            
1293 12 50         if (index >= map->capacity) return false;
  2 50          
  6 50          
  4            
1294 12           *out_value = ++map->nodes[index].value;
  2            
  6            
  4            
1295 12 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  2 50          
  6 50          
  4            
1296 12 50         if (HM_UNLIKELY(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          
1297 12           return true;
  2            
  6            
  4            
1298             }
1299              
1300 18           static bool HM_FN(increment_by)(HM_MAP_TYPE* map,
  6            
  6            
  6            
1301             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
1302             HM_INT_TYPE delta, HM_INT_TYPE* out_value) {
1303 18 50         if (!map || !key || !out_value) return false;
  6 50          
  6 50          
  6 50          
    50          
    50          
    50          
    50          
    50          
1304              
1305 18           size_t index = HM_FN(find_node)(map, key, key_len, key_hash, key_utf8);
  6            
  6            
  6            
1306 18 50         if (index < map->capacity && map->nodes[index].key != NULL &&
  6 100          
  6 50          
  6 100          
    50          
    100          
1307 13 50         map->nodes[index].key != HM_STR_TOMBSTONE) {
  4 50          
  4 50          
  5            
1308             /* TTL check */
1309 13 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  4 0          
  4 50          
  5 0          
    50          
    0          
1310 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0            
1311 0           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
1312 0           goto new_key;
  0            
  0            
  0            
1313             }
1314 13           HM_INT_TYPE val = map->nodes[index].value;
  4            
  4            
  5            
1315 13 100         if (delta > 0 && val > HM_INT_MAX - delta) return false;
  4 50          
  4 100          
  5 50          
    100          
    100          
1316 10 100         if (delta < 0) {
  3 100          
  3 100          
  4            
1317 6 50         if (delta == HM_INT_MIN) { if (val < 0) return false; }
  2 0          
  2 50          
  2 0          
    50          
    0          
1318 6 100         else { if (val < HM_INT_MIN - delta) return false; }
  2 100          
  2 100          
  2            
1319             }
1320 7           map->nodes[index].value += delta;
  2            
  2            
  3            
1321 7           *out_value = map->nodes[index].value;
  2            
  2            
  3            
1322 7 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  2 50          
  2 50          
  3            
1323 7 50         if (HM_UNLIKELY(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          
1324 7           return true;
  2            
  2            
  3            
1325             }
1326              
1327 5           new_key:
  2            
  2            
  1            
1328 5 50         if (map->max_size > 0 && map->size >= map->max_size)
  2 0          
  2 50          
  1 0          
    50          
    0          
1329 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1330              
1331 5 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  2 50          
  2 50          
  1            
1332 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1333 0 0         if (!HM_FN(compact)(map)) return false;
  0 0          
  0 0          
  0            
1334             } else {
1335 0 0         if (!HM_FN(resize)(map)) return false;
  0 0          
  0 0          
  0            
1336             }
1337             }
1338              
1339 5           index = HM_FN(find_or_allocate)(map, key, key_len, key_hash, key_utf8);
  2            
  2            
  1            
1340 5 50         if (index >= map->capacity) return false;
  2 50          
  2 50          
  1            
1341 5           map->nodes[index].value = delta;
  2            
  2            
  1            
1342 5           *out_value = delta;
  2            
  2            
  1            
1343 5 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  2 50          
  2 50          
  1            
1344 5 50         if (HM_UNLIKELY(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          
1345 5           return true;
  2            
  2            
  1            
1346             }
1347              
1348 5014           static bool HM_FN(decrement)(HM_MAP_TYPE* map,
  3            
  5006            
  5            
1349             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
1350             HM_INT_TYPE* out_value) {
1351 5014 50         if (!map || !key || !out_value) return false;
  3 50          
  5006 50          
  5 50          
    50          
    50          
    50          
    50          
    50          
1352              
1353 5014           size_t index = HM_FN(find_node)(map, key, key_len, key_hash, key_utf8);
  3            
  5006            
  5            
1354 5014 50         if (index < map->capacity && map->nodes[index].key != NULL &&
  3 100          
  5006 50          
  5 100          
    50          
    100          
1355 5010 50         map->nodes[index].key != HM_STR_TOMBSTONE) {
  2 50          
  5004 50          
  4            
1356             /* TTL check */
1357 5010 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  2 0          
  5004 50          
  4 0          
    50          
    0          
1358 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0            
1359 0           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
1360 0           goto new_key;
  0            
  0            
  0            
1361             }
1362 5010 100         if (map->nodes[index].value == HM_INT_MIN) return false;
  2 100          
  5004 100          
  4            
1363 5006           *out_value = --map->nodes[index].value;
  1            
  5002            
  3            
1364 5006 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  1 50          
  5002 50          
  3            
1365 5006 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  1 0          
  5002 50          
  3 0          
    50          
    0          
1366 5006           return true;
  1            
  5002            
  3            
1367             }
1368              
1369 4           new_key:
  1            
  2            
  1            
1370 4 50         if (map->max_size > 0 && map->size >= map->max_size)
  1 0          
  2 50          
  1 0          
    50          
    0          
1371 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1372              
1373 4 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  1 50          
  2 50          
  1            
1374 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1375 0 0         if (!HM_FN(compact)(map)) return false;
  0 0          
  0 0          
  0            
1376             } else {
1377 0 0         if (!HM_FN(resize)(map)) return false;
  0 0          
  0 0          
  0            
1378             }
1379             }
1380              
1381 4           index = HM_FN(find_or_allocate)(map, key, key_len, key_hash, key_utf8);
  1            
  2            
  1            
1382 4 50         if (index >= map->capacity) return false;
  1 50          
  2 50          
  1            
1383 4           *out_value = --map->nodes[index].value;
  1            
  2            
  1            
1384 4 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  1 50          
  2 50          
  1            
1385 4 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = (uint32_t)time(NULL) + map->default_ttl;
  1 0          
  2 50          
  1 0          
    50          
    0          
1386 4           return true;
  1            
  2            
  1            
1387             }
1388              
1389             #endif /* HM_KEY_IS_INT for counters */
1390              
1391             #endif /* HM_HAS_COUNTERS */
1392              
1393             /* ---- get_or_set: single-probe get with insert on miss ---- */
1394              
1395             #ifdef HM_KEY_IS_INT
1396              
1397             #ifdef HM_VALUE_IS_STR
1398             /* get_or_set for int-key, string-value: returns index, sets *was_found */
1399 10           static size_t HM_FN(get_or_set)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  0            
  9            
  1            
1400             const char* def_val, uint32_t def_len, bool def_utf8,
1401             uint32_t entry_ttl, bool* was_found) {
1402 10           *was_found = false;
  0            
  9            
  1            
1403 10 0         if (!map || HM_IS_RESERVED_KEY(key)) return map ? map->capacity : 0;
  0 0          
  9 0          
  1 0          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
1404              
1405 10 0         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  0 50          
  9 50          
  1            
1406 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1407 0 0         if (!HM_FN(compact)(map)) return map->capacity;
  0 0          
  0 0          
  0            
1408             } else {
1409 0 0         if (!HM_FN(resize)(map)) return map->capacity;
  0 0          
  0 0          
  0            
1410             }
1411             }
1412              
1413             bool found;
1414 10           size_t index = HM_FN(find_slot_for_insert)(map, key, &found);
  0            
  9            
  1            
1415 10 0         if (index >= map->capacity) return map->capacity;
  0 50          
  9 50          
  1            
1416              
1417 10 0         if (found) {
  0 100          
  9 50          
  1            
1418             /* TTL check */
1419 3 0         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  0 0          
  3 50          
  0 0          
    0          
    0          
1420 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
  0 0          
  0 0          
  0            
1421 0           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
1422 0           found = false;
  0            
  0            
  0            
1423             /* Re-probe after expiry */
1424 0           index = HM_FN(find_slot_for_insert)(map, key, &found);
  0            
  0            
  0            
1425 0 0         if (index >= map->capacity) return map->capacity;
  0 0          
  0 0          
  0            
1426             }
1427             }
1428              
1429 10 0         if (found) {
  0 100          
  9 50          
  1            
1430 3           *was_found = true;
  0            
  3            
  0            
1431 3 0         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  0 50          
  3 0          
  0            
1432 3           return index;
  0            
  3            
  0            
1433             }
1434              
1435             /* LRU eviction */
1436 7 0         if (map->max_size > 0 && map->size >= map->max_size) {
  0 0          
  6 50          
  1 0          
    50          
    0          
1437 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1438 0           index = HM_FN(find_slot_for_insert)(map, key, &found);
  0            
  0            
  0            
1439 0 0         if (index >= map->capacity) return map->capacity;
  0 0          
  0 0          
  0            
1440             }
1441              
1442             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
1443 7 0         if (HM_UNLIKELY(entry_ttl > 0 && !map->expires_at)) {
  0 0          
  6 50          
  1 0          
    50          
    0          
1444 0           map->expires_at = (uint32_t*)calloc(map->capacity, sizeof(uint32_t));
  0            
  0            
  0            
1445 0 0         if (!map->expires_at) return map->capacity;
  0 0          
  0 0          
  0            
1446             }
1447              
1448             /* Pre-allocate value */
1449 7           char* new_val = NULL;
  0            
  6            
  1            
1450 7           uint32_t new_val_len = 0;
  0            
  6            
  1            
1451 7 0         if (def_val && def_len > 0) {
  0 0          
  6 50          
  1 50          
    50          
    50          
1452 7           new_val = (char*)malloc(def_len + 1);
  0            
  6            
  1            
1453 7 0         if (!new_val) return map->capacity;
  0 50          
  6 50          
  1            
1454 7           memcpy(new_val, def_val, def_len);
  0            
  6            
  1            
1455 7           new_val[def_len] = '\0';
  0            
  6            
  1            
1456 7 0         new_val_len = HM_PACK_LEN(def_len, def_utf8);
  0 50          
  6 50          
  1            
1457 0 0         } else if (def_val) {
  0 0          
  0 0          
  0            
1458 0           new_val = (char*)malloc(1);
  0            
  0            
  0            
1459 0 0         if (!new_val) return map->capacity;
  0 0          
  0 0          
  0            
1460 0           new_val[0] = '\0';
  0            
  0            
  0            
1461 0 0         new_val_len = HM_PACK_LEN(0, def_utf8);
  0 0          
  0 0          
  0            
1462             }
1463              
1464 7 0         if (map->nodes[index].key == HM_TOMBSTONE_KEY) map->tombstones--;
  0 50          
  6 50          
  1            
1465 7           map->size++;
  0            
  6            
  1            
1466 7           map->nodes[index].key = key;
  0            
  6            
  1            
1467 7           map->nodes[index].value = new_val;
  0            
  6            
  1            
1468 7           map->nodes[index].val_len = new_val_len;
  0            
  6            
  1            
1469              
1470 7 0         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  0 50          
  6 50          
  1            
1471 7 0         if (map->expires_at) {
  0 50          
  6 50          
  1            
1472 0 0         uint32_t ttl = entry_ttl > 0 ? entry_ttl : map->default_ttl;
  0 0          
  0 0          
  0            
1473 0 0         if (ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + ttl;
  0 0          
  0 0          
  0            
1474             }
1475              
1476 7           return index;
  0            
  6            
  1            
1477             }
1478             #elif defined(HM_VALUE_IS_SV)
1479 8           static size_t HM_FN(get_or_set)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  4            
  2            
  2            
1480             void* def_val, uint32_t entry_ttl, bool* was_found) {
1481 8           *was_found = false;
  4            
  2            
  2            
1482 8 50         if (!map || HM_IS_RESERVED_KEY(key)) return map ? map->capacity : 0;
  4 50          
  2 50          
  2 0          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
1483              
1484 8 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  4 50          
  2 50          
  2            
1485 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1486 0 0         if (!HM_FN(compact)(map)) return map->capacity;
  0 0          
  0 0          
  0            
1487             } else {
1488 0 0         if (!HM_FN(resize)(map)) return map->capacity;
  0 0          
  0 0          
  0            
1489             }
1490             }
1491              
1492             bool found;
1493 8           size_t index = HM_FN(find_slot_for_insert)(map, key, &found);
  4            
  2            
  2            
1494 8 50         if (index >= map->capacity) return map->capacity;
  4 50          
  2 50          
  2            
1495              
1496 8 100         if (found) {
  4 100          
  2 100          
  2            
1497 4 100         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  2 50          
  1 50          
  1 50          
    50          
    50          
1498 3 50         (uint32_t)time(NULL) >= map->expires_at[index]) {
  1 50          
  1 50          
  1            
1499 3           HM_FN(expire_at)(map, index, true);
  1            
  1            
  1            
1500 3           found = false;
  1            
  1            
  1            
1501 3           index = HM_FN(find_slot_for_insert)(map, key, &found);
  1            
  1            
  1            
1502 3 50         if (index >= map->capacity) return map->capacity;
  1 50          
  1 50          
  1            
1503             }
1504             }
1505              
1506 8 100         if (found) {
  4 50          
  2 50          
  2            
1507 1           *was_found = true;
  1            
  0            
  0            
1508 1 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  1 0          
  0 0          
  0            
1509 1           return index;
  1            
  0            
  0            
1510             }
1511              
1512 7 50         if (map->max_size > 0 && map->size >= map->max_size) {
  3 0          
  2 50          
  2 0          
    50          
    0          
1513 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1514 0           index = HM_FN(find_slot_for_insert)(map, key, &found);
  0            
  0            
  0            
1515 0 0         if (index >= map->capacity) return map->capacity;
  0 0          
  0 0          
  0            
1516             }
1517              
1518             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
1519 7 50         if (HM_UNLIKELY(entry_ttl > 0 && !map->expires_at)) {
  3 0          
  2 50          
  2 0          
    50          
    0          
1520 0           map->expires_at = (uint32_t*)calloc(map->capacity, sizeof(uint32_t));
  0            
  0            
  0            
1521 0 0         if (!map->expires_at) return map->capacity;
  0 0          
  0 0          
  0            
1522             }
1523              
1524 7 100         if (map->nodes[index].key == HM_TOMBSTONE_KEY) map->tombstones--;
  3 100          
  2 100          
  2            
1525 7           map->size++;
  3            
  2            
  2            
1526 7           map->nodes[index].key = key;
  3            
  2            
  2            
1527 7           map->nodes[index].value = def_val;
  3            
  2            
  2            
1528              
1529 7 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  3 50          
  2 50          
  2            
1530 7 100         if (map->expires_at) {
  3 50          
  2 50          
  2            
1531 6 50         uint32_t ttl = entry_ttl > 0 ? entry_ttl : map->default_ttl;
  2 50          
  2 50          
  2            
1532 6 50         if (ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + ttl;
  2 50          
  2 50          
  2            
1533             }
1534              
1535 7           return index;
  3            
  2            
  2            
1536             }
1537             #else
1538 21           static size_t HM_FN(get_or_set)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  15            
  3            
  3            
1539             HM_INT_TYPE def_val, uint32_t entry_ttl, bool* was_found) {
1540 21           *was_found = false;
  15            
  3            
  3            
1541 21 50         if (!map || HM_IS_RESERVED_KEY(key)) return map ? map->capacity : 0;
  15 50          
  3 50          
  3 0          
    50          
    100          
    50          
    50          
    50          
    100          
    50          
    50          
1542              
1543 19 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
  15 50          
  2 50          
  2            
1544 0 0         if (map->max_size > 0 && map->tombstones > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1545 0 0         if (!HM_FN(compact)(map)) return map->capacity;
  0 0          
  0 0          
  0            
1546             } else {
1547 0 0         if (!HM_FN(resize)(map)) return map->capacity;
  0 0          
  0 0          
  0            
1548             }
1549             }
1550              
1551             bool found;
1552 19           size_t index = HM_FN(find_slot_for_insert)(map, key, &found);
  15            
  2            
  2            
1553 19 50         if (index >= map->capacity) return map->capacity;
  15 50          
  2 50          
  2            
1554              
1555 19 100         if (found) {
  15 100          
  2 100          
  2            
1556 7 100         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  5 50          
  1 50          
  1 0          
    50          
    0          
1557 1 50         (uint32_t)time(NULL) >= map->expires_at[index]) {
  1 0          
  0 0          
  0            
1558 1           HM_FN(expire_at)(map, index, true);
  1            
  0            
  0            
1559 1           found = false;
  1            
  0            
  0            
1560 1           index = HM_FN(find_slot_for_insert)(map, key, &found);
  1            
  0            
  0            
1561 1 50         if (index >= map->capacity) return map->capacity;
  1 0          
  0 0          
  0            
1562             }
1563             }
1564              
1565 19 100         if (found) {
  15 100          
  2 100          
  2            
1566 6           *was_found = true;
  4            
  1            
  1            
1567 6 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  4 50          
  1 50          
  1            
1568 6           return index;
  4            
  1            
  1            
1569             }
1570              
1571 13 100         if (map->max_size > 0 && map->size >= map->max_size) {
  11 100          
  1 50          
  1 0          
    50          
    0          
1572 1           HM_FN(lru_evict_one)(map);
  1            
  0            
  0            
1573 1           index = HM_FN(find_slot_for_insert)(map, key, &found);
  1            
  0            
  0            
1574 1 50         if (index >= map->capacity) return map->capacity;
  1 0          
  0 0          
  0            
1575             }
1576              
1577             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
1578 13 50         if (HM_UNLIKELY(entry_ttl > 0 && !map->expires_at)) {
  11 0          
  1 50          
  1 0          
    50          
    0          
1579 0           map->expires_at = (uint32_t*)calloc(map->capacity, sizeof(uint32_t));
  0            
  0            
  0            
1580 0 0         if (!map->expires_at) return map->capacity;
  0 0          
  0 0          
  0            
1581             }
1582              
1583 13 100         if (map->nodes[index].key == HM_TOMBSTONE_KEY) map->tombstones--;
  11 50          
  1 50          
  1            
1584 13           map->size++;
  11            
  1            
  1            
1585 13           map->nodes[index].key = key;
  11            
  1            
  1            
1586 13           map->nodes[index].value = def_val;
  11            
  1            
  1            
1587              
1588 13 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  11 50          
  1 50          
  1            
1589 13 100         if (map->expires_at) {
  11 50          
  1 50          
  1            
1590 2 50         uint32_t ttl = entry_ttl > 0 ? entry_ttl : map->default_ttl;
  2 0          
  0 0          
  0            
1591 2 100         if (ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + ttl;
  2 0          
  0 0          
  0            
1592             }
1593              
1594 13           return index;
  11            
  1            
  1            
1595             }
1596             #endif
1597              
1598             #else /* string keys */
1599              
1600             #ifdef HM_VALUE_IS_STR
1601 9           static size_t HM_FN(get_or_set)(HM_MAP_TYPE* map,
1602             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
1603             const char* def_val, uint32_t def_len, bool def_utf8,
1604             uint32_t entry_ttl, bool* was_found) {
1605 9           *was_found = false;
1606 9 50         if (!map || !key) return map ? map->capacity : 0;
    50          
    0          
1607              
1608 9 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
1609 0 0         if (map->max_size > 0 && map->tombstones > 0) {
    0          
1610 0 0         if (!HM_FN(compact)(map)) return map->capacity;
1611             } else {
1612 0 0         if (!HM_FN(resize)(map)) return map->capacity;
1613             }
1614             }
1615              
1616             bool found;
1617 9           size_t index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, key_utf8, &found);
1618 9 50         if (index >= map->capacity) return map->capacity;
1619              
1620 9 100         if (found) {
1621 3 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
    0          
1622 0 0         (uint32_t)time(NULL) >= map->expires_at[index]) {
1623 0           HM_FN(expire_at)(map, index, true);
1624 0           found = false;
1625 0           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, key_utf8, &found);
1626 0 0         if (index >= map->capacity) return map->capacity;
1627             }
1628             }
1629              
1630 9 100         if (found) {
1631 3           *was_found = true;
1632 3 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
1633 3           return index;
1634             }
1635              
1636 6 50         if (map->max_size > 0 && map->size >= map->max_size) {
    0          
1637 0           HM_FN(lru_evict_one)(map);
1638 0           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, key_utf8, &found);
1639 0 0         if (index >= map->capacity) return map->capacity;
1640             }
1641              
1642             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
1643 6 50         if (HM_UNLIKELY(entry_ttl > 0 && !map->expires_at)) {
    0          
1644 0           map->expires_at = (uint32_t*)calloc(map->capacity, sizeof(uint32_t));
1645 0 0         if (!map->expires_at) return map->capacity;
1646             }
1647              
1648             /* Allocate key + value */
1649 6           char* new_key = (char*)malloc(key_len + 1);
1650 6 50         if (!new_key) return map->capacity;
1651 6           memcpy(new_key, key, key_len);
1652 6           new_key[key_len] = '\0';
1653              
1654 6           char* new_val = NULL;
1655 6           uint32_t new_val_len = 0;
1656 6 50         if (def_val && def_len > 0) {
    50          
1657 6           new_val = (char*)malloc(def_len + 1);
1658 6 50         if (!new_val) { free(new_key); return map->capacity; }
1659 6           memcpy(new_val, def_val, def_len);
1660 6           new_val[def_len] = '\0';
1661 6 50         new_val_len = HM_PACK_LEN(def_len, def_utf8);
1662 0 0         } else if (def_val) {
1663 0           new_val = (char*)malloc(1);
1664 0 0         if (!new_val) { free(new_key); return map->capacity; }
1665 0           new_val[0] = '\0';
1666 0 0         new_val_len = HM_PACK_LEN(0, def_utf8);
1667             }
1668              
1669 6 50         if (HM_SLOT_IS_TOMBSTONE(&map->nodes[index])) map->tombstones--;
1670 6           map->size++;
1671 6           map->nodes[index].key = new_key;
1672 6 50         map->nodes[index].key_len = HM_PACK_LEN(key_len, key_utf8);
1673 6           map->nodes[index].key_hash = key_hash;
1674 6           map->nodes[index].value = new_val;
1675 6           map->nodes[index].val_len = new_val_len;
1676              
1677 6 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
1678 6 50         if (map->expires_at) {
1679 0 0         uint32_t ttl = entry_ttl > 0 ? entry_ttl : map->default_ttl;
1680 0 0         if (ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + ttl;
1681             }
1682              
1683 6           return index;
1684             }
1685             #elif defined(HM_VALUE_IS_SV)
1686 4           static size_t HM_FN(get_or_set)(HM_MAP_TYPE* map,
1687             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
1688             void* def_val, uint32_t entry_ttl, bool* was_found) {
1689 4           *was_found = false;
1690 4 50         if (!map || !key) return map ? map->capacity : 0;
    50          
    0          
1691              
1692 4 50         if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
1693 0 0         if (map->max_size > 0 && map->tombstones > 0) {
    0          
1694 0 0         if (!HM_FN(compact)(map)) return map->capacity;
1695             } else {
1696 0 0         if (!HM_FN(resize)(map)) return map->capacity;
1697             }
1698             }
1699              
1700             bool found;
1701 4           size_t index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, key_utf8, &found);
1702 4 50         if (index >= map->capacity) return map->capacity;
1703              
1704 4 100         if (found) {
1705 2 100         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
    50          
1706 1 50         (uint32_t)time(NULL) >= map->expires_at[index]) {
1707 1           HM_FN(expire_at)(map, index, true);
1708 1           found = false;
1709 1           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, key_utf8, &found);
1710 1 50         if (index >= map->capacity) return map->capacity;
1711             }
1712             }
1713              
1714 4 100         if (found) {
1715 1           *was_found = true;
1716 1 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
1717 1           return index;
1718             }
1719              
1720 3 50         if (map->max_size > 0 && map->size >= map->max_size) {
    0          
1721 0           HM_FN(lru_evict_one)(map);
1722 0           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, key_utf8, &found);
1723 0 0         if (index >= map->capacity) return map->capacity;
1724             }
1725              
1726             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
1727 3 50         if (HM_UNLIKELY(entry_ttl > 0 && !map->expires_at)) {
    0          
1728 0           map->expires_at = (uint32_t*)calloc(map->capacity, sizeof(uint32_t));
1729 0 0         if (!map->expires_at) return map->capacity;
1730             }
1731              
1732 3           char* new_key = (char*)malloc(key_len + 1);
1733 3 50         if (!new_key) return map->capacity;
1734 3           memcpy(new_key, key, key_len);
1735 3           new_key[key_len] = '\0';
1736              
1737 3 100         if (HM_SLOT_IS_TOMBSTONE(&map->nodes[index])) map->tombstones--;
1738 3           map->size++;
1739 3           map->nodes[index].key = new_key;
1740 3 50         map->nodes[index].key_len = HM_PACK_LEN(key_len, key_utf8);
1741 3           map->nodes[index].key_hash = key_hash;
1742 3           map->nodes[index].value = def_val;
1743              
1744 3 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
1745 3 100         if (map->expires_at) {
1746 2 50         uint32_t ttl = entry_ttl > 0 ? entry_ttl : map->default_ttl;
1747 2 50         if (ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + ttl;
1748             }
1749              
1750 3           return index;
1751             }
1752             #else
1753 9           static size_t HM_FN(get_or_set)(HM_MAP_TYPE* map,
1754             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
1755             HM_INT_TYPE def_val, uint32_t entry_ttl, bool* was_found) {
1756 9           *was_found = false;
1757 9           if (!map || !key) return map ? map->capacity : 0;
1758              
1759 9           if ((map->size + map->tombstones) * 4 >= map->capacity * 3) {
1760 0           if (map->max_size > 0 && map->tombstones > 0) {
1761 0           if (!HM_FN(compact)(map)) return map->capacity;
1762             } else {
1763 0           if (!HM_FN(resize)(map)) return map->capacity;
1764             }
1765             }
1766              
1767             bool found;
1768 9           size_t index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, key_utf8, &found);
1769 9           if (index >= map->capacity) return map->capacity;
1770              
1771 9           if (found) {
1772 3           if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
1773 0           (uint32_t)time(NULL) >= map->expires_at[index]) {
1774 0           HM_FN(expire_at)(map, index, true);
1775 0           found = false;
1776 0           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, key_utf8, &found);
1777 0           if (index >= map->capacity) return map->capacity;
1778             }
1779             }
1780              
1781 9           if (found) {
1782 3           *was_found = true;
1783 3           if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
1784 3           return index;
1785             }
1786              
1787 6           if (map->max_size > 0 && map->size >= map->max_size) {
1788 0           HM_FN(lru_evict_one)(map);
1789 0           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, key_utf8, &found);
1790 0           if (index >= map->capacity) return map->capacity;
1791             }
1792              
1793             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
1794 6           if (HM_UNLIKELY(entry_ttl > 0 && !map->expires_at)) {
1795 0           map->expires_at = (uint32_t*)calloc(map->capacity, sizeof(uint32_t));
1796 0           if (!map->expires_at) return map->capacity;
1797             }
1798              
1799 6           char* new_key = (char*)malloc(key_len + 1);
1800 6           if (!new_key) return map->capacity;
1801 6           memcpy(new_key, key, key_len);
1802 6           new_key[key_len] = '\0';
1803              
1804 6           if (HM_SLOT_IS_TOMBSTONE(&map->nodes[index])) map->tombstones--;
1805 6           map->size++;
1806 6           map->nodes[index].key = new_key;
1807 6           map->nodes[index].key_len = HM_PACK_LEN(key_len, key_utf8);
1808 6           map->nodes[index].key_hash = key_hash;
1809 6           map->nodes[index].value = def_val;
1810              
1811 6           if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
1812 6           if (map->expires_at) {
1813 0           uint32_t ttl = entry_ttl > 0 ? entry_ttl : map->default_ttl;
1814 0           if (ttl > 0) map->expires_at[index] = (uint32_t)time(NULL) + ttl;
1815             }
1816              
1817 6           return index;
1818             }
1819             #endif
1820              
1821             #endif /* HM_KEY_IS_INT for get_or_set */
1822              
1823             /* ---- Cleanup macros for next inclusion ---- */
1824              
1825             #undef HM_SLOT_IS_EMPTY
1826             #undef HM_SLOT_IS_TOMBSTONE
1827             #undef HM_SLOT_IS_LIVE
1828             #ifdef HM_KEY_IS_INT
1829             #undef HM_EMPTY_KEY
1830             #undef HM_TOMBSTONE_KEY
1831             #undef HM_IS_RESERVED_KEY
1832             #endif
1833              
1834             #undef HM_PREFIX
1835             #undef HM_NODE_TYPE
1836             #undef HM_MAP_TYPE
1837             #undef HM_FN
1838             #undef HM_PASTE
1839             #undef HM_PASTE2
1840              
1841             #ifdef HM_KEY_IS_INT
1842             #undef HM_KEY_IS_INT
1843             #endif
1844             #ifdef HM_VALUE_IS_STR
1845             #undef HM_VALUE_IS_STR
1846             #endif
1847             #ifdef HM_VALUE_IS_SV
1848             #undef HM_VALUE_IS_SV
1849             #endif
1850             #ifdef HM_HAS_COUNTERS
1851             #undef HM_HAS_COUNTERS
1852             #endif
1853              
1854             #undef HM_INT_TYPE
1855             #undef HM_INT_MIN
1856             #undef HM_INT_MAX