File Coverage

hashmap_generic.h
Criterion Covered Total %
statement 5188 7143 72.6
branch 3044 6774 44.9
condition n/a
subroutine n/a
pod n/a
total 8232 13917 59.1


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             /* ---- TTL timestamp helper ---- */
60              
61             /* Compute expiry timestamp, clamping to 1 if the addition wraps to 0.
62             * expires_at==0 is the sentinel for "no expiry", so we must avoid it. */
63             #ifndef HM_EXPIRY_AT_DEFINED
64             #define HM_EXPIRY_AT_DEFINED
65 1042           static inline uint32_t hm_expiry_at(uint32_t ttl) {
66 1042           uint32_t e = (uint32_t)time(NULL) + ttl;
67 1042 50         return e ? e : 1;
68             }
69             #endif
70              
71             /* Shared TTL check at slot `idx`: if entry has an expires_at set and it is
72             * in the past, call the variant's expire_at() and return false from the
73             * enclosing function. `may_compact` tells expire_at whether to trigger
74             * compaction (false for read paths, true for write paths so compaction
75             * can happen while the lock is held). Defined once; HM_FN() in the body
76             * is expanded at each call site to the current variant's symbol.
77             */
78             #ifndef HM_TTL_CHECK_EXPIRED_DEFINED
79             #define HM_TTL_CHECK_EXPIRED_DEFINED
80             #define HM_TTL_CHECK_EXPIRED(map, idx, may_compact) do { \
81             if (HM_UNLIKELY((map)->expires_at && (map)->expires_at[idx]) && \
82             (uint32_t)time(NULL) > (map)->expires_at[idx]) { \
83             HM_FN(expire_at)((map), (idx), (may_compact)); \
84             return false; \
85             } \
86             } while (0)
87             #endif
88              
89             /* Compaction policy after a tombstone is created: compact when dead slots
90             * exceed 25% capacity or outnumber live entries. Centralized so future
91             * threshold tuning touches one macro. */
92             #ifndef HM_MAYBE_COMPACT_DEFINED
93             #define HM_MAYBE_COMPACT_DEFINED
94             #define HM_MAYBE_COMPACT(map) do { \
95             if ((map)->tombstones > (map)->capacity / 4 || \
96             ((map)->size > 0 && (map)->tombstones > (map)->size)) \
97             HM_FN(compact)(map); \
98             } while (0)
99             #endif
100              
101             /* Grow-or-compact when load factor >= 75%. LRU maps with tombstones
102             * compact in place (preserving max_size); all others resize. Returns
103             * `fail_ret` from the enclosing function if the operation fails. */
104             #ifndef HM_ENSURE_CAPACITY_DEFINED
105             #define HM_ENSURE_CAPACITY_DEFINED
106             #define HM_ENSURE_CAPACITY(map, fail_ret) do { \
107             if (((map)->size + (map)->tombstones) * 4 >= (map)->capacity * 3) { \
108             if ((map)->max_size > 0 && (map)->tombstones > 0) { \
109             if (!HM_FN(compact)(map)) return fail_ret; \
110             } else { \
111             if (!HM_FN(resize)(map)) return fail_ret; \
112             } \
113             } \
114             } while (0)
115             #endif
116              
117             /* Lazily allocate expires_at[] on first per-key TTL use. Returns
118             * `fail_ret` on OOM. */
119             #ifndef HM_LAZY_ALLOC_EXPIRES_DEFINED
120             #define HM_LAZY_ALLOC_EXPIRES_DEFINED
121             #define HM_LAZY_ALLOC_EXPIRES(map, entry_ttl, fail_ret) do { \
122             if (HM_UNLIKELY((entry_ttl) > 0 && !(map)->expires_at)) { \
123             (map)->expires_at = (uint32_t*)calloc((map)->capacity, sizeof(uint32_t)); \
124             if (!(map)->expires_at) return fail_ret; \
125             } \
126             } while (0)
127             #endif
128              
129             /* Apply TTL to slot (get_or_set path, where slot is known-zero). Use
130             * entry_ttl if > 0, else default_ttl. No-op if neither is set. */
131             #ifndef HM_APPLY_ENTRY_TTL_DEFINED
132             #define HM_APPLY_ENTRY_TTL_DEFINED
133             #define HM_APPLY_ENTRY_TTL(map, idx, entry_ttl) do { \
134             if ((map)->expires_at) { \
135             uint32_t _hm_ttl = (entry_ttl) > 0 ? (entry_ttl) : (map)->default_ttl; \
136             if (_hm_ttl > 0) (map)->expires_at[idx] = hm_expiry_at(_hm_ttl); \
137             } \
138             } while (0)
139             #endif
140              
141             /* ---- Constants ---- */
142              
143             #ifndef HM_INITIAL_CAPACITY
144             #define HM_INITIAL_CAPACITY 16
145             #endif
146              
147             #ifdef HM_KEY_IS_INT
148             #define HM_EMPTY_KEY HM_INT_MIN
149             #define HM_TOMBSTONE_KEY (HM_INT_MIN + 1)
150             #define HM_IS_RESERVED_KEY(k) ((k) == HM_EMPTY_KEY || (k) == HM_TOMBSTONE_KEY)
151             #endif
152              
153             /* ---- UTF-8 flag packing in uint32_t length ---- */
154              
155             #ifndef HM_UTF8_MACROS_DEFINED
156             #define HM_UTF8_MACROS_DEFINED
157             #define HM_UTF8_FLAG ((uint32_t)0x80000000U)
158             #define HM_LEN_MASK ((uint32_t)0x7FFFFFFFU)
159             #define HM_PACK_LEN(len, is_utf8) ((uint32_t)(len) | ((is_utf8) ? HM_UTF8_FLAG : 0))
160             #define HM_UNPACK_LEN(packed) ((uint32_t)((packed) & HM_LEN_MASK))
161             #define HM_UNPACK_UTF8(packed) (((packed) & HM_UTF8_FLAG) != 0)
162             #endif
163              
164             /* ---- LRU sentinel ---- */
165              
166             #ifndef HM_LRU_NONE_DEFINED
167             #define HM_LRU_NONE_DEFINED
168             #define HM_LRU_NONE UINT32_MAX
169             #endif
170              
171             /* ---- Hash functions (xxHash v0.8.3) ---- */
172              
173             #ifndef HM_HASH_FUNCTIONS_DEFINED
174             #define HM_HASH_FUNCTIONS_DEFINED
175              
176             /* Perl may redefine malloc/free/calloc/realloc to its own allocators.
177             * On threaded perls with PERL_NO_GET_CONTEXT these need my_perl in scope,
178             * which static inline functions (xxHash, our own helpers) won't have.
179             * We intentionally use system allocators for our data structures. */
180             #undef malloc
181             #undef free
182             #undef calloc
183             #undef realloc
184              
185             #define XXH_INLINE_ALL
186             #include "xxhash.h"
187              
188 4345447           static inline size_t hm_hash_int64(int64_t key) {
189 4345447           return (size_t)XXH3_64bits(&key, sizeof(key));
190             }
191              
192 1075956           static inline uint32_t hm_hash_string(const char* data, uint32_t len) {
193 1075956           return (uint32_t)XXH3_64bits(data, len);
194             }
195              
196             #endif /* HM_HASH_FUNCTIONS_DEFINED */
197              
198             /* ---- Tombstone marker for string keys ---- */
199              
200             #ifndef HM_KEY_IS_INT
201             #ifndef HM_STR_TOMBSTONE_DEFINED
202             #define HM_STR_TOMBSTONE_DEFINED
203             static char hm_str_tombstone_marker;
204             #endif
205             #define HM_STR_TOMBSTONE (&hm_str_tombstone_marker)
206             #endif
207              
208             /* ---- Node struct ---- */
209              
210             typedef struct {
211             #ifdef HM_KEY_IS_INT
212             HM_INT_TYPE key;
213             #else
214             char* key; /* NULL = empty, HM_STR_TOMBSTONE = deleted */
215             uint32_t key_len; /* high bit = UTF-8 flag */
216             uint32_t key_hash;
217             #endif
218             #ifdef HM_VALUE_IS_STR
219             char* value;
220             uint32_t val_len; /* high bit = UTF-8 flag */
221             #elif defined(HM_VALUE_IS_SV)
222             void* value; /* opaque SV* — refcounted by caller */
223             #else
224             HM_INT_TYPE value;
225             #endif
226             } HM_NODE_TYPE;
227              
228             typedef struct {
229             HM_NODE_TYPE* nodes;
230             size_t capacity;
231             size_t size;
232             size_t tombstones;
233             size_t mask;
234             /* LRU fields (active only when max_size > 0) */
235             size_t max_size;
236             uint32_t lru_head;
237             uint32_t lru_tail;
238             uint32_t* lru_prev;
239             uint32_t* lru_next;
240             /* LRU probabilistic skip (0 = strict, 1-99 = skip %) */
241             uint32_t lru_skip; /* original percentage for accessor */
242             uint32_t lru_skip_every; /* promote every Nth access (0 = every time) */
243             uint32_t lru_skip_ctr; /* countdown to next promotion */
244             /* TTL fields (active only when default_ttl > 0) */
245             uint32_t default_ttl;
246             uint32_t* expires_at;
247             /* Iterator state for each() */
248             size_t iter_pos;
249             #ifdef HM_VALUE_IS_SV
250             void (*free_value_fn)(void*); /* SvREFCNT_dec callback */
251             #endif
252             } HM_MAP_TYPE;
253              
254             /* ---- Slot state macros ---- */
255              
256             #ifdef HM_KEY_IS_INT
257             #define HM_SLOT_IS_EMPTY(n) ((n)->key == HM_EMPTY_KEY)
258             #define HM_SLOT_IS_TOMBSTONE(n) ((n)->key == HM_TOMBSTONE_KEY)
259             #define HM_SLOT_IS_LIVE(n) (!HM_SLOT_IS_EMPTY(n) && !HM_SLOT_IS_TOMBSTONE(n))
260             #else
261             #define HM_SLOT_IS_EMPTY(n) ((n)->key == NULL)
262             #define HM_SLOT_IS_TOMBSTONE(n) ((n)->key == HM_STR_TOMBSTONE)
263             #define HM_SLOT_IS_LIVE(n) (!HM_SLOT_IS_EMPTY(n) && !HM_SLOT_IS_TOMBSTONE(n))
264             #endif
265              
266             /* ---- Init nodes ---- */
267              
268 2801           static inline void HM_FN(init_nodes)(HM_NODE_TYPE* nodes, size_t capacity) {
  311            
  311            
  46            
  52            
  102            
  165            
  385            
  118            
  231            
  545            
  141            
  98            
  161            
  135            
269             #ifdef HM_KEY_IS_INT
270             /* Integer keys use sentinel values — must init per-element */
271             size_t i;
272 30033915 100         for (i = 0; i < capacity; i++) {
  2734007 100          
  983454 100          
  2098148 100          
  2722629 100          
  2726759 100          
  10861745 100          
  4294605 100          
  1607745 100          
  2004823            
273 30032128           nodes[i].key = HM_EMPTY_KEY;
  2733696            
  983408            
  2098096            
  2722464            
  2726528            
  10861200            
  4294464            
  1607584            
  2004688            
274             #ifdef HM_VALUE_IS_STR
275 7056576           nodes[i].value = NULL;
  2722464            
  2726528            
  1607584            
276 7056576           nodes[i].val_len = 0;
  2722464            
  2726528            
  1607584            
277             #elif defined(HM_VALUE_IS_SV)
278 5815200           nodes[i].value = NULL;
  2733696            
  983408            
  2098096            
279             #else
280 17160352           nodes[i].value = 0;
  10861200            
  4294464            
  2004688            
281             #endif
282             }
283             #else
284             /* String keys: NULL=empty, all-zero works for pointers and lengths */
285 1014           memset(nodes, 0, capacity * sizeof(HM_NODE_TYPE));
  311            
  102            
  385            
  118            
  98            
286             #endif
287 2801           }
  311            
  311            
  46            
  52            
  102            
  165            
  385            
  118            
  231            
  545            
  141            
  98            
  161            
  135            
288              
289             /* ---- Free resources for a single node ---- */
290              
291 1301473           static inline void HM_FN(free_node)(HM_MAP_TYPE* map, HM_NODE_TYPE* node) {
  81580            
  82025            
  30066            
  50126            
  80230            
  80417            
  82493            
  80286            
  81034            
  282753            
  150010            
  80128            
  60314            
  80011            
292             (void)map;
293             #ifndef HM_KEY_IS_INT
294 404717 50         if (node->key != NULL && node->key != HM_STR_TOMBSTONE) {
  81580 50          
  80230 50          
  82493 50          
  80286 50          
  80128 50          
    50          
    50          
    50          
    50          
295 404717           free(node->key);
  81580            
  80230            
  82493            
  80286            
  80128            
296 404717           node->key = NULL;
  81580            
  80230            
  82493            
  80286            
  80128            
297             }
298             #endif
299             #ifdef HM_VALUE_IS_STR
300 304258 100         if (node->value != NULL) {
  80417 100          
  82493 100          
  81034 100          
  60314            
301 304254           free(node->value);
  80416            
  82492            
  81033            
  60313            
302 304254           node->value = NULL;
  80416            
  82492            
  81033            
  60313            
303             }
304             #elif defined(HM_VALUE_IS_SV)
305 243797 100         if (node->value != NULL && map->free_value_fn) {
  81580 50          
  82025 100          
  30066 50          
  50126 100          
    50          
    100          
    50          
306 243789           map->free_value_fn(node->value);
  81578            
  82021            
  30065            
  50125            
307 243789           node->value = NULL;
  81578            
  82021            
  30065            
  50125            
308             }
309             #endif
310             (void)node;
311 1301473           }
  81580            
  82025            
  30066            
  50126            
  80230            
  80417            
  82493            
  80286            
  81034            
  282753            
  150010            
  80128            
  60314            
  80011            
312              
313             /* ---- LRU helpers ---- */
314              
315 4252           static inline void HM_FN(lru_unlink)(HM_MAP_TYPE* map, uint32_t idx) {
  343            
  186            
  2            
  2            
  2            
  181            
  352            
  4            
  181            
  2812            
  1            
  4            
  181            
  1            
316 4252           uint32_t p = map->lru_prev[idx];
  343            
  186            
  2            
  2            
  2            
  181            
  352            
  4            
  181            
  2812            
  1            
  4            
  181            
  1            
317 4252           uint32_t n = map->lru_next[idx];
  343            
  186            
  2            
  2            
  2            
  181            
  352            
  4            
  181            
  2812            
  1            
  4            
  181            
  1            
318 4252 100         if (p != HM_LRU_NONE) map->lru_next[p] = n;
  343 50          
  186 50          
  2 50          
  2 100          
  2 50          
  181 50          
  352 100          
  4 50          
  181 100          
  2812 50          
  1 100          
  4 50          
  181 50          
  1            
319 10           else map->lru_head = n;
  1            
  0            
  0            
  0            
  1            
  0            
  0            
  1            
  0            
  6            
  0            
  1            
  0            
  0            
320 4252 50         if (n != HM_LRU_NONE) map->lru_prev[n] = p;
  343 100          
  186 50          
  2 50          
  2 50          
  2 50          
  181 50          
  352 100          
  4 50          
  181 100          
  2812 50          
  1 100          
  4 50          
  181 50          
  1            
321 4231           else map->lru_tail = p;
  343            
  185            
  2            
  2            
  2            
  181            
  352            
  3            
  181            
  2794            
  1            
  3            
  181            
  1            
322 4252           map->lru_prev[idx] = HM_LRU_NONE;
  343            
  186            
  2            
  2            
  2            
  181            
  352            
  4            
  181            
  2812            
  1            
  4            
  181            
  1            
323 4252           map->lru_next[idx] = HM_LRU_NONE;
  343            
  186            
  2            
  2            
  2            
  181            
  352            
  4            
  181            
  2812            
  1            
  4            
  181            
  1            
324 4252           }
  343            
  186            
  2            
  2            
  2            
  181            
  352            
  4            
  181            
  2812            
  1            
  4            
  181            
  1            
325              
326 4985           static inline void HM_FN(lru_push_front)(HM_MAP_TYPE* map, uint32_t idx) {
  409            
  213            
  8            
  8            
  4            
  203            
  426            
  7            
  203            
  3286            
  4            
  7            
  203            
  4            
327 4985           map->lru_prev[idx] = HM_LRU_NONE;
  409            
  213            
  8            
  8            
  4            
  203            
  426            
  7            
  203            
  3286            
  4            
  7            
  203            
  4            
328 4985           map->lru_next[idx] = map->lru_head;
  409            
  213            
  8            
  8            
  4            
  203            
  426            
  7            
  203            
  3286            
  4            
  7            
  203            
  4            
329 4985 100         if (map->lru_head != HM_LRU_NONE)
  409 100          
  213 100          
  8 100          
  8 100          
  4 100          
  203 100          
  426 100          
  7 100          
  203 100          
  3286 100          
  4 100          
  7 100          
  203 100          
  4            
330 4910           map->lru_prev[map->lru_head] = idx;
  402            
  208            
  6            
  6            
  2            
  200            
  419            
  5            
  200            
  3251            
  3            
  5            
  200            
  3            
331             else
332 75           map->lru_tail = idx;
  7            
  5            
  2            
  2            
  2            
  3            
  7            
  2            
  3            
  35            
  1            
  2            
  3            
  1            
333 4985           map->lru_head = idx;
  409            
  213            
  8            
  8            
  4            
  203            
  426            
  7            
  203            
  3286            
  4            
  7            
  203            
  4            
334 4985           }
  409            
  213            
  8            
  8            
  4            
  203            
  426            
  7            
  203            
  3286            
  4            
  7            
  203            
  4            
