File Coverage

hashmap_generic.h
Criterion Covered Total %
statement 5407 7855 68.8
branch 3056 6800 44.9
condition n/a
subroutine n/a
pod n/a
total 8463 14655 57.7


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