File Coverage

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