335              
336 143           static inline void HM_FN(lru_promote)(HM_MAP_TYPE* map, uint32_t idx) {
  2            
  4            
  2            
  2            
  0            
  0            
  2            
  2            
  1            
  126            
  0            
  2            
  0            
  0            
337 143 50         if (map->lru_head == idx) return;
  2 100          
  4 50          
  2 50          
  2 0          
  0 0          
  0 50          
  2 50          
  2 50          
  1 100          
  126 0          
  0 50          
  2 0          
  0 0          
  0            
338 125 0         if (map->lru_skip_every && idx != map->lru_tail) {
  0 0          
  2 50          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  2 0          
  0 0          
  119 0          
  0 0          
  2 0          
  0 0          
  0 50          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
339 1 0         if (++map->lru_skip_ctr < map->lru_skip_every) return;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0            
340 0           map->lru_skip_ctr = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
341             }
342 124           HM_FN(lru_unlink)(map, idx);
  0            
  2            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  118            
  0            
  2            
  0            
  0            
343 124           HM_FN(lru_push_front)(map, idx);
  0            
  2            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  118            
  0            
  2            
  0            
  0            
344             }
345              
346             /* Tombstone a node at a known index (used by LRU eviction and TTL expiry) */
347 1295905           static void HM_FN(tombstone_at)(HM_MAP_TYPE* map, size_t index) {
  80851            
  80694            
  30009            
  50009            
  80009            
  80187            
  80873            
  80009            
  80296            
  282753            
  150010            
  80009            
  60185            
  80011            
348 1295905           HM_FN(free_node)(map, &map->nodes[index]);
  80851            
  80694            
  30009            
  50009            
  80009            
  80187            
  80873            
  80009            
  80296            
  282753            
  150010            
  80009            
  60185            
  80011            
349             #ifdef HM_KEY_IS_INT
350 894154           map->nodes[index].key = HM_TOMBSTONE_KEY;
  80694            
  30009            
  50009            
  80187            
  80296            
  282753            
  150010            
  60185            
  80011            
351             #else
352 401751           map->nodes[index].key = HM_STR_TOMBSTONE;
  80851            
  80009            
  80873            
  80009            
  80009            
353 401751           map->nodes[index].key_len = 0;
  80851            
  80009            
  80873            
  80009            
  80009            
354 401751           map->nodes[index].key_hash = 0;
  80851            
  80009            
  80873            
  80009            
  80009            
355             #endif
356             #ifdef HM_VALUE_IS_STR
357 301541           map->nodes[index].val_len = 0;
  80187            
  80873            
  80296            
  60185            
358             #endif
359 1295905 100         if (map->expires_at) map->expires_at[index] = 0;
  80851 100          
  80694 100          
  30009 100          
  50009 100          
  80009 50          
  80187 100          
  80873 100          
  80009 100          
  80296 100          
  282753 100          
  150010 100          
  80009 50          
  60185 100          
  80011            
360 1295905           map->size--;
  80851            
  80694            
  30009            
  50009            
  80009            
  80187            
  80873            
  80009            
  80296            
  282753            
  150010            
  80009            
  60185            
  80011            
361 1295905           map->tombstones++;
  80851            
  80694            
  30009            
  50009            
  80009            
  80187            
  80873            
  80009            
  80296            
  282753            
  150010            
  80009            
  60185            
  80011            
362 1295905           }
  80851            
  80694            
  30009            
  50009            
  80009            
  80187            
  80873            
  80009            
  80296            
  282753            
  150010            
  80009            
  60185            
  80011            
363              
364             /* Evict the LRU tail entry */
365 4106           static void HM_FN(lru_evict_one)(HM_MAP_TYPE* map) {
  342            
  183            
  2            
  2            
  1            
  181            
  351            
  1            
  181            
  2678            
  1            
  1            
  181            
  1            
366 4106           uint32_t victim = map->lru_tail;
  342            
  183            
  2            
  2            
  1            
  181            
  351            
  1            
  181            
  2678            
  1            
  1            
  181            
  1            
367 4106 50         if (victim == HM_LRU_NONE) return;
  342 50          
  183 50          
  2 50          
  2 50          
  1 50          
  181 50          
  351 50          
  1 50          
  181 50          
  2678 50          
  1 50          
  1 50          
  181 50          
  1            
368 4106           HM_FN(lru_unlink)(map, victim);
  342            
  183            
  2            
  2            
  1            
  181            
  351            
  1            
  181            
  2678            
  1            
  1            
  181            
  1            
369 4106           HM_FN(tombstone_at)(map, (size_t)victim);
  342            
  183            
  2            
  2            
  1            
  181            
  351            
  1            
  181            
  2678            
  1            
  1            
  181            
  1            
370             }
371              
372             /* Forward declaration (needed by expire_at) */
373             static bool HM_FN(compact)(HM_MAP_TYPE* map);
374              
375             /* Expire a TTL'd entry at a known index.
376             * may_compact: true for write paths (put/remove/incr/get_or_set),
377             * false for read paths (get/exists) to avoid resetting
378             * iter_pos and invalidating get_direct pointers. */
379 41           static void HM_FN(expire_at)(HM_MAP_TYPE* map, size_t index, bool may_compact) {
  3            
  3            
  2            
  2            
  1            
  0            
  3            
  1            
  1            
  21            
  1            
  2            
  0            
  1            
380 41 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          
  3 50          
  1 50          
  1 100          
  21 50          
  1 50          
  2 0          
  0 50          
  1            
381 41           HM_FN(tombstone_at)(map, index);
  3            
  3            
  2            
  2            
  1            
  0            
  3            
  1            
  1            
  21            
  1            
  2            
  0            
  1            
382 41 100         if (may_compact) HM_MAYBE_COMPACT(map);
  3 50          
  3 50          
  2 0          
  2 100          
  1 50          
  0 50          
  3 0          
  1 100          
  1 50          
  21 50          
  1 0          
  2 100          
  0 50          
  1 50          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    100          
    50          
    100          
    100          
    50          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
383 41           }
  3            
  3            
  2            
  2            
  1            
  0            
  3            
  1            
  1            
  21            
  1            
  2            
  0            
  1            
384              
385             /* ---- Create / Destroy ---- */
386              
387 516           static HM_MAP_TYPE* HM_FN(create)(size_t max_size, uint32_t default_ttl, uint32_t lru_skip) {
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
388 516           HM_MAP_TYPE* map = (HM_MAP_TYPE*)malloc(sizeof(HM_MAP_TYPE));
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
389 516 50         if (!map) return NULL;
  47 50          
  55 50          
  16 50          
  16 50          
  18 50          
  14 50          
  78 50          
  33 50          
  36 50          
  124 50          
  26 50          
  17 50          
  15 50          
  21            
390              
391 516           map->capacity = HM_INITIAL_CAPACITY;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
392 516           map->mask = HM_INITIAL_CAPACITY - 1;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
393 516           map->size = 0;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
394 516           map->tombstones = 0;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
395 516           map->max_size = max_size;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
396 516           map->default_ttl = default_ttl;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
397 516           map->lru_skip = (lru_skip > 99) ? 99 : lru_skip;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
398             /* Convert percentage to "promote every Nth": 0%→1(every), 50%→2, 90%→10, 99%→100 */
399 516 50         map->lru_skip_every = (map->lru_skip > 0) ? (uint32_t)(100 / (100 - map->lru_skip)) : 0;
  47 50          
  55 50          
  16 50          
  16 50          
  18 50          
  14 100          
  78 50          
  33 50          
  36 100          
  124 50          
  26 50          
  17 50          
  15 50          
  21            
400 516           map->lru_skip_ctr = 0;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
401 516           map->lru_head = HM_LRU_NONE;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
402 516           map->lru_tail = HM_LRU_NONE;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
403 516           map->lru_prev = NULL;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
404 516           map->lru_next = NULL;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
405 516           map->iter_pos = 0;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
406             #ifdef HM_VALUE_IS_SV
407 134           map->free_value_fn = NULL;
  47            
  55            
  16            
  16            
408             #endif
409 516           map->expires_at = NULL;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
410              
411 516           map->nodes = (HM_NODE_TYPE*)malloc(map->capacity * sizeof(HM_NODE_TYPE));
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
412 516 50         if (!map->nodes) { free(map); return NULL; }
  47 50          
  55 50          
  16 50          
  16 50          
  18 50          
  14 50          
  78 50          
  33 50          
  36 50          
  124 50          
  26 50          
  17 50          
  15 50          
  21            
413 516           HM_FN(init_nodes)(map->nodes, map->capacity);
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
414              
415 516 100         if (max_size > 0) {
  47 100          
  55 100          
  16 100          
  16 100          
  18 100          
  14 100          
  78 100          
  33 100          
  36 100          
  124 100          
  26 100          
  17 100          
  15 100          
  21            
416 79           map->lru_prev = (uint32_t*)malloc(map->capacity * sizeof(uint32_t));
  7            
  5            
  2            
  2            
  2            
  3            
  7            
  2            
  3            
  39            
  1            
  2            
  3            
  1            
417 79           map->lru_next = (uint32_t*)malloc(map->capacity * sizeof(uint32_t));
  7            
  5            
  2            
  2            
  2            
  3            
  7            
  2            
  3            
  39            
  1            
  2            
  3            
  1            
418 79 50         if (!map->lru_prev || !map->lru_next) {
  7 50          
  5 50          
  2 50          
  2 50          
  2 50          
  3 50          
  7 50          
  2 50          
  3 50          
  39 50          
  1 50          
  2 50          
  3 50          
  1 50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
419 0           free(map->lru_prev); free(map->lru_next);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
420 0           free(map->nodes); free(map);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
421 0           return NULL;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
422             }
423 79           memset(map->lru_prev, 0xFF, map->capacity * sizeof(uint32_t));
  7            
  5            
  2            
  2            
  2            
  3            
  7            
  2            
  3            
  39            
  1            
  2            
  3            
  1            
424 79           memset(map->lru_next, 0xFF, map->capacity * sizeof(uint32_t));
  7            
  5            
  2            
  2            
  2            
  3            
  7            
  2            
  3            
  39            
  1            
  2            
  3            
  1            
425             }
426              
427 516 100         if (default_ttl > 0) {
  47 100          
  55 100          
  16 100          
  16 50          
  18 50          
  14 100          
  78 100          
  33 100          
  36 100          
  124 50          
  26 100          
  17 50          
  15 100          
  21            
428 55           map->expires_at = (uint32_t*)calloc(map->capacity, sizeof(uint32_t));
  4            
  4            
  2            
  2            
  0            
  0            
  9            
  1            
  1            
  30            
  0            
  1            
  0            
  1            
429 55 50         if (!map->expires_at) {
  4 50          
  4 50          
  2 50          
  2 0          
  0 0          
  0 50          
  9 50          
  1 50          
  1 50          
  30 0          
  0 50          
  1 0          
  0 50          
  1            
430 0           free(map->lru_prev); free(map->lru_next);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
431 0           free(map->nodes); free(map);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
432 0           return NULL;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
433             }
434             }
435              
436 516           return map;
  47            
  55            
  16            
  16            
  18            
  14            
  78            
  33            
  36            
  124            
  26            
  17            
  15            
  21            
437             }
438              
439 522           static void HM_FN(destroy)(HM_MAP_TYPE* map) {
  47            
  56            
  16            
  16            
  18            
  14            
  79            
  33            
  36            
  128            
  26            
  17            
  15            
  21            
440 522 50         if (!map) return;
  47 50          
  56 50          
  16 50          
  16 50          
  18 50          
  14 50          
  79 50          
  33 50          
  36 50          
  128 50          
  26 50          
  17 50          
  15 50          
  21            
441             #if !defined(HM_KEY_IS_INT) || defined(HM_VALUE_IS_STR) || defined(HM_VALUE_IS_SV)
442             {
443             size_t i;
444 1476043 100         for (i = 0; i < map->capacity; i++) {
  149855 100          
  151448 100          
  65824 100          
  131552 100          
  148210 100          
  148142 100          
  152351 100          
  148497 100          
  149860 100          
  147937 100          
  82367            
445 1475696 100         if (HM_SLOT_IS_LIVE(&map->nodes[i])) {
  149808 100          
  151392 100          
  65808 100          
  131536 100          
  148192 100          
  148128 100          
  152272 100          
  148464 100          
  149824 100          
  147920 100          
  82352 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
446 3545           HM_FN(free_node)(map, &map->nodes[i]);
  630            
  1032            
  18            
  18            
  23            
  31            
  1123            
  79            
  540            
  21            
  30            
447             }
448             }
449             }
450             #endif
451 522           free(map->lru_prev);
  47            
  56            
  16            
  16            
  18            
  14            
  79            
  33            
  36            
  128            
  26            
  17            
  15            
  21            
452 522           free(map->lru_next);
  47            
  56            
  16            
  16            
  18            
  14            
  79            
  33            
  36            
  128            
  26            
  17            
  15            
  21            
453 522           free(map->expires_at);
  47            
  56            
  16            
  16            
  18            
  14            
  79            
  33            
  36            
  128            
  26            
  17            
  15            
  21            
454 522           free(map->nodes);
  47            
  56            
  16            
  16            
  18            
  14            
  79            
  33            
  36            
  128            
  26            
  17            
  15            
  21            
455 522           free(map);
  47            
  56            
  16            
  16            
  18            
  14            
  79            
  33            
  36            
  128            
  26            
  17            
  15            
  21            
456             }
457              
458             /* ---- clear: remove all entries without destroying the map ---- */
459              
460 53           static void HM_FN(clear)(HM_MAP_TYPE* map) {
  3            
  7            
  3            
  3            
  2            
  3            
  9            
  2            
  2            
  8            
  3            
  2            
  3            
  3            
461 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            
462             #if !defined(HM_KEY_IS_INT) || defined(HM_VALUE_IS_STR) || defined(HM_VALUE_IS_SV)
463             {
464             size_t i;
465 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            
466 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          
467 2023           HM_FN(free_node)(map, &map->nodes[i]);
  99            
  299            
  39            
  99            
  198            
  199            
  497            
  198            
  198            
  98            
  99            
468             }
469             }
470             #endif
471 53           HM_FN(init_nodes)(map->nodes, map->capacity);
  3            
  7            
  3            
  3            
  2            
  3            
  9            
  2            
  2            
  8            
  3            
  2            
  3            
  3            
472 53           map->size = 0;
  3            
  7            
  3            
  3            
  2            
  3            
  9            
  2            
  2            
  8            
  3            
  2            
  3            
  3            
473 53           map->tombstones = 0;
  3            
  7            
  3            
  3            
  2            
  3            
  9            
  2            
  2            
  8            
  3            
  2            
  3            
  3            
474 53           map->iter_pos = 0;
  3            
  7            
  3            
  3            
  2            
  3            
  9            
  2            
  2            
  8            
  3            
  2            
  3            
  3            
475 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            
476 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            
477 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            
478 1           map->lru_head = HM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
479 1           map->lru_tail = HM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
480             }
481 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            
482 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            
483             }
484              
485             /* ---- purge: force-expire all TTL'd entries ---- */
486              
487 2           static void HM_FN(purge)(HM_MAP_TYPE* map) {
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
488 2 0         if (!map || !map->expires_at) return;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  1 0          
  0 0          
  0 0          
  1 0          
  0 0          
  0 50          
  0 50          
  0 0          
    0          
    0          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
489 2           uint32_t now = (uint32_t)time(NULL);
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
490             size_t i;
491 34 0         for (i = 0; i < map->capacity; i++) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  17 0          
  0 0          
  0 100          
  17 0          
  0 0          
  0 0          
  0 0          
  0            
492 32 0         if (HM_SLOT_IS_LIVE(&map->nodes[i]) &&
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  16 0          
  0 0          
  0 0          
  16 0          
  0 0          
  0 100          
  0 50          
  0 0          
    0          
    0          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
493 16 0         map->expires_at[i] && now > map->expires_at[i]) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  5 0          
  0 0          
  0 0          
  11 0          
  0 0          
  0 50          
  0 50          
  0 0          
    0          
    0          
    0          
    50          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
494 15 0         if (HM_UNLIKELY(map->lru_prev))
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  5 0          
  0 0          
  0 50          
  10 0          
  0 0          
  0 0          
  0 0          
  0            
495 0           HM_FN(lru_unlink)(map, (uint32_t)i);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
496 15           HM_FN(tombstone_at)(map, i);
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
  10            
  0            
  0            
  0            
  0            
497             }
498             }
499 2 0         if (map->tombstones > 0) HM_FN(compact)(map);
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0            
500             }
501              
502             /* ---- clone: deep copy the entire map ---- */
503              
504 6           static HM_MAP_TYPE* HM_FN(clone)(const HM_MAP_TYPE* map) {
  0            
  1            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
  0            
  0            
505 6 0         if (!map) return NULL;
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  4 0          
  0 0          
  0 0          
  0 0          
  0            
506              
507 6           HM_MAP_TYPE* c = (HM_MAP_TYPE*)malloc(sizeof(HM_MAP_TYPE));
  0            
  1            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
  0            
  0            
508 6 0         if (!c) return NULL;
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  4 0          
  0 0          
  0 0          
  0 0          
  0            
509 6           *c = *map; /* shallow copy */
  0            
  1            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
  0            
  0            
510 6           c->iter_pos = 0;
  0            
  1            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
  0            
  0            
511             #ifdef HM_VALUE_IS_SV
512 1           c->free_value_fn = NULL; /* prevent double-dec on OOM cleanup */
  0            
  1            
  0            
  0            
513             #endif
514              
515 6           c->nodes = (HM_NODE_TYPE*)malloc(map->capacity * sizeof(HM_NODE_TYPE));
  0            
  1            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
  0            
  0            
516 6 0         if (!c->nodes) { free(c); return NULL; }
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  4 0          
  0 0          
  0 0          
  0 0          
  0            
517             /* Null shared pointers so goto-fail destroy() won't free source arrays */
518 6           c->lru_prev = NULL;
  0            
  1            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
  0            
  0            
519 6           c->lru_next = NULL;
  0            
  1            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
  0            
  0            
520 6           c->expires_at = NULL;
  0            
  1            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
  0            
  0            
521              
522             /* Init all nodes empty first, then deep-copy live entries one by one.
523             This ensures OOM during copy leaves a valid (partial) map for destroy. */
524 6           HM_FN(init_nodes)(c->nodes, map->capacity);
  0            
  1            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
  0            
  0            
525 6           c->size = 0;
  0            
  1            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
  0            
  0            
526 6           c->tombstones = 0;
  0            
  1            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
  0            
  0            
527             {
528             size_t i;
529 454 0         for (i = 0; i < map->capacity; i++) {
  0 100          
  17 0          
  0 0          
  0 0          
  0 0          
  0 100          
  17 0          
  0 0          
  0 100          
  420 0          
  0 0          
  0 0          
  0 0          
  0            
530 448 0         if (!HM_SLOT_IS_LIVE(&map->nodes[i])) continue;
  0 0          
  16 100          
  0 50          
  0 0          
  0 0          
  0 0          
  16 0          
  0 0          
  0 0          
  416 0          
  0 0          
  0 100          
  0 50          
  0 0          
    0          
    0          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
531             #ifdef HM_KEY_IS_INT
532 157           c->nodes[i].key = map->nodes[i].key;
  1            
  0            
  0            
  0            
  0            
  156            
  0            
  0            
  0            
533             #else
534             {
535 10           uint32_t klen = HM_UNPACK_LEN(map->nodes[i].key_len);
  0            
  0            
  10            
  0            
  0            
536 10           c->nodes[i].key = (char*)malloc(klen + 1);
  0            
  0            
  10            
  0            
  0            
537 10 0         if (!c->nodes[i].key) goto fail;
  0 0          
  0 50          
  10 0          
  0 0          
  0            
538 10           memcpy(c->nodes[i].key, map->nodes[i].key, klen + 1);
  0            
  0            
  10            
  0            
  0            
539 10           c->nodes[i].key_len = map->nodes[i].key_len;
  0            
  0            
  10            
  0            
  0            
540 10           c->nodes[i].key_hash = map->nodes[i].key_hash;
  0            
  0            
  10            
  0            
  0            
541             }
542             #endif
543             #ifdef HM_VALUE_IS_STR
544 10 0         if (map->nodes[i].value) {
  0 50          
  10 0          
  0 0          
  0            
545 10           uint32_t vlen = HM_UNPACK_LEN(map->nodes[i].val_len);
  0            
  10            
  0            
  0            
546 10           c->nodes[i].value = (char*)malloc(vlen + 1);
  0            
  10            
  0            
  0            
547 10 0         if (!c->nodes[i].value) goto fail;
  0 50          
  10 0          
  0 0          
  0            
548 10           memcpy(c->nodes[i].value, map->nodes[i].value, vlen + 1);
  0            
  10            
  0            
  0            
549             }
550 10           c->nodes[i].val_len = map->nodes[i].val_len;
  0            
  10            
  0            
  0            
551             #elif defined(HM_VALUE_IS_SV)
552 1           c->nodes[i].value = map->nodes[i].value;
  0            
  1            
  0            
  0            
553             /* SV* refcount increment done by caller (needs pTHX) */
554             #else
555 156           c->nodes[i].value = map->nodes[i].value;
  0            
  0            
  156            
  0            
  0            
  0            
556             #endif
557 167           c->size++;
  0            
  1            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
  156            
  0            
  0            
  0            
  0            
558             }
559             }
560              
561             /* Deep copy LRU arrays (already NULLed above) */
562 6 0         if (map->lru_prev) {
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 100          
  4 0          
  0 0          
  0 0          
  0 0          
  0            
563 2           c->lru_prev = (uint32_t*)malloc(map->capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
  0            
  0            
564 2           c->lru_next = (uint32_t*)malloc(map->capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
  0            
  0            
565 2 0         if (!c->lru_prev || !c->lru_next) goto fail;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
566 2           memcpy(c->lru_prev, map->lru_prev, map->capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
  0            
  0            
567 2           memcpy(c->lru_next, map->lru_next, map->capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
  0            
  0            
568             }
569              
570             /* Deep copy TTL array (already NULLed above) */
571 6 0         if (map->expires_at) {
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 100          
  4 0          
  0 0          
  0 0          
  0 0          
  0            
572 1           c->expires_at = (uint32_t*)malloc(map->capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
573 1 0         if (!c->expires_at) goto fail;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0            
574 1           memcpy(c->expires_at, map->expires_at, map->capacity * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
575             }
576              
577             #ifdef HM_VALUE_IS_SV
578 1           c->free_value_fn = map->free_value_fn; /* restore after successful copy */
  0            
  1            
  0            
  0            
579             #endif
580 6           return c;
  0            
  1            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
  0            
  0            
581              
582 0           fail:
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
583             /* Partial cleanup — nodes may have partially-copied keys/values.
584             Use destroy which handles all cases correctly. */
585 0           HM_FN(destroy)(c);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
586 0           return NULL;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
587             }
588              
589             /* ---- Rehash: resize to specific capacity ---- */
590              
591 2226           static bool HM_FN(rehash_to)(HM_MAP_TYPE* map, size_t new_capacity) {
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
592 2226           size_t old_capacity = map->capacity;
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
593 2226           HM_NODE_TYPE* old_nodes = map->nodes;
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
594 2226           size_t new_mask = new_capacity - 1;
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
595 2226           HM_NODE_TYPE* new_nodes = (HM_NODE_TYPE*)malloc(new_capacity * sizeof(HM_NODE_TYPE));
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
596 2226 50         if (!new_nodes) return false;
  261 50          
  248 50          
  27 50          
  33 50          
  82 50          
  148 50          
  297 50          
  83 50          
  193 50          
  409 50          
  112 50          
  79 50          
  143 50          
  111            
597              
598             /* Allocate new LRU arrays if active */
599 2226           uint32_t* new_lru_prev = NULL;
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
600 2226           uint32_t* new_lru_next = NULL;
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
601 2226           uint32_t* old_to_new = NULL;
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
602 2226 100         if (map->lru_prev) {
  261 100          
  248 50          
  27 50          
  33 50          
  82 100          
  148 100          
  297 50          
  83 100          
  193 100          
  409 50          
  112 50          
  79 100          
  143 50          
  111            
603 704 50         if (new_capacity > (size_t)(uint32_t)-2) {
  104 50          
  66 0          
  0 0          
  0 0          
  0 50          
  66 50          
  107 0          
  0 50          
  66 50          
  229 0          
  0 0          
  0 50          
  66 0          
  0            
604 0           free(new_nodes);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
605 0           return false; /* capacity would overflow uint32_t LRU indices */
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
606             }
607 704           new_lru_prev = (uint32_t*)malloc(new_capacity * sizeof(uint32_t));
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  229            
  0            
  0            
  66            
  0            
608 704           new_lru_next = (uint32_t*)malloc(new_capacity * sizeof(uint32_t));
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  229            
  0            
  0            
  66            
  0            
609 704           old_to_new = (uint32_t*)malloc(old_capacity * sizeof(uint32_t));
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  229            
  0            
  0            
  66            
  0            
610 704 50         if (!new_lru_prev || !new_lru_next || !old_to_new) {
  104 50          
  66 50          
  0 50          
  0 50          
  0 50          
  66 0          
  107 0          
  0 0          
  66 0          
  229 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          
611 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            
612 0           return false;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
613             }
614 704           memset(new_lru_prev, 0xFF, new_capacity * sizeof(uint32_t));
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  229            
  0            
  0            
  66            
  0            
615 704           memset(new_lru_next, 0xFF, new_capacity * sizeof(uint32_t));
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  229            
  0            
  0            
  66            
  0            
616 704           memset(old_to_new, 0xFF, old_capacity * sizeof(uint32_t));
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  229            
  0            
  0            
  66            
  0            
617             }
618              
619             /* Allocate new TTL array if active */
620 2226           uint32_t* new_expires_at = NULL;
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
621 2226 100         if (map->expires_at) {
  261 100          
  248 50          
  27 50          
  33 50          
  82 50          
  148 100          
  297 50          
  83 50          
  193 100          
  409 50          
  112 50          
  79 50          
  143 50          
  111            
622 103           new_expires_at = (uint32_t*)calloc(new_capacity, sizeof(uint32_t));
  34            
  12            
  0            
  0            
  0            
  0            
  47            
  0            
  0            
  10            
  0            
  0            
  0            
  0            
623 103 50         if (!new_expires_at) {
  34 50          
  12 0          
  0 0          
  0 0          
  0 0          
  0 50          
  47 0          
  0 0          
  0 50          
  10 0          
  0 0          
  0 0          
  0 0          
  0            
624 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            
625 0           return false;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
626             }
627             }
628              
629 2226           HM_FN(init_nodes)(new_nodes, new_capacity);
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
630              
631             /* Copy live entries */
632             {
633             size_t i;
634 41257234 100         for (i = 0; i < old_capacity; i++) {
  2581413 100          
  2581768 100          
  917547 100          
  1966321 100          
  2572834 100          
  2573956 100          
  2583465 100          
  2572851 100          
  2576385 100          
  10184153 100          
  4129344 100          
  2572559 100          
  1525103 100          
  1919535            
635 41255008 100         if (HM_SLOT_IS_LIVE(&old_nodes[i])) {
  2581152 100          
  2581520 100          
  917520 100          
  1966288 100          
  2572752 100          
  2573808 100          
  2583168 100          
  2572768 100          
  2576192 100          
  10183744 100          
  4129232 100          
  2572480 100          
  1524960 100          
  1919424 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
636             #ifdef HM_KEY_IS_INT
637 1990363           size_t index = hm_hash_int64((int64_t)old_nodes[i].key) & new_mask;
  199302            
  79143            
  148439            
  196938            
  198036            
  597500            
  291182            
  127593            
  152230            
638 2415551 100         while (new_nodes[index].key != HM_EMPTY_KEY)
  243827 100          
  96829 100          
  181600 100          
  240779 100          
  242253 100          
  716516 100          
  352605 100          
  155981 100          
  185161            
639 425188           index = (index + 1) & new_mask;
  44525            
  17686            
  33161            
  43841            
  44217            
  119016            
  61423            
  28388            
  32931            
640             #else
641 987948           size_t index = (size_t)old_nodes[i].key_hash & new_mask;
  198901            
  196278            
  200395            
  196289            
  196085            
642 1211556 100         while (new_nodes[index].key != NULL)
  243873 100          
  240738 100          
  245690 100          
  240750 100          
  240505            
643 223608           index = (index + 1) & new_mask;
  44972            
  44460            
  45295            
  44461            
  44420            
644             #endif
645 2978311           new_nodes[index] = old_nodes[i];
  198901            
  199302            
  79143            
  148439            
  196278            
  196938            
  200395            
  196289            
  198036            
  597500            
  291182            
  196085            
  127593            
  152230            
646 2978311 100         if (old_to_new) old_to_new[i] = (uint32_t)index;
  198901 100          
  199302 50          
  79143 50          
  148439 50          
  196278 100          
  196938 100          
  200395 50          
  196289 100          
  198036 100          
  597500 50          
  291182 50          
  196085 100          
  127593 50          
  152230            
647 2978311 100         if (new_expires_at)
  198901 100          
  199302 50          
  79143 50          
  148439 50          
  196278 50          
  196938 100          
  200395 50          
  196289 50          
  198036 100          
  597500 50          
  291182 50          
  196085 50          
  127593 50          
  152230            
648 2206           new_expires_at[index] = map->expires_at[i];
  664            
  336            
  0            
  0            
  0            
  0            
  1000            
  0            
  0            
  206            
  0            
  0            
  0            
  0            
649             }
650             }
651             }
652              
653             /* Rebuild LRU linked list preserving order */
654 2226 100         if (map->lru_prev && map->lru_head != HM_LRU_NONE) {
  261 50          
  248 100          
  27 50          
  33 50          
  82 0          
  148 50          
  297 0          
  83 50          
  193 0          
  409 100          
  112 50          
  79 100          
  143 50          
  111 50          
    0          
    100          
    50          
    100          
    100          
    50          
    0          
    50          
    0          
    100          
    50          
    50          
    0          
655 703           uint32_t old_idx = map->lru_head;
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  228            
  0            
  0            
  66            
  0            
656 703           uint32_t prev_new = HM_LRU_NONE;
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  228            
  0            
  0            
  66            
  0            
657 703           uint32_t new_head = HM_LRU_NONE;
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  228            
  0            
  0            
  66            
  0            
658 703           uint32_t new_tail = HM_LRU_NONE;
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  228            
  0            
  0            
  66            
  0            
659              
660 10142 100         while (old_idx != HM_LRU_NONE) {
  1468 100          
  726 0          
  0 0          
  0 0          
  0 100          
  726 100          
  1501 0          
  0 100          
  726 100          
  4269 0          
  0 0          
  0 100          
  726 0          
  0            
661 9439           uint32_t next_old = map->lru_next[old_idx];
  1364            
  660            
  0            
  0            
  0            
  660            
  1394            
  0            
  660            
  4041            
  0            
  0            
  660            
  0            
662 9439           uint32_t ni = old_to_new[old_idx];
  1364            
  660            
  0            
  0            
  0            
  660            
  1394            
  0            
  660            
  4041            
  0            
  0            
  660            
  0            
663 9439 50         if (ni != HM_LRU_NONE) {
  1364 50          
  660 0          
  0 0          
  0 0          
  0 50          
  660 50          
  1394 0          
  0 50          
  660 50          
  4041 0          
  0 0          
  0 50          
  660 0          
  0            
664 9439           new_lru_prev[ni] = prev_new;
  1364            
  660            
  0            
  0            
  0            
  660            
  1394            
  0            
  660            
  4041            
  0            
  0            
  660            
  0            
665 9439           new_lru_next[ni] = HM_LRU_NONE;
  1364            
  660            
  0            
  0            
  0            
  660            
  1394            
  0            
  660            
  4041            
  0            
  0            
  660            
  0            
666 9439 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          
  1394 0          
  0 100          
  660 100          
  4041 0          
  0 0          
  0 100          
  660 0          
  0            
667 703           else new_head = ni;
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  228            
  0            
  0            
  66            
  0            
668 9439           new_tail = ni;
  1364            
  660            
  0            
  0            
  0            
  660            
  1394            
  0            
  660            
  4041            
  0            
  0            
  660            
  0            
669 9439           prev_new = ni;
  1364            
  660            
  0            
  0            
  0            
  660            
  1394            
  0            
  660            
  4041            
  0            
  0            
  660            
  0            
670             }
671 9439           old_idx = next_old;
  1364            
  660            
  0            
  0            
  0            
  660            
  1394            
  0            
  660            
  4041            
  0            
  0            
  660            
  0            
672             }
673              
674 703           free(map->lru_prev); free(map->lru_next);
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  228            
  0            
  0            
  66            
  0            
675 703           map->lru_prev = new_lru_prev;
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  228            
  0            
  0            
  66            
  0            
676 703           map->lru_next = new_lru_next;
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  228            
  0            
  0            
  66            
  0            
677 703           map->lru_head = new_head;
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  228            
  0            
  0            
  66            
  0            
678 703           map->lru_tail = new_tail;
  104            
  66            
  0            
  0            
  0            
  66            
  107            
  0            
  66            
  228            
  0            
  0            
  66            
  0            
679 1523 50         } else if (map->lru_prev) {
  157 50          
  182 50          
  27 50          
  33 50          
  82 50          
  82 50          
  190 50          
  83 50          
  127 100          
  181 50          
  112 50          
  79 50          
  77 50          
  111            
680 1           free(map->lru_prev); free(map->lru_next);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
681 1           map->lru_prev = new_lru_prev;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
682 1           map->lru_next = new_lru_next;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
683             }
684 2226           free(old_to_new);
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
685              
686 2226 100         if (map->expires_at) {
  261 100          
  248 50          
  27 50          
  33 50          
  82 50          
  148 100          
  297 50          
  83 50          
  193 100          
  409 50          
  112 50          
  79 50          
  143 50          
  111            
687 103           free(map->expires_at);
  34            
  12            
  0            
  0            
  0            
  0            
  47            
  0            
  0            
  10            
  0            
  0            
  0            
  0            
688 103           map->expires_at = new_expires_at;
  34            
  12            
  0            
  0            
  0            
  0            
  47            
  0            
  0            
  10            
  0            
  0            
  0            
  0            
689             }
690              
691 2226           free(old_nodes);
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
692 2226           map->nodes = new_nodes;
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
693 2226           map->capacity = new_capacity;
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
694 2226           map->mask = new_mask;
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
695 2226           map->tombstones = 0;
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
696 2226           map->iter_pos = 0;
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
697 2226           return true;
  261            
  248            
  27            
  33            
  82            
  148            
  297            
  83            
  193            
  409            
  112            
  79            
  143            
  111            
698             }
699              
700 661           static bool HM_FN(resize)(HM_MAP_TYPE* map) {
  69            
  91            
  14            
  19            
  31            
  31            
  101            
  32            
  67            
  80            
  32            
  29            
  28            
  37            
701 661           return HM_FN(rehash_to)(map, map->capacity * 2);
  69            
  91            
  14            
  19            
  31            
  31            
  101            
  32            
  67            
  80            
  32            
  29            
  28            
  37            
702             }
703              
704 1560           static bool HM_FN(compact)(HM_MAP_TYPE* map) {
  192            
  157            
  13            
  14            
  51            
  117            
  196            
  50            
  126            
  325            
  80            
  50            
  115            
  74            
705 1560           return HM_FN(rehash_to)(map, map->capacity);
  192            
  157            
  13            
  14            
  51            
  117            
  196            
  50            
  126            
  325            
  80            
  50            
  115            
  74            
706             }
707              
708 20           static bool HM_FN(reserve)(HM_MAP_TYPE* map, size_t count) {
  0            
  1            
  0            
  0            
  0            
  0            
  4            
  1            
  0            
  13            
  1            
  0            
  0            
  0            
709 20 0         if (count > SIZE_MAX / 4) return false; /* overflow guard */
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  4 50          
  1 0          
  0 50          
  13 50          
  1 0          
  0 0          
  0 0          
  0            
710             /* Compute capacity for count entries at 75% load factor */
711 20           size_t needed = (count * 4 + 2) / 3;
  0            
  1            
  0            
  0            
  0            
  0            
  4            
  1            
  0            
  13            
  1            
  0            
  0            
  0            
712 20 0         if (needed <= map->capacity) return true;
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  4 50          
  1 0          
  0 100          
  13 50          
  1 0          
  0 0          
  0 0          
  0            
713             /* Round up to power of 2 */
714 5           size_t cap = map->capacity;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  4            
  0            
  0            
  0            
  0            
715 30 0         while (cap < needed) cap <<= 1;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  2 0          
  0 100          
  28 0          
  0 0          
  0 0          
  0 0          
  0            
716 5           return HM_FN(rehash_to)(map, cap);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  4            
  0            
  0            
  0            
  0            
717             }
718              
719             /* ---- find_node: find existing key or return empty/capacity (not found) ---- */
720              
721             #ifdef HM_KEY_IS_INT
722              
723 1351093           static inline size_t HM_FN(find_node)(const HM_MAP_TYPE* map, HM_INT_TYPE key) {
  130538            
  60025            
  100025            
  130017            
  130123            
  400259            
  200041            
  90018            
  110047            
724 1351093           size_t index = hm_hash_int64((int64_t)key) & map->mask;
  130538            
  60025            
  100025            
  130017            
  130123            
  400259            
  200041            
  90018            
  110047            
725 1351093           const size_t original_index = index;
  130538            
  60025            
  100025            
  130017            
  130123            
  400259            
  200041            
  90018            
  110047            
726 1351093           const HM_NODE_TYPE* nodes = map->nodes;
  130538            
  60025            
  100025            
  130017            
  130123            
  400259            
  200041            
  90018            
  110047            
727              
728             do {
729 1573260           HM_INT_TYPE k = nodes[index].key;
  152688            
  75351            
  118831            
  152089            
  152213            
  452908            
  229751            
  108610            
  130819            
730 1573260 100         if (k == key) return index;
  152688 100          
  75351 100          
  118831 100          
  152089 100          
  152213 100          
  452908 100          
  229751 100          
  108610 100          
  130819            
731 222252 100         if (k == HM_EMPTY_KEY) return index;
  22158 100          
  15331 100          
  18811 100          
  22076 100          
  22095 100          
  52680 100          
  29720 100          
  18597 100          
  20784            
732 222167           index = (index + 1) & map->mask;
  22150            
  15326            
  18806            
  22072            
  22090            
  52649            
  29710            
  18592            
  20772            
733 222167 50         } while (HM_LIKELY(index != original_index));
  22150 50          
  15326 50          
  18806 50          
  22072 50          
  22090 50          
  52649 50          
  29710 50          
  18592 50          
  20772            
734              
735 0           return map->capacity;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
736             }
737              
738             #else /* string keys */
739              
740 671246           static inline size_t HM_FN(find_node)(const HM_MAP_TYPE* map,
  130537            
  130041            
  130564            
  150061            
  130043            
741             const char* key, uint32_t key_len, uint32_t key_hash) {
742 671246           size_t index = (size_t)key_hash & map->mask;
  130537            
  130041            
  130564            
  150061            
  130043            
743 671246           const size_t original_index = index;
  130537            
  130041            
  130564            
  150061            
  130043            
744 671246           const HM_NODE_TYPE* nodes = map->nodes;
  130537            
  130041            
  130564            
  150061            
  130043            
745              
746             do {
747 786857 100         if (nodes[index].key == NULL) return index; /* empty */
  153678 100          
  153148 100          
  153709 100          
  173171 100          
  153151            
748 786810 100         if (nodes[index].key != HM_STR_TOMBSTONE &&
  153673 100          
  153139 100          
  153701 100          
  173156 100          
  153141            
749 749562 100         nodes[index].key_hash == key_hash &&
  146206 100          
  145702 100          
  146234 100          
  165717 100          
  145703            
750 671199 50         HM_UNPACK_LEN(nodes[index].key_len) == key_len &&
  130532 50          
  130032 50          
  130556 50          
  150046 50          
  130033            
751 671199 50         memcmp(nodes[index].key, key, key_len) == 0) {
  130532 50          
  130032 50          
  130556 50          
  150046 50          
  130033            
752 671199           return index; /* found */
  130532            
  130032            
  130556            
  150046            
  130033            
753             }
754 115611           index = (index + 1) & map->mask;
  23141            
  23107            
  23145            
  23110            
  23108            
755 115611 50         } while (HM_LIKELY(index != original_index));
  23141 50          
  23107 50          
  23145 50          
  23110 50          
  23108            
756              
757 0           return map->capacity;
  0            
  0            
  0            
  0            
  0            
758             }
759              
760             #endif
761              
762             /* ---- find_slot_for_insert ---- */
763              
764             #ifdef HM_KEY_IS_INT
765              
766 1003970           static inline size_t HM_FN(find_slot_for_insert)(HM_MAP_TYPE* map, HM_INT_TYPE key, bool* found) {
  82215            
  30071            
  50131            
  80599            
  81223            
  387747            
  150256            
  60496            
  81232            
767 1003970           size_t index = hm_hash_int64((int64_t)key) & map->mask;
  82215            
  30071            
  50131            
  80599            
  81223            
  387747            
  150256            
  60496            
  81232            
768 1003970           const size_t original_index = index;
  82215            
  30071            
  50131            
  80599            
  81223            
  387747            
  150256            
  60496            
  81232            
769 1003970           HM_NODE_TYPE* nodes = map->nodes;
  82215            
  30071            
  50131            
  80599            
  81223            
  387747            
  150256            
  60496            
  81232            
770 1003970           size_t first_tombstone = map->capacity;
  82215            
  30071            
  50131            
  80599            
  81223            
  387747            
  150256            
  60496            
  81232            
771              
772             do {
773 2836138           HM_INT_TYPE k = nodes[index].key;
  270753            
  108692            
  193375            
  266887            
  268580            
  918650            
  405880            
  182164            
  221157            
774 2836138 100         if (k == key) {
  270753 100          
  108692 100          
  193375 100          
  266887 100          
  268580 100          
  918650 100          
  405880 100          
  182164 100          
  221157            
775 41           *found = true;
  8            
  3            
  3            
  1            
  8            
  13            
  2            
  1            
  2            
776 41           return index;
  8            
  3            
  3            
  1            
  8            
  13            
  2            
  1            
  2            
777             }
778 2836097 100         if (k == HM_EMPTY_KEY) {
  270745 100          
  108689 100          
  193372 100          
  266886 100          
  268572 100          
  918637 100          
  405878 100          
  182163 100          
  221155            
779 1003929           *found = false;
  82207            
  30068            
  50128            
  80598            
  81215            
  387734            
  150254            
  60495            
  81230            
780 1003929 100         return (first_tombstone < map->capacity) ? first_tombstone : index;
  82207 100          
  30068 100          
  50128 100          
  80598 100          
  81215 100          
  387734 100          
  150254 100          
  60495 100          
  81230            
781             }
782 1832168 100         if (k == HM_TOMBSTONE_KEY && first_tombstone >= map->capacity) {
  188538 100          
  78621 100          
  143244 50          
  186288 100          
  187357 50          
  530903 100          
  255624 100          
  121668 100          
  139925 100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    50          
783 1824           first_tombstone = index;
  71            
  1            
  1            
  55            
  54            
  1579            
  4            
  54            
  5            
784             }
785 1832168           index = (index + 1) & map->mask;
  188538            
  78621            
  143244            
  186288            
  187357            
  530903            
  255624            
  121668            
  139925            
786 1832168 50         } while (index != original_index);
  188538 50          
  78621 50          
  143244 50          
  186288 50          
  187357 50          
  530903 50          
  255624 50          
  121668 50          
  139925            
787              
788 0           *found = false;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
789 0           return first_tombstone;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
790             }
791              
792             #else /* string keys */
793              
794 405409           static inline size_t HM_FN(find_slot_for_insert)(HM_MAP_TYPE* map,
  81931            
  80227            
  82845            
  80282            
  80124            
795             const char* key, uint32_t key_len,
796             uint32_t key_hash, bool* found) {
797 405409           size_t index = (size_t)key_hash & map->mask;
  81931            
  80227            
  82845            
  80282            
  80124            
798 405409           const size_t original_index = index;
  81931            
  80227            
  82845            
  80282            
  80124            
799 405409           HM_NODE_TYPE* nodes = map->nodes;
  81931            
  80227            
  82845            
  80282            
  80124            
800 405409           size_t first_tombstone = map->capacity;
  81931            
  80227            
  82845            
  80282            
  80124            
801              
802             do {
803 1263766 100         if (nodes[index].key == NULL) {
  254381 100          
  250966 100          
  256787 100          
  251050 100          
  250582            
804 405381           *found = false;
  81922            
  80226            
  82834            
  80276            
  80123            
805 405381 100         return (first_tombstone < map->capacity) ? first_tombstone : index;
  81922 100          
  80226 100          
  82834 100          
  80276 100          
  80123            
806             }
807 858385 100         if (nodes[index].key == HM_STR_TOMBSTONE) {
  172459 100          
  170740 100          
  173953 100          
  170774 100          
  170459            
808 280 100         if (first_tombstone >= map->capacity) first_tombstone = index;
  135 50          
  2 100          
  139 50          
  2 50          
  2            
809 858105 100         } else if (nodes[index].key_hash == key_hash &&
  172324 100          
  170738 100          
  173814 100          
  170772 100          
  170457            
810 28 50         HM_UNPACK_LEN(nodes[index].key_len) == key_len &&
  9 50          
  1 50          
  11 50          
  6 50          
  1            
811 28 50         memcmp(nodes[index].key, key, key_len) == 0) {
  9 50          
  1 50          
  11 50          
  6 50          
  1            
812 28           *found = true;
  9            
  1            
  11            
  6            
  1            
813 28           return index;
  9            
  1            
  11            
  6            
  1            
814             }
815 858357           index = (index + 1) & map->mask;
  172450            
  170739            
  173942            
  170768            
  170458            
816 858357 50         } while (index != original_index);
  172450 50          
  170739 50          
  173942 50          
  170768 50          
  170458            
817              
818 0           *found = false;
  0            
  0            
  0            
  0            
  0            
819 0           return first_tombstone;
  0            
  0            
  0            
  0            
  0            
820             }
821              
822             #endif
823              
824             /* ---- put ---- */
825              
826             #ifdef HM_KEY_IS_INT
827              
828 1000538           static bool HM_FN(put)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  82029            
  30068            
  50128            
  80420            
  81035            
  385056            
  150255            
  60316            
  81231            
829             #ifdef HM_VALUE_IS_STR
830             const char* value, uint32_t val_len, bool val_utf8,
831             #elif defined(HM_VALUE_IS_SV)
832             void* value,
833             #else
834             HM_INT_TYPE value,
835             #endif
836             uint32_t entry_ttl) {
837 1000538 50         if (!map || HM_IS_RESERVED_KEY(key)) return false;
  82029 100          
  30068 100          
  50128 50          
  80420 100          
  81035 100          
  385056 50          
  150255 100          
  60316 100          
  81231 50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
838              
839 1000520 100         HM_ENSURE_CAPACITY(map, false);
  82027 100          
  30066 50          
  50126 50          
  80418 50          
  81033 100          
  385054 50          
  150253 0          
  60314 0          
  81229 50          
    100          
    50          
    0          
    0          
    50          
    100          
    100          
    50          
    50          
    50          
    100          
    100          
    50          
    50          
    50          
    100          
    100          
    100          
    50          
    50          
    100          
    50          
    0          
    0          
    50          
    100          
    100          
    50          
    50          
    50          
    100          
    50          
    0          
    0          
    50          
840              
841             bool found;
842 1000520           size_t index = HM_FN(find_slot_for_insert)(map, key, &found);
  82027            
  30066            
  50126            
  80418            
  81033            
  385054            
  150253            
  60314            
  81229            
843 1000520 50         if (index >= map->capacity) return false;
  82027 50          
  30066 50          
  50126 50          
  80418 50          
  81033 50          
  385054 50          
  150253 50          
  60314 50          
  81229            
844              
845             /* LRU eviction: only on new insert at capacity */
846 1000520 100         if (!found && map->max_size > 0 && map->size >= map->max_size) {
  82027 100          
  30066 100          
  50126 100          
  80418 100          
  81033 100          
  385054 100          
  150253 100          
  60314 100          
  81229 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
847 3408           HM_FN(lru_evict_one)(map);
  183            
  2            
  2            
  181            
  181            
  2676            
  1            
  181            
  1            
848             /* Re-probe after eviction to find optimal insertion slot */
849 3408           index = HM_FN(find_slot_for_insert)(map, key, &found);
  183            
  2            
  2            
  181            
  181            
  2676            
  1            
  181            
  1            
850 3408 50         if (index >= map->capacity) return false;
  183 50          
  2 50          
  2 50          
  181 50          
  181 50          
  2676 50          
  1 50          
  181 50          
  1            
851             }
852              
853             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
854 1000520 100         HM_LAZY_ALLOC_EXPIRES(map, entry_ttl, false);
  82027 100          
  30066 50          
  50126 50          
  80418 0          
  81033 0          
  385054 50          
  150253 0          
  60314 0          
  81229 50          
    0          
    0          
    50          
    0          
    0          
    100          
    100          
    50          
    100          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
    0          
855              
856             #ifdef HM_VALUE_IS_STR
857             /* Pre-allocate value before modifying map state */
858 221765           char* new_val = NULL;
  80418            
  81033            
  60314            
859 221765           uint32_t new_val_len = 0;
  80418            
  81033            
  60314            
860 221765 50         if (value && val_len > 0) {
  80418 100          
  81033 50          
  60314 100          
    50          
    50          
861 221763           new_val = (char*)malloc(val_len + 1);
  80417            
  81032            
  60314            
862 221763 50         if (!new_val) return false;
  80417 50          
  81032 50          
  60314            
863 221763           memcpy(new_val, value, val_len);
  80417            
  81032            
  60314            
864 221763           new_val[val_len] = '\0';
  80417            
  81032            
  60314            
865 221763 100         new_val_len = HM_PACK_LEN(val_len, val_utf8);
  80417 100          
  81032 100          
  60314            
866 2 50         } else if (value) {
  1 50          
  1 0          
  0            
867 2           new_val = (char*)malloc(1);
  1            
  1            
  0            
868 2 50         if (!new_val) return false;
  1 50          
  1 0          
  0            
869 2           new_val[0] = '\0';
  1            
  1            
  0            
870 2 50         new_val_len = HM_PACK_LEN(0, val_utf8);
  1 50          
  1 0          
  0            
871             }
872             #endif
873              
874 1000520 100         if (found) {
  82027 100          
  30066 100          
  50126 100          
  80418 100          
  81033 100          
  385054 100          
  150253 100          
  60314 100          
  81229            
875             #ifdef HM_VALUE_IS_STR
876 7 50         if (map->nodes[index].value) free(map->nodes[index].value);
  1 50          
  5 50          
  1            
877             #elif defined(HM_VALUE_IS_SV)
878 10 50         if (map->nodes[index].value && map->free_value_fn)
  6 50          
  2 50          
  2 50          
    50          
    50          
879 10           map->free_value_fn(map->nodes[index].value);
  6            
  2            
  2            
880             #endif
881             } else {
882 1000493 100         if (HM_SLOT_IS_TOMBSTONE(&map->nodes[index])) {
  82021 50          
  30064 50          
  50124 100          
  80417 100          
  81028 100          
  385046 100          
  150252 100          
  60313 100          
  81228            
883 1058           map->tombstones--;
  62            
  0            
  0            
  47            
  46            
  848            
  4            
  46            
  5            
884             }
885 1000493           map->size++;
  82021            
  30064            
  50124            
  80417            
  81028            
  385046            
  150252            
  60313            
  81228            
886 1000493           map->nodes[index].key = key;
  82021            
  30064            
  50124            
  80417            
  81028            
  385046            
  150252            
  60313            
  81228            
887             }
888              
889             #ifdef HM_VALUE_IS_STR
890 221765           map->nodes[index].value = new_val;
  80418            
  81033            
  60314            
891 221765           map->nodes[index].val_len = new_val_len;
  80418            
  81033            
  60314            
892             #else
893 778755           map->nodes[index].value = value;
  82027            
  30066            
  50126            
  385054            
  150253            
  81229            
894             #endif
895              
896             /* LRU maintenance */
897 1000520 100         if (HM_UNLIKELY(map->lru_prev)) {
  82027 100          
  30066 100          
  50126 100          
  80418 100          
  81033 100          
  385054 100          
  150253 100          
  60314 100          
  81229            
898 4010 50         if (found) HM_FN(lru_promote)(map, (uint32_t)index);
  211 50          
  8 50          
  8 50          
  203 50          
  203 100          
  3166 50          
  4 50          
  203 50          
  4            
899 4007           else HM_FN(lru_push_front)(map, (uint32_t)index);
  211            
  8            
  8            
  203            
  203            
  3163            
  4            
  203            
  4            
900             }
901             /* TTL maintenance */
902 1000520 100         if (map->expires_at) {
  82027 100          
  30066 100          
  50126 50          
  80418 100          
  81033 100          
  385054 100          
  150253 50          
  60314 100          
  81229            
903 411 100         uint32_t ttl = entry_ttl > 0 ? entry_ttl : map->default_ttl;
  203 50          
  1 50          
  1 0          
  0 50          
  1 100          
  203 50          
  1 0          
  0 50          
  1            
904 411 50         if (ttl > 0)
  203 50          
  1 50          
  1 0          
  0 50          
  1 100          
  203 50          
  1 0          
  0 50          
  1            
905 408           map->expires_at[index] = hm_expiry_at(ttl);
  203            
  1            
  1            
  0            
  1            
  200            
  1            
  0            
  1            
906             else
907 3           map->expires_at[index] = 0;
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
  0            
908             }
909              
910 1000520           return true;
  82027            
  30066            
  50126            
  80418            
  81033            
  385054            
  150253            
  60314            
  81229            
911             }
912              
913             #else /* string keys */
914              
915 404690           static bool HM_FN(put)(HM_MAP_TYPE* map,
  81584            
  80225            
  82485            
  80273            
  80123            
916             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
917             #ifdef HM_VALUE_IS_STR
918             const char* value, uint32_t val_len, bool val_utf8,
919             #elif defined(HM_VALUE_IS_SV)
920             void* value,
921             #else
922             HM_INT_TYPE value,
923             #endif
924             uint32_t entry_ttl) {
925 404690 50         if (!map || !key) return false;
  81584 50          
  80225 50          
  82485 50          
  80273 50          
  80123 50          
    50          
    50          
    50          
    50          
926              
927 404690 100         HM_ENSURE_CAPACITY(map, false);
  81584 100          
  80225 100          
  82485 50          
  80273 50          
  80123 100          
    50          
    0          
    0          
    50          
    100          
    100          
    100          
    50          
    50          
    100          
    50          
    0          
    0          
    50          
    100          
    50          
    0          
    0          
    50          
928              
929             bool found;
930 404690           size_t index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, &found);
  81584            
  80225            
  82485            
  80273            
  80123            
931 404690 50         if (index >= map->capacity) return false;
  81584 50          
  80225 50          
  82485 50          
  80273 50          
  80123            
932              
933             /* LRU eviction: only on new insert at capacity */
934 404690 100         if (!found && map->max_size > 0 && map->size >= map->max_size) {
  81584 100          
  80225 100          
  82485 100          
  80273 100          
  80123 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
935 696           HM_FN(lru_evict_one)(map);
  342            
  1            
  351            
  1            
  1            
936             /* Re-probe after eviction to find optimal insertion slot */
937 696           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, &found);
  342            
  1            
  351            
  1            
  1            
938 696 50         if (index >= map->capacity) return false;
  342 50          
  1 50          
  351 50          
  1 50          
  1            
939             }
940              
941             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
942 404690 100         HM_LAZY_ALLOC_EXPIRES(map, entry_ttl, false);
  81584 50          
  80225 50          
  82485 100          
  80273 50          
  80123 50          
    100          
    100          
    50          
    50          
    0          
    0          
    100          
    50          
    50          
943              
944             #ifdef HM_VALUE_IS_STR
945             /* Pre-allocate value before modifying map state */
946 82485           char* new_val = NULL;
  82485            
947 82485           uint32_t new_val_len = 0;
  82485            
948 82485 50         if (value && val_len > 0) {
  82485 100          
949 82484           new_val = (char*)malloc(val_len + 1);
  82484            
950 82484 50         if (!new_val) return false;
  82484            
951 82484           memcpy(new_val, value, val_len);
  82484            
952 82484           new_val[val_len] = '\0';
  82484            
953 82484 100         new_val_len = HM_PACK_LEN(val_len, val_utf8);
  82484            
954 1 50         } else if (value) {
  1            
955 1           new_val = (char*)malloc(1);
  1            
956 1 50         if (!new_val) return false;
  1            
957 1           new_val[0] = '\0';
  1            
958 1 50         new_val_len = HM_PACK_LEN(0, val_utf8);
  1            
959             }
960             #endif
961              
962 404690 100         if (found) {
  81584 100          
  80225 100          
  82485 100          
  80273 100          
  80123            
963             #ifdef HM_VALUE_IS_STR
964 8 50         if (map->nodes[index].value) free(map->nodes[index].value);
  8            
965             #elif defined(HM_VALUE_IS_SV)
966 7 50         if (map->nodes[index].value && map->free_value_fn)
  7 50          
967 7           map->free_value_fn(map->nodes[index].value);
  7            
968             #endif
969 20 100         map->nodes[index].key_len = HM_PACK_LEN(key_len, key_utf8);
  7 50          
  1 100          
  8 100          
  3 50          
  1            
970             } else {
971 404670           char* new_key = (char*)malloc(key_len + 1);
  81577            
  80224            
  82477            
  80270            
  80122            
972 404670 50         if (!new_key) {
  81577 50          
  80224 50          
  82477 50          
  80270 50          
  80122            
973             #ifdef HM_VALUE_IS_STR
974 0           free(new_val);
  0            
975             #endif
976 0           return false;
  0            
  0            
  0            
  0            
  0            
977             }
978 404670           memcpy(new_key, key, key_len);
  81577            
  80224            
  82477            
  80270            
  80122            
979 404670           new_key[key_len] = '\0';
  81577            
  80224            
  82477            
  80270            
  80122            
980 404670 100         if (HM_SLOT_IS_TOMBSTONE(&map->nodes[index])) {
  81577 100          
  80224 100          
  82477 100          
  80270 100          
  80122            
981 177           map->tombstones--;
  84            
  2            
  87            
  2            
  2            
982             }
983 404670           map->size++;
  81577            
  80224            
  82477            
  80270            
  80122            
984 404670           map->nodes[index].key = new_key;
  81577            
  80224            
  82477            
  80270            
  80122            
985 404670 100         map->nodes[index].key_len = HM_PACK_LEN(key_len, key_utf8);
  81577 100          
  80224 100          
  82477 100          
  80270 100          
  80122            
986 404670           map->nodes[index].key_hash = key_hash;
  81577            
  80224            
  82477            
  80270            
  80122            
987             }
988              
989             #ifdef HM_VALUE_IS_STR
990 82485           map->nodes[index].value = new_val;
  82485            
991 82485           map->nodes[index].val_len = new_val_len;
  82485            
992             #else
993 322205           map->nodes[index].value = value;
  81584            
  80225            
  80273            
  80123            
994             #endif
995              
996             /* LRU maintenance */
997 404690 100         if (HM_UNLIKELY(map->lru_prev)) {
  81584 100          
  80225 100          
  82485 100          
  80273 100          
  80123            
998 849 50         if (found) HM_FN(lru_promote)(map, (uint32_t)index);
  409 50          
  4 50          
  426 50          
  5 50          
  5            
999 849           else HM_FN(lru_push_front)(map, (uint32_t)index);
  409            
  4            
  426            
  5            
  5            
1000             }
1001             /* TTL maintenance */
1002 404690 100         if (map->expires_at) {
  81584 100          
  80225 100          
  82485 100          
  80273 100          
  80123            
1003 622 100         uint32_t ttl = entry_ttl > 0 ? entry_ttl : map->default_ttl;
  203 50          
  1 100          
  415 50          
  1 100          
  2            
1004 622 50         if (ttl > 0)
  203 50          
  1 100          
  415 50          
  1 50          
  2            
1005 620           map->expires_at[index] = hm_expiry_at(ttl);
  203            
  1            
  413            
  1            
  2            
1006             else
1007 2           map->expires_at[index] = 0;
  0            
  0            
  2            
  0            
  0            
1008             }
1009              
1010 404690           return true;
  81584            
  80225            
  82485            
  80273            
  80123            
1011             }
1012              
1013             #endif /* HM_KEY_IS_INT */
1014              
1015             /* ---- get ---- */
1016              
1017             #ifdef HM_KEY_IS_INT
1018              
1019             #ifdef HM_VALUE_IS_STR
1020 130039           static bool HM_FN(get)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  50011            
  50017            
  30011            
1021             const char** out_value, uint32_t* out_len, bool* out_utf8) {
1022 130039 50         if (!map || HM_IS_RESERVED_KEY(key)) return false;
  50011 100          
  50017 50          
  30011 50          
    100          
    50          
    50          
    100          
    50          
1023              
1024 130036           size_t index = HM_FN(find_node)(map, key);
  50010            
  50016            
  30010            
1025 130036 50         if (index >= map->capacity || map->nodes[index].key == HM_EMPTY_KEY) return false;
  50010 100          
  50016 50          
  30010 100          
    50          
    100          
1026              
1027 130028 50         HM_TTL_CHECK_EXPIRED(map, index, false);
  50008 0          
  50013 0          
  30007 100          
    50          
    100          
    50          
    0          
    0          
1028              
1029 130027 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  50008 100          
  50012 50          
  30007            
1030              
1031 130027           *out_value = map->nodes[index].value;
  50008            
  50012            
  30007            
1032 130027           *out_len = HM_UNPACK_LEN(map->nodes[index].val_len);
  50008            
  50012            
  30007            
1033 130027           *out_utf8 = HM_UNPACK_UTF8(map->nodes[index].val_len);
  50008            
  50012            
  30007            
1034 130027           return true;
  50008            
  50012            
  30007            
1035             }
1036             #elif defined(HM_VALUE_IS_SV)
1037 130058           static bool HM_FN(get)(HM_MAP_TYPE* map, HM_INT_TYPE key, void** out_value) {
  50026            
  30016            
  50016            
1038 130058 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  50026 50          
  30016 100          
  50016 50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
1039              
1040 130057           size_t index = HM_FN(find_node)(map, key);
  50025            
  30016            
  50016            
1041 130057 50         if (index >= map->capacity || map->nodes[index].key == HM_EMPTY_KEY) return false;
  50025 100          
  30016 50          
  50016 100          
    50          
    100          
1042              
1043 130047 100         HM_TTL_CHECK_EXPIRED(map, index, false);
  50021 50          
  30013 50          
  50013 100          
    50          
    50          
    100          
    50          
    50          
1044              
1045 130043 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  50019 100          
  30012 100          
  50012            
1046              
1047 130043           *out_value = map->nodes[index].value;
  50019            
  30012            
  50012            
1048 130043           return true;
  50019            
  30012            
  50012            
1049             }
1050             #else
1051 180215           static bool HM_FN(get)(HM_MAP_TYPE* map, HM_INT_TYPE key, HM_INT_TYPE* out_value) {
  100192            
  50010            
  30013            
1052 180215 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  100192 50          
  50010 100          
  30013 50          
    50          
    50          
    100          
    50          
    50          
    50          
    100          
    50          
1053              
1054 180212           size_t index = HM_FN(find_node)(map, key);
  100191            
  50009            
  30012            
1055 180212 50         if (index >= map->capacity || map->nodes[index].key == HM_EMPTY_KEY) return false;
  100191 100          
  50009 50          
  30012 100          
    50          
    100          
1056              
1057 180190 100         HM_TTL_CHECK_EXPIRED(map, index, false);
  100176 100          
  50006 100          
  30008 100          
    50          
    100          
    100          
    50          
    100          
1058              
1059 180174 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  100162 50          
  50005 50          
  30007            
1060              
1061 180174           *out_value = map->nodes[index].value;
  100162            
  50005            
  30007            
1062 180174           return true;
  100162            
  50005            
  30007            
1063             }
1064             #endif
1065              
1066             #else /* string keys */
1067              
1068             #ifdef HM_VALUE_IS_STR
1069 50046           static bool HM_FN(get)(HM_MAP_TYPE* map,
1070             const char* key, uint32_t key_len, uint32_t key_hash,
1071             const char** out_value, uint32_t* out_len, bool* out_utf8) {
1072 50046 50         if (!map || !key) return false;
    50          
1073              
1074 50046           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
1075 50046 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
    100          
1076              
1077 50041 100         HM_TTL_CHECK_EXPIRED(map, index, false);
    100          
    100          
1078              
1079 50038 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
1080              
1081 50038           *out_value = map->nodes[index].value;
1082 50038           *out_len = HM_UNPACK_LEN(map->nodes[index].val_len);
1083 50038           *out_utf8 = HM_UNPACK_UTF8(map->nodes[index].val_len);
1084 50038           return true;
1085             }
1086             #elif defined(HM_VALUE_IS_SV)
1087 50026           static bool HM_FN(get)(HM_MAP_TYPE* map,
1088             const char* key, uint32_t key_len, uint32_t key_hash,
1089             void** out_value) {
1090 50026 50         if (!map || !key || !out_value) return false;
    50          
    50          
1091              
1092 50026           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
1093 50026 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
    100          
1094              
1095 50023 100         HM_TTL_CHECK_EXPIRED(map, index, false);
    50          
    50          
1096              
1097 50021 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
1098              
1099 50021           *out_value = map->nodes[index].value;
1100 50021           return true;
1101             }
1102             #else
1103 150044           static bool HM_FN(get)(HM_MAP_TYPE* map,
  50011            
  50022            
  50011            
1104             const char* key, uint32_t key_len, uint32_t key_hash,
1105             HM_INT_TYPE* out_value) {
1106 150044 50         if (!map || !key || !out_value) return false;
  50011 50          
  50022 50          
  50011 50          
    50          
    50          
    50          
    50          
    50          
1107              
1108 150044           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  50011            
  50022            
  50011            
1109 150044 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
  50011 100          
  50022 50          
  50011 100          
    50          
    100          
1110              
1111 150038 100         HM_TTL_CHECK_EXPIRED(map, index, false);
  50009 50          
  50020 100          
  50009 100          
    50          
    50          
    100          
    50          
    100          
1112              
1113 150034 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  50008 100          
  50019 100          
  50007            
1114              
1115 150034           *out_value = map->nodes[index].value;
  50008            
  50019            
  50007            
1116 150034           return true;
  50008            
  50019            
  50007            
1117             }
1118             #endif
1119              
1120             #endif /* HM_KEY_IS_INT */
1121              
1122             /* ---- exists ---- */
1123              
1124             #ifdef HM_KEY_IS_INT
1125              
1126 37           static bool HM_FN(exists)(HM_MAP_TYPE* map, HM_INT_TYPE key) {
  4            
  3            
  3            
  3            
  3            
  10            
  3            
  4            
  4            
1127 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          
1128 31           size_t index = HM_FN(find_node)(map, key);
  4            
  3            
  3            
  2            
  2            
  9            
  2            
  3            
  3            
1129 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          
1130              
1131 20 50         HM_TTL_CHECK_EXPIRED(map, index, false);
  3 0          
  2 0          
  2 50          
  1 0          
  1 0          
  6 50          
  1 0          
  2 0          
  2 50          
    0          
    0          
    50          
    0          
    0          
    100          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
1132 20           return true;
  3            
  2            
  2            
  1            
  1            
  6            
  1            
  2            
  2            
1133             }
1134              
1135             #else
1136              
1137 15           static bool HM_FN(exists)(HM_MAP_TYPE* map,
  5            
  2            
  2            
  2            
  4            
1138             const char* key, uint32_t key_len, uint32_t key_hash) {
1139 15 50         if (!map || !key) return false;
  5 50          
  2 50          
  2 50          
  2 50          
  4 50          
    50          
    50          
    50          
    50          
1140 15           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  5            
  2            
  2            
  2            
  4            
1141 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          
1142              
1143 10 50         HM_TTL_CHECK_EXPIRED(map, index, false);
  4 0          
  1 0          
  1 50          
  1 0          
  3 0          
    50          
    0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
1144 10           return true;
  4            
  1            
  1            
  1            
  3            
1145             }
1146              
1147             #endif
1148              
1149             /* ---- remove ---- */
1150              
1151             #ifdef HM_KEY_IS_INT
1152              
1153 890666           static bool HM_FN(remove)(HM_MAP_TYPE* map, HM_INT_TYPE key) {
  80505            
  30005            
  50005            
  80006            
  80106            
  280022            
  150004            
  60006            
  80007            
1154 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          
1155              
1156 890657           size_t index = HM_FN(find_node)(map, key);
  80505            
  30005            
  50005            
  80004            
  80104            
  280021            
  150003            
  60004            
  80006            
1157 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          
1158              
1159 890648 50         HM_TTL_CHECK_EXPIRED(map, index, true);
  80504 0          
  30004 0          
  50004 50          
  80003 0          
  80103 0          
  280020 50          
  150002 0          
  60003 0          
  80005 50          
    0          
    0          
    50          
    0          
    0          
    100          
    50          
    100          
    50          
    0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
1160              
1161 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            
1162 890647           HM_FN(tombstone_at)(map, index);
  80504            
  30004            
  50004            
  80003            
  80103            
  280019            
  150002            
  60003            
  80005            
1163 890647 100         HM_MAYBE_COMPACT(map);
  80504 100          
  30004 100          
  50004 50          
  80003 100          
  80103 100          
  280019 50          
  150002 100          
  60003 100          
  80005 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
1164 890647           return true;
  80504            
  30004            
  50004            
  80003            
  80103            
  280019            
  150002            
  60003            
  80005            
1165             }
1166              
1167             #else /* string keys */
1168              
1169 401031           static bool HM_FN(remove)(HM_MAP_TYPE* map,
  80505            
  80005            
  80510            
  80006            
  80005            
1170             const char* key, uint32_t key_len, uint32_t key_hash) {
1171 401031 50         if (!map || !key) return false;
  80505 50          
  80005 50          
  80510 50          
  80006 50          
  80005 50          
    50          
    50          
    50          
    50          
1172              
1173 401031           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  80505            
  80005            
  80510            
  80006            
  80005            
1174 401031 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
  80505 100          
  80005 50          
  80510 100          
  80006 50          
  80005 100          
    50          
    100          
    50          
    100          
1175              
1176 401026 50         HM_TTL_CHECK_EXPIRED(map, index, true);
  80504 0          
  80004 0          
  80509 50          
  80005 0          
  80004 0          
    50          
    0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
1177              
1178 401026 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_unlink)(map, (uint32_t)index);
  80504 50          
  80004 50          
  80509 50          
  80005 50          
  80004            
1179 401026           HM_FN(tombstone_at)(map, index);
  80504            
  80004            
  80509            
  80005            
  80004            
1180 401026 100         HM_MAYBE_COMPACT(map);
  80504 100          
  80004 100          
  80509 100          
  80005 100          
  80004 100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
1181 401026           return true;
  80504            
  80004            
  80509            
  80005            
  80004            
1182             }
1183              
1184             #endif
1185              
1186             /* ---- Take (remove and return value) ---- */
1187              
1188             #ifdef HM_KEY_IS_INT
1189              
1190             #ifdef HM_VALUE_IS_STR
1191 3           static bool HM_FN(take)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  1            
  1            
  1            
1192             const char** out_value, uint32_t* out_len, bool* out_utf8) {
1193 3 50         if (!map || HM_IS_RESERVED_KEY(key)) return false;
  1 50          
  1 50          
  1 50          
    50          
    50          
    50          
    50          
    50          
1194              
1195 3           size_t index = HM_FN(find_node)(map, key);
  1            
  1            
  1            
1196 3 50         if (index >= map->capacity || map->nodes[index].key != key) return false;
  1 50          
  1 50          
  1 50          
    50          
    50          
1197              
1198 3 50         HM_TTL_CHECK_EXPIRED(map, index, true);
  1 0          
  1 0          
  1 50          
    0          
    0          
    50          
    0          
    0          
1199              
1200 3           *out_value = map->nodes[index].value;
  1            
  1            
  1            
1201 3           *out_len = HM_UNPACK_LEN(map->nodes[index].val_len);
  1            
  1            
  1            
1202 3           *out_utf8 = HM_UNPACK_UTF8(map->nodes[index].val_len);
  1            
  1            
  1            
1203 3           map->nodes[index].value = NULL; /* prevent free_node from freeing */
  1            
  1            
  1            
1204              
1205 3 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_unlink)(map, (uint32_t)index);
  1 50          
  1 50          
  1            
1206 3           HM_FN(tombstone_at)(map, index);
  1            
  1            
  1            
1207 3 50         HM_MAYBE_COMPACT(map);
  1 50          
  1 0          
  1 50          
    50          
    0          
    50          
    50          
    0          
1208 3           return true;
  1            
  1            
  1            
1209             }
1210             #elif defined(HM_VALUE_IS_SV)
1211 4           static bool HM_FN(take)(HM_MAP_TYPE* map, HM_INT_TYPE key, void** out_value) {
  2            
  1            
  1            
1212 4 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  2 50          
  1 50          
  1 50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
1213              
1214 4           size_t index = HM_FN(find_node)(map, key);
  2            
  1            
  1            
1215 4 50         if (index >= map->capacity || map->nodes[index].key != key) return false;
  2 100          
  1 50          
  1 50          
    50          
    50          
1216              
1217 3 50         HM_TTL_CHECK_EXPIRED(map, index, true);
  1 0          
  1 0          
  1 50          
    0          
    0          
    50          
    0          
    0          
1218              
1219 3           *out_value = map->nodes[index].value;
  1            
  1            
  1            
1220 3           map->nodes[index].value = NULL; /* transfer ownership to caller */
  1            
  1            
  1            
1221              
1222 3 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_unlink)(map, (uint32_t)index);
  1 50          
  1 50          
  1            
1223 3           HM_FN(tombstone_at)(map, index);
  1            
  1            
  1            
1224 3 50         HM_MAYBE_COMPACT(map);
  1 50          
  1 0          
  1 50          
    50          
    0          
    50          
    50          
    0          
1225 3           return true;
  1            
  1            
  1            
1226             }
1227             #else
1228 7           static bool HM_FN(take)(HM_MAP_TYPE* map, HM_INT_TYPE key, HM_INT_TYPE* out_value) {
  3            
  2            
  2            
1229 7 50         if (!map || !out_value || HM_IS_RESERVED_KEY(key)) return false;
  3 50          
  2 50          
  2 50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
1230              
1231 7           size_t index = HM_FN(find_node)(map, key);
  3            
  2            
  2            
1232 7 50         if (index >= map->capacity || map->nodes[index].key != key) return false;
  3 100          
  2 50          
  2 50          
    50          
    50          
1233              
1234 6 100         HM_TTL_CHECK_EXPIRED(map, index, true);
  2 50          
  2 50          
  2 50          
    0          
    0          
    50          
    0          
    0          
1235              
1236 5           *out_value = map->nodes[index].value;
  1            
  2            
  2            
1237              
1238 5 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_unlink)(map, (uint32_t)index);
  1 50          
  2 50          
  2            
1239 5           HM_FN(tombstone_at)(map, index);
  1            
  2            
  2            
1240 5 50         HM_MAYBE_COMPACT(map);
  1 50          
  2 0          
  2 50          
    100          
    50          
    50          
    100          
    50          
1241 5           return true;
  1            
  2            
  2            
1242             }
1243             #endif
1244              
1245             #else /* string keys */
1246              
1247             #ifdef HM_VALUE_IS_STR
1248 1           static bool HM_FN(take)(HM_MAP_TYPE* map,
1249             const char* key, uint32_t key_len, uint32_t key_hash,
1250             const char** out_value, uint32_t* out_len, bool* out_utf8) {
1251 1 50         if (!map || !key) return false;
    50          
1252              
1253 1           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
1254 1 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
    50          
1255              
1256 1 50         HM_TTL_CHECK_EXPIRED(map, index, true);
    0          
    0          
1257              
1258 1           *out_value = map->nodes[index].value;
1259 1           *out_len = HM_UNPACK_LEN(map->nodes[index].val_len);
1260 1           *out_utf8 = HM_UNPACK_UTF8(map->nodes[index].val_len);
1261 1           map->nodes[index].value = NULL;
1262              
1263 1 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_unlink)(map, (uint32_t)index);
1264 1           HM_FN(tombstone_at)(map, index);
1265 1 50         HM_MAYBE_COMPACT(map);
    50          
    0          
1266 1           return true;
1267             }
1268             #elif defined(HM_VALUE_IS_SV)
1269 1           static bool HM_FN(take)(HM_MAP_TYPE* map,
1270             const char* key, uint32_t key_len, uint32_t key_hash,
1271             void** out_value) {
1272 1 50         if (!map || !key || !out_value) return false;
    50          
    50          
1273              
1274 1           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
1275 1 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
    50          
1276              
1277 1 50         HM_TTL_CHECK_EXPIRED(map, index, true);
    0          
    0          
1278              
1279 1           *out_value = map->nodes[index].value;
1280 1           map->nodes[index].value = NULL;
1281              
1282 1 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_unlink)(map, (uint32_t)index);
1283 1           HM_FN(tombstone_at)(map, index);
1284 1 50         HM_MAYBE_COMPACT(map);
    50          
    0          
1285 1           return true;
1286             }
1287             #else
1288 3           static bool HM_FN(take)(HM_MAP_TYPE* map,
  1            
  1            
  1            
1289             const char* key, uint32_t key_len, uint32_t key_hash,
1290             HM_INT_TYPE* out_value) {
1291 3 50         if (!map || !key || !out_value) return false;
  1 50          
  1 50          
  1 50          
    50          
    50          
    50          
    50          
    50          
1292              
1293 3           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  1            
  1            
  1            
1294 3 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
  1 50          
  1 50          
  1 50          
    50          
    50          
1295              
1296 3 50         HM_TTL_CHECK_EXPIRED(map, index, true);
  1 0          
  1 0          
  1 50          
    0          
    0          
    50          
    0          
    0          
1297              
1298 3           *out_value = map->nodes[index].value;
  1            
  1            
  1            
1299              
1300 3 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_unlink)(map, (uint32_t)index);
  1 50          
  1 50          
  1            
1301 3           HM_FN(tombstone_at)(map, index);
  1            
  1            
  1            
1302 3 50         HM_MAYBE_COMPACT(map);
  1 50          
  1 0          
  1 50          
    50          
    0          
    50          
    50          
    0          
1303 3           return true;
  1            
  1            
  1            
1304             }
1305             #endif
1306              
1307             #endif /* HM_KEY_IS_INT */
1308              
1309             /* ---- persist: remove TTL from a key ---- */
1310              
1311             #ifdef HM_KEY_IS_INT
1312 4           static bool HM_FN(persist)(HM_MAP_TYPE* map, HM_INT_TYPE key) {
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
  0            
1313 4 0         if (!map || HM_IS_RESERVED_KEY(key)) return false;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  4 0          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1314 4           size_t index = HM_FN(find_node)(map, key);
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
  0            
1315 4 0         if (index >= map->capacity || map->nodes[index].key == HM_EMPTY_KEY) return false;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  4 0          
  0 0          
  0 0          
  0 0          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
1316 4 0         HM_TTL_CHECK_EXPIRED(map, index, true);
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  4 0          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1317 3 0         if (map->expires_at) map->expires_at[index] = 0;
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0 0          
  0            
1318 3           return true;
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
  0            
1319             }
1320             #else
1321 3           static bool HM_FN(persist)(HM_MAP_TYPE* map,
  0            
  0            
  3            
  0            
  0            
1322             const char* key, uint32_t key_len, uint32_t key_hash) {
1323 3 0         if (!map || !key) return false;
  0 0          
  0 0          
  3 0          
  0 50          
  0 50          
    0          
    0          
    0          
    0          
1324 3           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  0            
  0            
  3            
  0            
  0            
1325 3 0         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
  0 0          
  0 0          
  3 0          
  0 50          
  0 50          
    0          
    0          
    0          
    0          
1326 3 0         HM_TTL_CHECK_EXPIRED(map, index, true);
  0 0          
  0 0          
  3 0          
  0 0          
  0 0          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
1327 3 0         if (map->expires_at) map->expires_at[index] = 0;
  0 0          
  0 50          
  3 0          
  0 0          
  0            
1328 3           return true;
  0            
  0            
  3            
  0            
  0            
1329             }
1330             #endif
1331              
1332             /* ---- swap: replace value, return old value ---- */
1333              
1334             #ifdef HM_KEY_IS_INT
1335              
1336             #ifdef HM_VALUE_IS_STR
1337 0           static bool HM_FN(swap)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  0            
  0            
  0            
1338             const char* new_val, uint32_t new_len, bool new_utf8,
1339             const char** out_old, uint32_t* out_old_len, bool* out_old_utf8) {
1340 0 0         if (!map || HM_IS_RESERVED_KEY(key)) return false;
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
1341 0           size_t index = HM_FN(find_node)(map, key);
  0            
  0            
  0            
1342 0 0         if (index >= map->capacity || map->nodes[index].key != key) return false;
  0 0          
  0 0          
  0 0          
    0          
    0          
1343 0 0         HM_TTL_CHECK_EXPIRED(map, index, true);
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
1344             /* Extract old */
1345 0           *out_old = map->nodes[index].value;
  0            
  0            
  0            
1346 0           *out_old_len = HM_UNPACK_LEN(map->nodes[index].val_len);
  0            
  0            
  0            
1347 0           *out_old_utf8 = HM_UNPACK_UTF8(map->nodes[index].val_len);
  0            
  0            
  0            
1348             /* Store new */
1349 0 0         if (new_val && new_len > 0) {
  0 0          
  0 0          
  0 0          
    0          
    0          
1350 0           char* buf = (char*)malloc(new_len + 1);
  0            
  0            
  0            
1351 0 0         if (!buf) return false;
  0 0          
  0 0          
  0            
1352 0           memcpy(buf, new_val, new_len);
  0            
  0            
  0            
1353 0           buf[new_len] = '\0';
  0            
  0            
  0            
1354 0           map->nodes[index].value = buf;
  0            
  0            
  0            
1355 0 0         } else if (new_val) {
  0 0          
  0 0          
  0            
1356 0           char* buf = (char*)malloc(1);
  0            
  0            
  0            
1357 0 0         if (!buf) return false;
  0 0          
  0 0          
  0            
1358 0           buf[0] = '\0';
  0            
  0            
  0            
1359 0           map->nodes[index].value = buf;
  0            
  0            
  0            
1360             } else {
1361 0           map->nodes[index].value = NULL;
  0            
  0            
  0            
1362             }
1363 0 0         map->nodes[index].val_len = HM_PACK_LEN(new_len, new_utf8);
  0 0          
  0 0          
  0            
1364 0 0         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  0 0          
  0 0          
  0            
1365 0           return true;
  0            
  0            
  0            
1366             }
1367             #elif defined(HM_VALUE_IS_SV)
1368 2           static bool HM_FN(swap)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  2            
  0            
  0            
1369             void* new_val, void** out_old) {
1370 2 50         if (!map || !out_old || HM_IS_RESERVED_KEY(key)) return false;
  2 50          
  0 50          
  0 50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1371 2           size_t index = HM_FN(find_node)(map, key);
  2            
  0            
  0            
1372 2 50         if (index >= map->capacity || map->nodes[index].key != key) return false;
  2 100          
  0 0          
  0 0          
    0          
    0          
1373 1 50         HM_TTL_CHECK_EXPIRED(map, index, true);
  1 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
1374 1           *out_old = map->nodes[index].value;
  1            
  0            
  0            
1375 1           map->nodes[index].value = new_val;
  1            
  0            
  0            
1376 1 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  1 0          
  0 0          
  0            
1377 1           return true;
  1            
  0            
  0            
1378             }
1379             #else
1380 2           static bool HM_FN(swap)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  2            
  0            
  0            
1381             HM_INT_TYPE new_val, HM_INT_TYPE* out_old) {
1382 2 50         if (!map || !out_old || HM_IS_RESERVED_KEY(key)) return false;
  2 50          
  0 50          
  0 50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1383 2           size_t index = HM_FN(find_node)(map, key);
  2            
  0            
  0            
1384 2 50         if (index >= map->capacity || map->nodes[index].key != key) return false;
  2 100          
  0 0          
  0 0          
    0          
    0          
1385 1 50         HM_TTL_CHECK_EXPIRED(map, index, true);
  1 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
1386 1           *out_old = map->nodes[index].value;
  1            
  0            
  0            
1387 1           map->nodes[index].value = new_val;
  1            
  0            
  0            
1388 1 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  1 0          
  0 0          
  0            
1389 1           return true;
  1            
  0            
  0            
1390             }
1391             #endif
1392              
1393             #else /* string keys */
1394              
1395             #ifdef HM_VALUE_IS_STR
1396 2           static bool HM_FN(swap)(HM_MAP_TYPE* map,
1397             const char* key, uint32_t key_len, uint32_t key_hash,
1398             const char* new_val, uint32_t new_len, bool new_utf8,
1399             const char** out_old, uint32_t* out_old_len, bool* out_old_utf8) {
1400 2 50         if (!map || !key) return false;
    50          
1401 2           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
1402 2 50         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
    100          
1403 1 50         HM_TTL_CHECK_EXPIRED(map, index, true);
    0          
    0          
1404 1           *out_old = map->nodes[index].value;
1405 1           *out_old_len = HM_UNPACK_LEN(map->nodes[index].val_len);
1406 1           *out_old_utf8 = HM_UNPACK_UTF8(map->nodes[index].val_len);
1407 1 50         if (new_val && new_len > 0) {
    50          
1408 1           char* buf = (char*)malloc(new_len + 1);
1409 1 50         if (!buf) return false;
1410 1           memcpy(buf, new_val, new_len);
1411 1           buf[new_len] = '\0';
1412 1           map->nodes[index].value = buf;
1413 0 0         } else if (new_val) {
1414 0           char* buf = (char*)malloc(1);
1415 0 0         if (!buf) return false;
1416 0           buf[0] = '\0';
1417 0           map->nodes[index].value = buf;
1418             } else {
1419 0           map->nodes[index].value = NULL;
1420             }
1421 1 50         map->nodes[index].val_len = HM_PACK_LEN(new_len, new_utf8);
1422 1 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
1423 1           return true;
1424             }
1425             #elif defined(HM_VALUE_IS_SV)
1426 0           static bool HM_FN(swap)(HM_MAP_TYPE* map,
1427             const char* key, uint32_t key_len, uint32_t key_hash,
1428             void* new_val, void** out_old) {
1429 0 0         if (!map || !key || !out_old) return false;
    0          
    0          
1430 0           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
1431 0 0         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
    0          
1432 0 0         HM_TTL_CHECK_EXPIRED(map, index, true);
    0          
    0          
1433 0           *out_old = map->nodes[index].value;
1434 0           map->nodes[index].value = new_val;
1435 0 0         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
1436 0           return true;
1437             }
1438             #else
1439 0           static bool HM_FN(swap)(HM_MAP_TYPE* map,
  0            
  0            
  0            
1440             const char* key, uint32_t key_len, uint32_t key_hash,
1441             HM_INT_TYPE new_val, HM_INT_TYPE* out_old) {
1442 0 0         if (!map || !key || !out_old) return false;
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
1443 0           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  0            
  0            
  0            
1444 0 0         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
  0 0          
  0 0          
  0 0          
    0          
    0          
1445 0 0         HM_TTL_CHECK_EXPIRED(map, index, true);
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
1446 0           *out_old = map->nodes[index].value;
  0            
  0            
  0            
1447 0           map->nodes[index].value = new_val;
  0            
  0            
  0            
1448 0 0         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  0 0          
  0 0          
  0            
1449 0           return true;
  0            
  0            
  0            
1450             }
1451             #endif
1452              
1453             #endif /* HM_KEY_IS_INT */
1454              
1455             /* ---- cas: compare-and-swap (int values only) ---- */
1456              
1457             #if !defined(HM_VALUE_IS_STR) && !defined(HM_VALUE_IS_SV)
1458              
1459             #ifdef HM_KEY_IS_INT
1460 3           static bool HM_FN(cas)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  3            
  0            
  0            
1461             HM_INT_TYPE expected, HM_INT_TYPE new_val) {
1462 3 50         if (!map || HM_IS_RESERVED_KEY(key)) return false;
  3 50          
  0 50          
  0 0          
    0          
    0          
    0          
    0          
    0          
1463 3           size_t index = HM_FN(find_node)(map, key);
  3            
  0            
  0            
1464 3 50         if (index >= map->capacity || map->nodes[index].key != key) return false;
  3 100          
  0 0          
  0 0          
    0          
    0          
1465 2 50         HM_TTL_CHECK_EXPIRED(map, index, true);
  2 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
1466 2 100         if (map->nodes[index].value != expected) return false;
  2 0          
  0 0          
  0            
1467 1           map->nodes[index].value = new_val;
  1            
  0            
  0            
1468 1 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  1 0          
  0 0          
  0            
1469 1           return true;
  1            
  0            
  0            
1470             }
1471             #else
1472 1           static bool HM_FN(cas)(HM_MAP_TYPE* map,
  0            
  1            
  0            
1473             const char* key, uint32_t key_len, uint32_t key_hash,
1474             HM_INT_TYPE expected, HM_INT_TYPE new_val) {
1475 1 0         if (!map || !key) return false;
  0 0          
  1 50          
  0 50          
    0          
    0          
1476 1           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  0            
  1            
  0            
1477 1 0         if (index >= map->capacity || map->nodes[index].key == NULL) return false;
  0 0          
  1 50          
  0 50          
    0          
    0          
1478 1 0         HM_TTL_CHECK_EXPIRED(map, index, true);
  0 0          
  1 0          
  0 50          
    0          
    0          
    0          
    0          
    0          
1479 1 0         if (map->nodes[index].value != expected) return false;
  0 50          
  1 0          
  0            
1480 1           map->nodes[index].value = new_val;
  0            
  1            
  0            
1481 1 0         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  0 50          
  1 0          
  0            
1482 1           return true;
  0            
  1            
  0            
1483             }
1484             #endif
1485              
1486             #endif /* int values only */
1487              
1488             /* ---- Counter operations (int values only) ---- */
1489              
1490             #ifdef HM_HAS_COUNTERS
1491              
1492             #ifdef HM_KEY_IS_INT
1493              
1494 21           static inline size_t HM_FN(find_or_allocate)(HM_MAP_TYPE* map, HM_INT_TYPE key) {
  10            
  5            
  6            
1495 21           size_t index = hm_hash_int64((int64_t)key) & map->mask;
  10            
  5            
  6            
1496 21           const size_t original_index = index;
  10            
  5            
  6            
1497 21           HM_NODE_TYPE* nodes = map->nodes;
  10            
  5            
  6            
1498 21           size_t first_tombstone = map->capacity;
  10            
  5            
  6            
1499              
1500             do {
1501 29           HM_INT_TYPE k = nodes[index].key;
  15            
  6            
  8            
1502 29 50         if (k == key) return index;
  15 50          
  6 50          
  8            
1503 29 100         if (k == HM_EMPTY_KEY) {
  15 100          
  6 100          
  8            
1504 21 100         size_t target = (first_tombstone < map->capacity) ? first_tombstone : index;
  10 100          
  5 100          
  6            
1505 21 100         if (HM_SLOT_IS_TOMBSTONE(&nodes[target])) map->tombstones--;
  10 100          
  5 100          
  6            
1506 21           nodes[target].key = key;
  10            
  5            
  6            
1507 21           nodes[target].value = 0;
  10            
  5            
  6            
1508 21           map->size++;
  10            
  5            
  6            
1509 21           return target;
  10            
  5            
  6            
1510             }
1511 8 100         if (k == HM_TOMBSTONE_KEY && first_tombstone >= map->capacity) {
  5 50          
  1 50          
  2 50          
    50          
    50          
1512 5           first_tombstone = index;
  2            
  1            
  2            
1513             }
1514 8           index = (index + 1) & map->mask;
  5            
  1            
  2            
1515 8 50         } while (index != original_index);
  5 50          
  1 50          
  2            
1516              
1517 0 0         if (first_tombstone < map->capacity) {
  0 0          
  0 0          
  0            
1518 0           map->tombstones--;
  0            
  0            
  0            
1519 0           nodes[first_tombstone].key = key;
  0            
  0            
  0            
1520 0           nodes[first_tombstone].value = 0;
  0            
  0            
  0            
1521 0           map->size++;
  0            
  0            
  0            
1522 0           return first_tombstone;
  0            
  0            
  0            
1523             }
1524 0           return map->capacity;
  0            
  0            
  0            
1525             }
1526              
1527 15035           static bool HM_FN(increment)(HM_MAP_TYPE* map, HM_INT_TYPE key, HM_INT_TYPE* out_value) {
  15014            
  9            
  12            
1528 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          
1529              
1530 15032           size_t index = HM_FN(find_node)(map, key);
  15013            
  8            
  11            
1531 15032 50         if (index < map->capacity && map->nodes[index].key == key) {
  15013 100          
  8 50          
  11 100          
    50          
    100          
1532             /* TTL check */
1533 15023 100         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  15009 50          
  6 50          
  8 0          
    50          
    0          
1534 3 100         (uint32_t)time(NULL) > map->expires_at[index]) {
  3 0          
  0 0          
  0            
1535 1           HM_FN(expire_at)(map, index, true);
  1            
  0            
  0            
1536 1           goto new_key;
  1            
  0            
  0            
1537             }
1538 15022 100         if (map->nodes[index].value == HM_INT_MAX) return false;
  15008 100          
  6 100          
  8            
1539 15017           *out_value = ++map->nodes[index].value;
  15006            
  5            
  6            
1540 15017 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  15006 50          
  5 50          
  6            
1541 15017 100         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = hm_expiry_at(map->default_ttl);
  15006 100          
  5 50          
  6 0          
    50          
    0          
1542 15017           return true;
  15006            
  5            
  6            
1543             }
1544              
1545 9           new_key:
  4            
  2            
  3            
1546 10 100         if (map->max_size > 0 && map->size >= map->max_size)
  5 50          
  2 50          
  3 0          
    50          
    0          
1547 1           HM_FN(lru_evict_one)(map);
  1            
  0            
  0            
1548              
1549 10 50         HM_ENSURE_CAPACITY(map, false);
  5 0          
  2 0          
  3 0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
1550              
1551 10           index = HM_FN(find_or_allocate)(map, key);
  5            
  2            
  3            
1552 10 50         if (index >= map->capacity) return false;
  5 50          
  2 50          
  3            
1553 10           *out_value = ++map->nodes[index].value;
  5            
  2            
  3            
1554 10 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  5 50          
  2 50          
  3            
1555 10 100         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = hm_expiry_at(map->default_ttl);
  5 50          
  2 50          
  3 0          
    100          
    50          
1556 10           return true;
  5            
  2            
  3            
1557             }
1558              
1559 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            
1560 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          
1561              
1562 25           size_t index = HM_FN(find_node)(map, key);
  8            
  9            
  8            
1563 25 50         if (index < map->capacity && map->nodes[index].key == key) {
  8 100          
  9 50          
  8 100          
    50          
    100          
1564             /* TTL check */
1565 18 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  5 0          
  7 50          
  6 0          
    50          
    0          
1566 0 0         (uint32_t)time(NULL) > map->expires_at[index]) {
  0 0          
  0 0          
  0            
1567 0           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
1568 0           goto new_key;
  0            
  0            
  0            
1569             }
1570 18           HM_INT_TYPE val = map->nodes[index].value;
  5            
  7            
  6            
1571 18 100         if (delta > 0 && val > HM_INT_MAX - delta) return false;
  5 100          
  7 100          
  6 100          
    100          
    100          
1572 15 100         if (delta < 0) {
  4 100          
  6 100          
  5            
1573 6 50         if (delta == HM_INT_MIN) { if (val < 0) return false; }
  2 0          
  2 50          
  2 0          
    50          
    0          
1574 6 100         else { if (val < HM_INT_MIN - delta) return false; }
  2 100          
  2 100          
  2            
1575             }
1576 12           map->nodes[index].value += delta;
  3            
  5            
  4            
1577 12           *out_value = map->nodes[index].value;
  3            
  5            
  4            
1578 12 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  3 50          
  5 50          
  4            
1579 12 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = hm_expiry_at(map->default_ttl);
  3 0          
  5 50          
  4 0          
    50          
    0          
1580 12           return true;
  3            
  5            
  4            
1581             }
1582              
1583 7           new_key:
  3            
  2            
  2            
1584 7 50         if (map->max_size > 0 && map->size >= map->max_size)
  3 0          
  2 50          
  2 0          
    50          
    0          
1585 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1586              
1587 7 50         HM_ENSURE_CAPACITY(map, false);
  3 0          
  2 0          
  2 0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
1588              
1589 7           index = HM_FN(find_or_allocate)(map, key);
  3            
  2            
  2            
1590 7 50         if (index >= map->capacity) return false;
  3 50          
  2 50          
  2            
1591 7           map->nodes[index].value = delta;
  3            
  2            
  2            
1592 7           *out_value = delta;
  3            
  2            
  2            
1593 7 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  3 50          
  2 50          
  2            
1594 7 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = hm_expiry_at(map->default_ttl);
  3 0          
  2 50          
  2 0          
    50          
    0          
1595 7           return true;
  3            
  2            
  2            
1596             }
1597              
1598 5021           static bool HM_FN(decrement)(HM_MAP_TYPE* map, HM_INT_TYPE key, HM_INT_TYPE* out_value) {
  5006            
  9            
  6            
1599 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          
1600              
1601 5018           size_t index = HM_FN(find_node)(map, key);
  5005            
  8            
  5            
1602 5018 50         if (index < map->capacity && map->nodes[index].key == key) {
  5005 100          
  8 50          
  5 100          
    50          
    100          
1603             /* TTL check */
1604 5014 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  5003 0          
  7 50          
  4 0          
    50          
    0          
1605 0 0         (uint32_t)time(NULL) > map->expires_at[index]) {
  0 0          
  0 0          
  0            
1606 0           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
1607 0           goto new_key;
  0            
  0            
  0            
1608             }
1609 5014 100         if (map->nodes[index].value == HM_INT_MIN) return false;
  5003 100          
  7 100          
  4            
1610 5010           *out_value = --map->nodes[index].value;
  5002            
  5            
  3            
1611 5010 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  5002 50          
  5 50          
  3            
1612 5010 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = hm_expiry_at(map->default_ttl);
  5002 0          
  5 50          
  3 0          
    50          
    0          
1613 5010           return true;
  5002            
  5            
  3            
1614             }
1615              
1616 4           new_key:
  2            
  1            
  1            
1617 4 50         if (map->max_size > 0 && map->size >= map->max_size)
  2 0          
  1 50          
  1 0          
    50          
    0          
1618 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1619              
1620 4 50         HM_ENSURE_CAPACITY(map, false);
  2 0          
  1 0          
  1 0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
1621              
1622 4           index = HM_FN(find_or_allocate)(map, key);
  2            
  1            
  1            
1623 4 50         if (index >= map->capacity) return false;
  2 50          
  1 50          
  1            
1624 4           *out_value = --map->nodes[index].value;
  2            
  1            
  1            
1625 4 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  2 50          
  1 50          
  1            
1626 4 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = hm_expiry_at(map->default_ttl);
  2 0          
  1 50          
  1 0          
    50          
    0          
1627 4           return true;
  2            
  1            
  1            
1628             }
1629              
1630             #else /* string keys + counters */
1631              
1632 22           static inline size_t HM_FN(find_or_allocate)(HM_MAP_TYPE* map,
  5            
  11            
  6            
1633             const char* key, uint32_t key_len,
1634             uint32_t key_hash, bool key_utf8) {
1635 22           size_t index = (size_t)key_hash & map->mask;
  5            
  11            
  6            
1636 22           const size_t original_index = index;
  5            
  11            
  6            
1637 22           HM_NODE_TYPE* nodes = map->nodes;
  5            
  11            
  6            
1638 22           size_t first_tombstone = map->capacity;
  5            
  11            
  6            
1639              
1640             do {
1641 30 100         if (nodes[index].key == NULL) {
  7 100          
  15 100          
  8            
1642 22 100         size_t target = (first_tombstone < map->capacity) ? first_tombstone : index;
  5 100          
  11 100          
  6            
1643 22           char* new_key = (char*)malloc(key_len + 1);
  5            
  11            
  6            
1644 22 50         if (!new_key) return map->capacity;
  5 50          
  11 50          
  6            
1645 22           memcpy(new_key, key, key_len);
  5            
  11            
  6            
1646 22           new_key[key_len] = '\0';
  5            
  11            
  6            
1647 22 100         if (HM_SLOT_IS_TOMBSTONE(&nodes[target])) map->tombstones--;
  5 100          
  11 100          
  6            
1648 22           nodes[target].key = new_key;
  5            
  11            
  6            
1649 22 50         nodes[target].key_len = HM_PACK_LEN(key_len, key_utf8);
  5 50          
  11 50          
  6            
1650 22           nodes[target].key_hash = key_hash;
  5            
  11            
  6            
1651 22           nodes[target].value = 0;
  5            
  11            
  6            
1652 22           map->size++;
  5            
  11            
  6            
1653 22           return target;
  5            
  11            
  6            
1654             }
1655 8 100         if (nodes[index].key == HM_STR_TOMBSTONE) {
  2 100          
  4 50          
  2            
1656 6 50         if (first_tombstone >= map->capacity) first_tombstone = index;
  1 50          
  3 50          
  2            
1657 2 50         } else if (nodes[index].key_hash == key_hash &&
  1 50          
  1 0          
  0            
1658 0 0         HM_UNPACK_LEN(nodes[index].key_len) == key_len &&
  0 0          
  0 0          
  0            
1659 0 0         memcmp(nodes[index].key, key, key_len) == 0) {
  0 0          
  0 0          
  0            
1660 0           return index;
  0            
  0            
  0            
1661             }
1662 8           index = (index + 1) & map->mask;
  2            
  4            
  2            
1663 8 50         } while (index != original_index);
  2 50          
  4 50          
  2            
1664              
1665 0 0         if (first_tombstone < map->capacity) {
  0 0          
  0 0          
  0            
1666 0           char* new_key = (char*)malloc(key_len + 1);
  0            
  0            
  0            
1667 0 0         if (!new_key) return map->capacity;
  0 0          
  0 0          
  0            
1668 0           memcpy(new_key, key, key_len);
  0            
  0            
  0            
1669 0           new_key[key_len] = '\0';
  0            
  0            
  0            
1670 0           map->tombstones--;
  0            
  0            
  0            
1671 0           nodes[first_tombstone].key = new_key;
  0            
  0            
  0            
1672 0 0         nodes[first_tombstone].key_len = HM_PACK_LEN(key_len, key_utf8);
  0 0          
  0 0          
  0            
1673 0           nodes[first_tombstone].key_hash = key_hash;
  0            
  0            
  0            
1674 0           nodes[first_tombstone].value = 0;
  0            
  0            
  0            
1675 0           map->size++;
  0            
  0            
  0            
1676 0           return first_tombstone;
  0            
  0            
  0            
1677             }
1678 0           return map->capacity;
  0            
  0            
  0            
1679             }
1680              
1681 15041           static bool HM_FN(increment)(HM_MAP_TYPE* map,
  13            
  15017            
  11            
1682             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
1683             HM_INT_TYPE* out_value) {
1684 15041 50         if (!map || !key || !out_value) return false;
  13 50          
  15017 50          
  11 50          
    50          
    50          
    50          
    50          
    50          
1685              
1686 15041           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  13            
  15017            
  11            
1687 15041 50         if (index < map->capacity && HM_SLOT_IS_LIVE(&map->nodes[index])) {
  13 100          
  15017 50          
  11 50          
    100          
    50          
    50          
    100          
    50          
1688             /* TTL check */
1689 15028 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  11 0          
  15010 50          
  7 0          
    50          
    0          
1690 0 0         (uint32_t)time(NULL) > map->expires_at[index]) {
  0 0          
  0 0          
  0            
1691 0           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
1692 0           goto new_key;
  0            
  0            
  0            
1693             }
1694 15028 100         if (map->nodes[index].value == HM_INT_MAX) return false;
  11 100          
  15010 100          
  7            
1695 15025           *out_value = ++map->nodes[index].value;
  10            
  15009            
  6            
1696 15025 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  10 100          
  15009 100          
  6            
1697 15025 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = hm_expiry_at(map->default_ttl);
  10 0          
  15009 50          
  6 0          
    50          
    0          
1698 15025           return true;
  10            
  15009            
  6            
1699             }
1700              
1701 13           new_key:
  2            
  7            
  4            
1702 13 50         if (map->max_size > 0 && map->size >= map->max_size)
  2 0          
  7 50          
  4 0          
    50          
    0          
1703 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1704              
1705 13 50         HM_ENSURE_CAPACITY(map, false);
  2 0          
  7 0          
  4 0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
1706              
1707 13           index = HM_FN(find_or_allocate)(map, key, key_len, key_hash, key_utf8);
  2            
  7            
  4            
1708 13 50         if (index >= map->capacity) return false;
  2 50          
  7 50          
  4            
1709 13           *out_value = ++map->nodes[index].value;
  2            
  7            
  4            
1710 13 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  2 50          
  7 50          
  4            
1711 13 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = hm_expiry_at(map->default_ttl);
  2 0          
  7 100          
  4 50          
    100          
    50          
1712 13           return true;
  2            
  7            
  4            
1713             }
1714              
1715 18           static bool HM_FN(increment_by)(HM_MAP_TYPE* map,
  6            
  6            
  6            
1716             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
1717             HM_INT_TYPE delta, HM_INT_TYPE* out_value) {
1718 18 50         if (!map || !key || !out_value) return false;
  6 50          
  6 50          
  6 50          
    50          
    50          
    50          
    50          
    50          
1719              
1720 18           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  6            
  6            
  6            
1721 18 50         if (index < map->capacity && HM_SLOT_IS_LIVE(&map->nodes[index])) {
  6 100          
  6 50          
  6 50          
    100          
    50          
    50          
    100          
    50          
1722             /* TTL check */
1723 13 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  4 0          
  4 50          
  5 0          
    50          
    0          
1724 0 0         (uint32_t)time(NULL) > map->expires_at[index]) {
  0 0          
  0 0          
  0            
1725 0           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
1726 0           goto new_key;
  0            
  0            
  0            
1727             }
1728 13           HM_INT_TYPE val = map->nodes[index].value;
  4            
  4            
  5            
1729 13 100         if (delta > 0 && val > HM_INT_MAX - delta) return false;
  4 50          
  4 100          
  5 50          
    100          
    100          
1730 10 100         if (delta < 0) {
  3 100          
  3 100          
  4            
1731 6 50         if (delta == HM_INT_MIN) { if (val < 0) return false; }
  2 0          
  2 50          
  2 0          
    50          
    0          
1732 6 100         else { if (val < HM_INT_MIN - delta) return false; }
  2 100          
  2 100          
  2            
1733             }
1734 7           map->nodes[index].value += delta;
  2            
  2            
  3            
1735 7           *out_value = map->nodes[index].value;
  2            
  2            
  3            
1736 7 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  2 50          
  2 50          
  3            
1737 7 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = hm_expiry_at(map->default_ttl);
  2 0          
  2 50          
  3 0          
    50          
    0          
1738 7           return true;
  2            
  2            
  3            
1739             }
1740              
1741 5           new_key:
  2            
  2            
  1            
1742 5 50         if (map->max_size > 0 && map->size >= map->max_size)
  2 0          
  2 50          
  1 0          
    50          
    0          
1743 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1744              
1745 5 50         HM_ENSURE_CAPACITY(map, false);
  2 0          
  2 0          
  1 0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
1746              
1747 5           index = HM_FN(find_or_allocate)(map, key, key_len, key_hash, key_utf8);
  2            
  2            
  1            
1748 5 50         if (index >= map->capacity) return false;
  2 50          
  2 50          
  1            
1749 5           map->nodes[index].value = delta;
  2            
  2            
  1            
1750 5           *out_value = delta;
  2            
  2            
  1            
1751 5 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  2 50          
  2 50          
  1            
1752 5 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = hm_expiry_at(map->default_ttl);
  2 0          
  2 50          
  1 0          
    50          
    0          
1753 5           return true;
  2            
  2            
  1            
1754             }
1755              
1756 5014           static bool HM_FN(decrement)(HM_MAP_TYPE* map,
  3            
  5006            
  5            
1757             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
1758             HM_INT_TYPE* out_value) {
1759 5014 50         if (!map || !key || !out_value) return false;
  3 50          
  5006 50          
  5 50          
    50          
    50          
    50          
    50          
    50          
1760              
1761 5014           size_t index = HM_FN(find_node)(map, key, key_len, key_hash);
  3            
  5006            
  5            
1762 5014 50         if (index < map->capacity && HM_SLOT_IS_LIVE(&map->nodes[index])) {
  3 100          
  5006 50          
  5 50          
    100          
    50          
    50          
    100          
    50          
1763             /* TTL check */
1764 5010 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  2 0          
  5004 50          
  4 0          
    50          
    0          
1765 0 0         (uint32_t)time(NULL) > map->expires_at[index]) {
  0 0          
  0 0          
  0            
1766 0           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
1767 0           goto new_key;
  0            
  0            
  0            
1768             }
1769 5010 100         if (map->nodes[index].value == HM_INT_MIN) return false;
  2 100          
  5004 100          
  4            
1770 5006           *out_value = --map->nodes[index].value;
  1            
  5002            
  3            
1771 5006 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  1 50          
  5002 50          
  3            
1772 5006 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = hm_expiry_at(map->default_ttl);
  1 0          
  5002 50          
  3 0          
    50          
    0          
1773 5006           return true;
  1            
  5002            
  3            
1774             }
1775              
1776 4           new_key:
  1            
  2            
  1            
1777 4 50         if (map->max_size > 0 && map->size >= map->max_size)
  1 0          
  2 50          
  1 0          
    50          
    0          
1778 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1779              
1780 4 50         HM_ENSURE_CAPACITY(map, false);
  1 0          
  2 0          
  1 0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
1781              
1782 4           index = HM_FN(find_or_allocate)(map, key, key_len, key_hash, key_utf8);
  1            
  2            
  1            
1783 4 50         if (index >= map->capacity) return false;
  1 50          
  2 50          
  1            
1784 4           *out_value = --map->nodes[index].value;
  1            
  2            
  1            
1785 4 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  1 50          
  2 50          
  1            
1786 4 50         if (HM_UNLIKELY(map->expires_at && map->default_ttl > 0)) map->expires_at[index] = hm_expiry_at(map->default_ttl);
  1 0          
  2 50          
  1 0          
    50          
    0          
1787 4           return true;
  1            
  2            
  1            
1788             }
1789              
1790             #endif /* HM_KEY_IS_INT for counters */
1791              
1792             #endif /* HM_HAS_COUNTERS */
1793              
1794             /* ---- get_or_set: single-probe get with insert on miss ---- */
1795              
1796             #ifdef HM_KEY_IS_INT
1797              
1798             #ifdef HM_VALUE_IS_STR
1799             /* get_or_set for int-key, string-value: returns index, sets *was_found */
1800 10           static size_t HM_FN(get_or_set)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  0            
  9            
  1            
1801             const char* def_val, uint32_t def_len, bool def_utf8,
1802             uint32_t entry_ttl, bool* was_found) {
1803 10           *was_found = false;
  0            
  9            
  1            
1804 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          
1805              
1806 10 0         HM_ENSURE_CAPACITY(map, map->capacity);
  0 0          
  9 0          
  1 0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
1807              
1808             bool found;
1809 10           size_t index = HM_FN(find_slot_for_insert)(map, key, &found);
  0            
  9            
  1            
1810 10 0         if (index >= map->capacity) return map->capacity;
  0 50          
  9 50          
  1            
1811              
1812 10 0         if (found) {
  0 100          
  9 50          
  1            
1813             /* TTL check */
1814 3 0         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  0 0          
  3 50          
  0 0          
    0          
    0          
1815 0 0         (uint32_t)time(NULL) > map->expires_at[index]) {
  0 0          
  0 0          
  0            
1816 0           HM_FN(expire_at)(map, index, true);
  0            
  0            
  0            
1817 0           found = false;
  0            
  0            
  0            
1818             /* Re-probe after expiry */
1819 0           index = HM_FN(find_slot_for_insert)(map, key, &found);
  0            
  0            
  0            
1820 0 0         if (index >= map->capacity) return map->capacity;
  0 0          
  0 0          
  0            
1821             }
1822             }
1823              
1824 10 0         if (found) {
  0 100          
  9 50          
  1            
1825 3           *was_found = true;
  0            
  3            
  0            
1826 3 0         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  0 50          
  3 0          
  0            
1827 3           return index;
  0            
  3            
  0            
1828             }
1829              
1830             /* LRU eviction */
1831 7 0         if (map->max_size > 0 && map->size >= map->max_size) {
  0 0          
  6 50          
  1 0          
    50          
    0          
1832 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1833 0           index = HM_FN(find_slot_for_insert)(map, key, &found);
  0            
  0            
  0            
1834 0 0         if (index >= map->capacity) return map->capacity;
  0 0          
  0 0          
  0            
1835             }
1836              
1837             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
1838 7 0         HM_LAZY_ALLOC_EXPIRES(map, entry_ttl, map->capacity);
  0 0          
  6 0          
  1 50          
    0          
    0          
    50          
    0          
    0          
1839              
1840             /* Pre-allocate value */
1841 7           char* new_val = NULL;
  0            
  6            
  1            
1842 7           uint32_t new_val_len = 0;
  0            
  6            
  1            
1843 7 0         if (def_val && def_len > 0) {
  0 0          
  6 50          
  1 50          
    50          
    50          
1844 7           new_val = (char*)malloc(def_len + 1);
  0            
  6            
  1            
1845 7 0         if (!new_val) return map->capacity;
  0 50          
  6 50          
  1            
1846 7           memcpy(new_val, def_val, def_len);
  0            
  6            
  1            
1847 7           new_val[def_len] = '\0';
  0            
  6            
  1            
1848 7 0         new_val_len = HM_PACK_LEN(def_len, def_utf8);
  0 50          
  6 50          
  1            
1849 0 0         } else if (def_val) {
  0 0          
  0 0          
  0            
1850 0           new_val = (char*)malloc(1);
  0            
  0            
  0            
1851 0 0         if (!new_val) return map->capacity;
  0 0          
  0 0          
  0            
1852 0           new_val[0] = '\0';
  0            
  0            
  0            
1853 0 0         new_val_len = HM_PACK_LEN(0, def_utf8);
  0 0          
  0 0          
  0            
1854             }
1855              
1856 7 0         if (HM_SLOT_IS_TOMBSTONE(&map->nodes[index])) map->tombstones--;
  0 50          
  6 50          
  1            
1857 7           map->size++;
  0            
  6            
  1            
1858 7           map->nodes[index].key = key;
  0            
  6            
  1            
1859 7           map->nodes[index].value = new_val;
  0            
  6            
  1            
1860 7           map->nodes[index].val_len = new_val_len;
  0            
  6            
  1            
1861              
1862 7 0         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  0 50          
  6 50          
  1            
1863 7 0         HM_APPLY_ENTRY_TTL(map, index, entry_ttl);
  0 0          
  6 0          
  1 50          
    0          
    0          
    50          
    0          
    0          
1864              
1865 7           return index;
  0            
  6            
  1            
1866             }
1867             #elif defined(HM_VALUE_IS_SV)
1868 8           static size_t HM_FN(get_or_set)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  4            
  2            
  2            
1869             void* def_val, uint32_t entry_ttl, bool* was_found) {
1870 8           *was_found = false;
  4            
  2            
  2            
1871 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          
1872              
1873 8 50         HM_ENSURE_CAPACITY(map, map->capacity);
  4 0          
  2 0          
  2 0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
1874              
1875             bool found;
1876 8           size_t index = HM_FN(find_slot_for_insert)(map, key, &found);
  4            
  2            
  2            
1877 8 50         if (index >= map->capacity) return map->capacity;
  4 50          
  2 50          
  2            
1878              
1879 8 100         if (found) {
  4 100          
  2 100          
  2            
1880 4 100         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  2 50          
  1 50          
  1 50          
    50          
    50          
1881 3 50         (uint32_t)time(NULL) > map->expires_at[index]) {
  1 50          
  1 50          
  1            
1882 3           HM_FN(expire_at)(map, index, true);
  1            
  1            
  1            
1883 3           found = false;
  1            
  1            
  1            
1884 3           index = HM_FN(find_slot_for_insert)(map, key, &found);
  1            
  1            
  1            
1885 3 50         if (index >= map->capacity) return map->capacity;
  1 50          
  1 50          
  1            
1886             }
1887             }
1888              
1889 8 100         if (found) {
  4 50          
  2 50          
  2            
1890 1           *was_found = true;
  1            
  0            
  0            
1891 1 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  1 0          
  0 0          
  0            
1892 1           return index;
  1            
  0            
  0            
1893             }
1894              
1895 7 50         if (map->max_size > 0 && map->size >= map->max_size) {
  3 0          
  2 50          
  2 0          
    50          
    0          
1896 0           HM_FN(lru_evict_one)(map);
  0            
  0            
  0            
1897 0           index = HM_FN(find_slot_for_insert)(map, key, &found);
  0            
  0            
  0            
1898 0 0         if (index >= map->capacity) return map->capacity;
  0 0          
  0 0          
  0            
1899             }
1900              
1901             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
1902 7 50         HM_LAZY_ALLOC_EXPIRES(map, entry_ttl, map->capacity);
  3 0          
  2 0          
  2 50          
    0          
    0          
    50          
    0          
    0          
1903              
1904 7 100         if (HM_SLOT_IS_TOMBSTONE(&map->nodes[index])) map->tombstones--;
  3 100          
  2 100          
  2            
1905 7           map->size++;
  3            
  2            
  2            
1906 7           map->nodes[index].key = key;
  3            
  2            
  2            
1907 7           map->nodes[index].value = def_val;
  3            
  2            
  2            
1908              
1909 7 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  3 50          
  2 50          
  2            
1910 7 100         HM_APPLY_ENTRY_TTL(map, index, entry_ttl);
  3 50          
  2 50          
  2 50          
    50          
    50          
    50          
    50          
    50          
1911              
1912 7           return index;
  3            
  2            
  2            
1913             }
1914             #else
1915 21           static size_t HM_FN(get_or_set)(HM_MAP_TYPE* map, HM_INT_TYPE key,
  15            
  3            
  3            
1916             HM_INT_TYPE def_val, uint32_t entry_ttl, bool* was_found) {
1917 21           *was_found = false;
  15            
  3            
  3            
1918 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          
1919              
1920 19 50         HM_ENSURE_CAPACITY(map, map->capacity);
  15 0          
  2 0          
  2 0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
1921              
1922             bool found;
1923 19           size_t index = HM_FN(find_slot_for_insert)(map, key, &found);
  15            
  2            
  2            
1924 19 50         if (index >= map->capacity) return map->capacity;
  15 50          
  2 50          
  2            
1925              
1926 19 100         if (found) {
  15 100          
  2 100          
  2            
1927 7 100         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
  5 50          
  1 50          
  1 0          
    50          
    0          
1928 1 50         (uint32_t)time(NULL) > map->expires_at[index]) {
  1 0          
  0 0          
  0            
1929 1           HM_FN(expire_at)(map, index, true);
  1            
  0            
  0            
1930 1           found = false;
  1            
  0            
  0            
1931 1           index = HM_FN(find_slot_for_insert)(map, key, &found);
  1            
  0            
  0            
1932 1 50         if (index >= map->capacity) return map->capacity;
  1 0          
  0 0          
  0            
1933             }
1934             }
1935              
1936 19 100         if (found) {
  15 100          
  2 100          
  2            
1937 6           *was_found = true;
  4            
  1            
  1            
1938 6 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
  4 50          
  1 50          
  1            
1939 6           return index;
  4            
  1            
  1            
1940             }
1941              
1942 13 100         if (map->max_size > 0 && map->size >= map->max_size) {
  11 100          
  1 50          
  1 0          
    50          
    0          
1943 1           HM_FN(lru_evict_one)(map);
  1            
  0            
  0            
1944 1           index = HM_FN(find_slot_for_insert)(map, key, &found);
  1            
  0            
  0            
1945 1 50         if (index >= map->capacity) return map->capacity;
  1 0          
  0 0          
  0            
1946             }
1947              
1948             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
1949 13 50         HM_LAZY_ALLOC_EXPIRES(map, entry_ttl, map->capacity);
  11 0          
  1 0          
  1 50          
    0          
    0          
    50          
    0          
    0          
1950              
1951 13 100         if (HM_SLOT_IS_TOMBSTONE(&map->nodes[index])) map->tombstones--;
  11 50          
  1 50          
  1            
1952 13           map->size++;
  11            
  1            
  1            
1953 13           map->nodes[index].key = key;
  11            
  1            
  1            
1954 13           map->nodes[index].value = def_val;
  11            
  1            
  1            
1955              
1956 13 100         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
  11 50          
  1 50          
  1            
1957 13 100         HM_APPLY_ENTRY_TTL(map, index, entry_ttl);
  11 50          
  1 100          
  1 50          
    0          
    0          
    50          
    0          
    0          
1958              
1959 13           return index;
  11            
  1            
  1            
1960             }
1961             #endif
1962              
1963             #else /* string keys */
1964              
1965             #ifdef HM_VALUE_IS_STR
1966 9           static size_t HM_FN(get_or_set)(HM_MAP_TYPE* map,
1967             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
1968             const char* def_val, uint32_t def_len, bool def_utf8,
1969             uint32_t entry_ttl, bool* was_found) {
1970 9           *was_found = false;
1971 9 50         if (!map || !key) return map ? map->capacity : 0;
    50          
    0          
1972              
1973 9 50         HM_ENSURE_CAPACITY(map, map->capacity);
    0          
    0          
    0          
    0          
1974              
1975             bool found;
1976 9           size_t index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, &found);
1977 9 50         if (index >= map->capacity) return map->capacity;
1978              
1979 9 100         if (found) {
1980 3 50         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
    0          
1981 0 0         (uint32_t)time(NULL) > map->expires_at[index]) {
1982 0           HM_FN(expire_at)(map, index, true);
1983 0           found = false;
1984 0           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, &found);
1985 0 0         if (index >= map->capacity) return map->capacity;
1986             }
1987             }
1988              
1989 9 100         if (found) {
1990 3           *was_found = true;
1991 3 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
1992 3           return index;
1993             }
1994              
1995 6 50         if (map->max_size > 0 && map->size >= map->max_size) {
    0          
1996 0           HM_FN(lru_evict_one)(map);
1997 0           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, &found);
1998 0 0         if (index >= map->capacity) return map->capacity;
1999             }
2000              
2001             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
2002 6 50         HM_LAZY_ALLOC_EXPIRES(map, entry_ttl, map->capacity);
    0          
    0          
2003              
2004             /* Allocate key + value */
2005 6           char* new_key = (char*)malloc(key_len + 1);
2006 6 50         if (!new_key) return map->capacity;
2007 6           memcpy(new_key, key, key_len);
2008 6           new_key[key_len] = '\0';
2009              
2010 6           char* new_val = NULL;
2011 6           uint32_t new_val_len = 0;
2012 6 50         if (def_val && def_len > 0) {
    50          
2013 6           new_val = (char*)malloc(def_len + 1);
2014 6 50         if (!new_val) { free(new_key); return map->capacity; }
2015 6           memcpy(new_val, def_val, def_len);
2016 6           new_val[def_len] = '\0';
2017 6 50         new_val_len = HM_PACK_LEN(def_len, def_utf8);
2018 0 0         } else if (def_val) {
2019 0           new_val = (char*)malloc(1);
2020 0 0         if (!new_val) { free(new_key); return map->capacity; }
2021 0           new_val[0] = '\0';
2022 0 0         new_val_len = HM_PACK_LEN(0, def_utf8);
2023             }
2024              
2025 6 50         if (HM_SLOT_IS_TOMBSTONE(&map->nodes[index])) map->tombstones--;
2026 6           map->size++;
2027 6           map->nodes[index].key = new_key;
2028 6 50         map->nodes[index].key_len = HM_PACK_LEN(key_len, key_utf8);
2029 6           map->nodes[index].key_hash = key_hash;
2030 6           map->nodes[index].value = new_val;
2031 6           map->nodes[index].val_len = new_val_len;
2032              
2033 6 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
2034 6 50         HM_APPLY_ENTRY_TTL(map, index, entry_ttl);
    0          
    0          
2035              
2036 6           return index;
2037             }
2038             #elif defined(HM_VALUE_IS_SV)
2039 4           static size_t HM_FN(get_or_set)(HM_MAP_TYPE* map,
2040             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
2041             void* def_val, uint32_t entry_ttl, bool* was_found) {
2042 4           *was_found = false;
2043 4 50         if (!map || !key) return map ? map->capacity : 0;
    50          
    0          
2044              
2045 4 50         HM_ENSURE_CAPACITY(map, map->capacity);
    0          
    0          
    0          
    0          
2046              
2047             bool found;
2048 4           size_t index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, &found);
2049 4 50         if (index >= map->capacity) return map->capacity;
2050              
2051 4 100         if (found) {
2052 2 100         if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
    50          
2053 1 50         (uint32_t)time(NULL) > map->expires_at[index]) {
2054 1           HM_FN(expire_at)(map, index, true);
2055 1           found = false;
2056 1           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, &found);
2057 1 50         if (index >= map->capacity) return map->capacity;
2058             }
2059             }
2060              
2061 4 100         if (found) {
2062 1           *was_found = true;
2063 1 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
2064 1           return index;
2065             }
2066              
2067 3 50         if (map->max_size > 0 && map->size >= map->max_size) {
    0          
2068 0           HM_FN(lru_evict_one)(map);
2069 0           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, &found);
2070 0 0         if (index >= map->capacity) return map->capacity;
2071             }
2072              
2073             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
2074 3 50         HM_LAZY_ALLOC_EXPIRES(map, entry_ttl, map->capacity);
    0          
    0          
2075              
2076 3           char* new_key = (char*)malloc(key_len + 1);
2077 3 50         if (!new_key) return map->capacity;
2078 3           memcpy(new_key, key, key_len);
2079 3           new_key[key_len] = '\0';
2080              
2081 3 100         if (HM_SLOT_IS_TOMBSTONE(&map->nodes[index])) map->tombstones--;
2082 3           map->size++;
2083 3           map->nodes[index].key = new_key;
2084 3 50         map->nodes[index].key_len = HM_PACK_LEN(key_len, key_utf8);
2085 3           map->nodes[index].key_hash = key_hash;
2086 3           map->nodes[index].value = def_val;
2087              
2088 3 50         if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
2089 3 100         HM_APPLY_ENTRY_TTL(map, index, entry_ttl);
    50          
    50          
2090              
2091 3           return index;
2092             }
2093             #else
2094 9           static size_t HM_FN(get_or_set)(HM_MAP_TYPE* map,
2095             const char* key, uint32_t key_len, uint32_t key_hash, bool key_utf8,
2096             HM_INT_TYPE def_val, uint32_t entry_ttl, bool* was_found) {
2097 9           *was_found = false;
2098 9           if (!map || !key) return map ? map->capacity : 0;
2099              
2100 9           HM_ENSURE_CAPACITY(map, map->capacity);
2101              
2102             bool found;
2103 9           size_t index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, &found);
2104 9           if (index >= map->capacity) return map->capacity;
2105              
2106 9           if (found) {
2107 3           if (HM_UNLIKELY(map->expires_at && map->expires_at[index]) &&
2108 0           (uint32_t)time(NULL) > map->expires_at[index]) {
2109 0           HM_FN(expire_at)(map, index, true);
2110 0           found = false;
2111 0           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, &found);
2112 0           if (index >= map->capacity) return map->capacity;
2113             }
2114             }
2115              
2116 9           if (found) {
2117 3           *was_found = true;
2118 3           if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_promote)(map, (uint32_t)index);
2119 3           return index;
2120             }
2121              
2122 6           if (map->max_size > 0 && map->size >= map->max_size) {
2123 0           HM_FN(lru_evict_one)(map);
2124 0           index = HM_FN(find_slot_for_insert)(map, key, key_len, key_hash, &found);
2125 0           if (index >= map->capacity) return map->capacity;
2126             }
2127              
2128             /* Pre-allocate expires_at before modifying map state (OOM-safe) */
2129 6           HM_LAZY_ALLOC_EXPIRES(map, entry_ttl, map->capacity);
2130              
2131 6           char* new_key = (char*)malloc(key_len + 1);
2132 6           if (!new_key) return map->capacity;
2133 6           memcpy(new_key, key, key_len);
2134 6           new_key[key_len] = '\0';
2135              
2136 6           if (HM_SLOT_IS_TOMBSTONE(&map->nodes[index])) map->tombstones--;
2137 6           map->size++;
2138 6           map->nodes[index].key = new_key;
2139 6           map->nodes[index].key_len = HM_PACK_LEN(key_len, key_utf8);
2140 6           map->nodes[index].key_hash = key_hash;
2141 6           map->nodes[index].value = def_val;
2142              
2143 6           if (HM_UNLIKELY(map->lru_prev)) HM_FN(lru_push_front)(map, (uint32_t)index);
2144 6           HM_APPLY_ENTRY_TTL(map, index, entry_ttl);
2145              
2146 6           return index;
2147             }
2148             #endif
2149              
2150             #endif /* HM_KEY_IS_INT for get_or_set */
2151              
2152             /* ---- Cleanup macros for next inclusion ---- */
2153              
2154             #undef HM_SLOT_IS_EMPTY
2155             #undef HM_SLOT_IS_TOMBSTONE
2156             #undef HM_SLOT_IS_LIVE
2157             #ifdef HM_KEY_IS_INT
2158             #undef HM_EMPTY_KEY
2159             #undef HM_TOMBSTONE_KEY
2160             #undef HM_IS_RESERVED_KEY
2161             #endif
2162              
2163             #undef HM_PREFIX
2164             #undef HM_NODE_TYPE
2165             #undef HM_MAP_TYPE
2166             #undef HM_FN
2167             #undef HM_PASTE
2168             #undef HM_PASTE2
2169              
2170             #undef HM_KEY_IS_INT
2171             #undef HM_VALUE_IS_STR
2172             #undef HM_VALUE_IS_SV
2173             #undef HM_HAS_COUNTERS
2174              
2175             #undef HM_INT_TYPE
2176             #undef HM_INT_MIN
2177             #undef HM_INT_MAX