File Coverage

shm_generic.h
Criterion Covered Total %
statement 8260 16743 49.3
branch 2911 10524 27.6
condition n/a
subroutine n/a
pod n/a
total 11171 27267 40.9


line stmt bran cond sub pod time code
1             /*
2             * shm_generic.h — Macro-template for shared-memory hash maps.
3             *
4             * Before including, define:
5             * SHM_PREFIX — function prefix (e.g., shm_ii)
6             * SHM_NODE_TYPE — node struct name
7             * SHM_VARIANT_ID — unique integer for header validation
8             *
9             * Key type (choose one):
10             * SHM_KEY_IS_INT + SHM_KEY_INT_TYPE — integer key
11             * (leave undefined for string keys via arena)
12             *
13             * Value type (choose one):
14             * SHM_VAL_IS_STR — string value via arena
15             * SHM_VAL_INT_TYPE — integer value
16             *
17             * Optional:
18             * SHM_HAS_COUNTERS — generate incr/decr/incr_by and integer cas (integer values only)
19             */
20              
21             /* ================================================================
22             * Part 1: Shared definitions (included once)
23             * ================================================================ */
24              
25             #ifndef SHM_DEFS_H
26             #define SHM_DEFS_H
27              
28             #include
29             #include
30             #include
31             #include
32             #include
33             #include
34             #include
35             #include
36             #include
37              
38             #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
39             #error "shm_generic.h: inline string packing requires little-endian architecture"
40             #endif
41             #include
42             #include
43             #include
44             #include
45             #include
46             #include
47              
48             #ifdef __SSE2__
49             #include
50             #endif
51              
52             #define XXH_INLINE_ALL
53             #include "xxhash.h"
54              
55             /* ---- Constants ---- */
56              
57             #define SHM_MAGIC 0x53484D31U /* "SHM1" */
58             #define SHM_VERSION 7
59             #define SHM_INITIAL_CAP 16
60             #define SHM_MAX_STR_LEN 0x3FFFFFFFU /* ~1GB, bit 30 reserved for inline flag */
61             #define SHM_LRU_NONE UINT32_MAX
62              
63             /* UINT32_MAX = use default TTL; 0 = no TTL; other = per-key TTL */
64             #define SHM_TTL_USE_DEFAULT UINT32_MAX
65              
66             #define SHM_IS_EXPIRED(h, i, now) \
67             ((h)->expires_at && (h)->expires_at[(i)] && \
68             (now) >= (h)->expires_at[(i)])
69              
70             /* Fast monotonic seconds — avoids time() syscall overhead.
71             * CLOCK_MONOTONIC_COARSE is always vDSO on Linux (~2ns). */
72 622           static inline uint32_t shm_now(void) {
73             struct timespec ts;
74 622           clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
75 622           return (uint32_t)ts.tv_sec;
76             }
77              
78             /* Compute expiry timestamp with overflow protection */
79 414           static inline uint32_t shm_expiry_ts(uint32_t ttl) {
80 414           uint64_t sum = (uint64_t)shm_now() + ttl;
81 414 50         return (sum > UINT32_MAX) ? UINT32_MAX : (uint32_t)sum;
82             }
83              
84             #define SHM_EMPTY 0
85             #define SHM_TOMBSTONE 1
86             #define SHM_TAG_MIN 2 /* state values 2-255 = LIVE with hash tag */
87             #define SHM_IS_LIVE(st) ((st) >= SHM_TAG_MIN)
88             #define SHM_MAKE_TAG(hash) ((uint8_t)(((hash) >> 24) % 254 + SHM_TAG_MIN))
89             /* Invariant: TOMBSTONE < TAG_MIN so tag-based probe filtering works.
90             * Compile-time check via negative-size array trick: */
91             typedef char shm_tag_invariant_check[(SHM_TOMBSTONE < SHM_TAG_MIN) ? 1 : -1];
92              
93             /* SIMD helper: find next LIVE slot (state >= SHM_TAG_MIN) in states[],
94             * starting at position *pos. Returns 1 if found (updates *pos), 0 if
95             * no live slot found before cap. */
96 1478           static inline int shm_find_next_live(const uint8_t *states, uint32_t cap, uint32_t *pos) {
97 1478           uint32_t i = *pos;
98             #ifdef __SSE2__
99             /* Check if byte is NOT empty(0) and NOT tombstone(1) using unsigned
100             * saturation: sub_sat(byte, 1) > 0 iff byte >= 2 = SHM_TAG_MIN */
101 1478           __m128i ones = _mm_set1_epi8(1);
102             /* Align to 16-byte boundary first (scalar) */
103 1478           uint32_t align_end = (i + 15) & ~(uint32_t)15;
104 1478 50         if (align_end > cap) align_end = cap;
105 2781 100         for (; i < align_end; i++) {
106 2514 100         if (states[i] >= SHM_TAG_MIN) { *pos = i; return 1; }
107             }
108             /* SIMD scan: subs_u8(chunk, 1) is nonzero for bytes >= 2 */
109 268 100         for (; i + 16 <= cap; i += 16) {
110 412           __m128i chunk = _mm_loadu_si128((const __m128i *)(states + i));
111 206           __m128i sub = _mm_subs_epu8(chunk, ones); /* unsigned saturating sub */
112 412           int mask = _mm_movemask_epi8(_mm_cmpeq_epi8(sub, _mm_setzero_si128()));
113 206           mask = ~mask & 0xFFFF; /* invert: bits set where sub != 0 (live) */
114 206 100         if (mask) { *pos = i + __builtin_ctz(mask); return 1; }
115             }
116             #endif
117             /* Scalar fallback */
118 62 50         for (; i < cap; i++) {
119 0 0         if (states[i] >= SHM_TAG_MIN) { *pos = i; return 1; }
120             }
121 62           return 0;
122             }
123              
124             /* SIMD probe helper: scan up to 16 state bytes from a given position
125             * for tag matches or EMPTY slots. Returns via *match_mask (tag hits)
126             * and *empty_mask (empty slots). Caller uses bitmasks to iterate.
127             * Works on contiguous memory — caller must handle table wrap. */
128             #ifdef __SSE2__
129 13569           static inline void shm_probe_group(const uint8_t *states, uint32_t pos,
130             uint8_t tag, uint16_t *match_mask,
131             uint16_t *empty_mask) {
132 13569           __m128i group = _mm_loadu_si128((const __m128i *)(states + pos));
133 27138           __m128i tag_v = _mm_set1_epi8((char)tag);
134 13569           __m128i zero_v = _mm_setzero_si128();
135 27138           *match_mask = (uint16_t)_mm_movemask_epi8(_mm_cmpeq_epi8(group, tag_v));
136 13569           *empty_mask = (uint16_t)_mm_movemask_epi8(_mm_cmpeq_epi8(group, zero_v));
137 13569           }
138             #endif
139              
140             #define SHM_ARENA_NUM_CLASSES 16 /* 2^4..2^19 = 16..524288 */
141             #define SHM_ARENA_MIN_ALLOC 16
142              
143             /* ---- UTF-8 and inline-string flag packing ---- */
144             /*
145             * key_len / val_len layout (uint32_t):
146             * bit 31 = UTF-8 flag
147             * bit 30 = INLINE flag (string ≤ 7 bytes stored in off+len fields)
148             * bits 0-29 = length (max ~1GB)
149             *
150             * When INLINE is set:
151             * The associated _off field (4 bytes) + bits 0-23 of _len (3 bytes) hold
152             * up to 7 bytes of string data. Length is in bits 24-26 of _len (0-7).
153             * bits 27-29 are reserved (0).
154             *
155             * When INLINE is NOT set (arena mode):
156             * _off = arena offset, _len bits 0-29 = length.
157             */
158              
159             #define SHM_UTF8_FLAG ((uint32_t)0x80000000U)
160             #define SHM_INLINE_FLAG ((uint32_t)0x40000000U)
161             #define SHM_LEN_MASK ((uint32_t)0x3FFFFFFFU)
162             #define SHM_INLINE_MAX 7 /* max bytes that fit inline */
163              
164             /* Arena-mode packing (unchanged for ≤1GB strings) */
165             #define SHM_PACK_LEN(len, utf8) ((uint32_t)(len) | ((utf8) ? SHM_UTF8_FLAG : 0))
166             #define SHM_UNPACK_LEN(packed) ((uint32_t)((packed) & SHM_LEN_MASK))
167             #define SHM_UNPACK_UTF8(packed) (((packed) & SHM_UTF8_FLAG) != 0)
168             #define SHM_IS_INLINE(packed) (((packed) & SHM_INLINE_FLAG) != 0)
169              
170             /* Get string length for either inline or arena mode */
171             #define SHM_STR_LEN(packed) \
172             (SHM_IS_INLINE(packed) ? shm_inline_len(packed) : SHM_UNPACK_LEN(packed))
173              
174             /* Inline packing: store len in bits 24-26, data in _off (4B) + _len bits 0-23 (3B) */
175 12382           static inline void shm_inline_pack(uint32_t *off, uint32_t *len_field,
176             const char *str, uint32_t slen, bool utf8) {
177 12382           uint32_t lf = SHM_INLINE_FLAG | ((uint32_t)slen << 24);
178 12382 100         if (utf8) lf |= SHM_UTF8_FLAG;
179 12382           uint32_t o = 0;
180             /* copy first 4 bytes into off, next 3 into lower 24 bits of len_field */
181 12382           memcpy(&o, str, slen > 4 ? 4 : slen);
182 12382 100         if (slen > 4) {
183 6559           uint32_t rest = 0;
184 6559           memcpy(&rest, str + 4, slen - 4);
185 6559           lf |= rest;
186             }
187 12382           *off = o;
188 12382           *len_field = lf;
189 12382           }
190              
191 52949           static inline uint32_t shm_inline_len(uint32_t len_field) {
192 52949           return (len_field >> 24) & 0x7;
193             }
194              
195             /* Read inline string into caller buffer. Returns pointer to data (buf). */
196 26424           static inline const char *shm_inline_read(uint32_t off, uint32_t len_field,
197             char *buf) {
198 26424           uint32_t slen = shm_inline_len(len_field);
199 26424           memcpy(buf, &off, slen > 4 ? 4 : slen);
200 26424 100         if (slen > 4) {
201 13465           uint32_t rest = len_field & 0x00FFFFFFU;
202 13465           memcpy(buf + 4, &rest, slen - 4);
203             }
204 26424           return buf;
205             }
206              
207             /* Get string pointer + length, handling both inline and arena modes.
208             * For inline, copies to buf and returns buf. For arena, returns arena pointer directly. */
209 15560           static inline const char *shm_str_ptr(uint32_t off, uint32_t len_field,
210             const char *arena, char *inline_buf,
211             uint32_t *out_len) {
212 15560 100         if (SHM_IS_INLINE(len_field)) {
213 15514           *out_len = shm_inline_len(len_field);
214 15514           return shm_inline_read(off, len_field, inline_buf);
215             }
216 46           *out_len = SHM_UNPACK_LEN(len_field);
217 46           return arena + off;
218             }
219              
220             /* ---- Shared memory header (256 bytes, 4 cache lines, in mmap) ---- */
221              
222             #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
223             #define SHM_STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
224             #else
225             #define SHM_STATIC_ASSERT(cond, msg)
226             #endif
227              
228             typedef struct {
229             /* ---- Cache line 0 (0-63): immutable after create ---- */
230             uint32_t magic; /* 0 */
231             uint32_t version; /* 4 */
232             uint32_t variant_id; /* 8 */
233             uint32_t node_size; /* 12 */
234             uint32_t max_table_cap; /* 16 */
235             uint32_t table_cap; /* 20: changes on resize only */
236             uint32_t max_size; /* 24: LRU capacity, 0 = disabled */
237             uint32_t default_ttl; /* 28: TTL seconds, 0 = disabled */
238             uint64_t total_size; /* 32 */
239             uint64_t nodes_off; /* 40 */
240             uint64_t states_off; /* 48 */
241             uint64_t arena_off; /* 56 */
242              
243             /* ---- Cache line 1 (64-127): seqlock + read-path data ---- */
244             uint32_t seq; /* 64: seqlock counter, odd = writer active */
245             uint32_t rwlock_writers_waiting; /* 68: count of writers in FUTEX_WAIT
246             (reader write-preferring yield signal) */
247             uint64_t arena_cap; /* 72: immutable, read by seqlock string path */
248             uint8_t _reserved1[48]; /* 80-127 */
249              
250             /* ---- Cache line 2 (128-191): rwlock + write-hot fields ---- */
251             uint32_t rwlock; /* 128: 0=unlocked, 1..0x7FFFFFFF=readers, 0x80000000|pid=writer */
252             uint32_t rwlock_waiters; /* 132 */
253             uint32_t size; /* 136 */
254             uint32_t tombstones; /* 140 */
255             uint32_t lru_head; /* 144: MRU slot index */
256             uint32_t lru_tail; /* 148: LRU slot index */
257             uint32_t flush_cursor; /* 152: partial flush_expired scan cursor */
258             uint32_t table_gen; /* 156: incremented on every resize */
259             uint64_t arena_bump; /* 160 */
260             uint64_t stat_evictions; /* 168: cumulative LRU eviction count */
261             uint64_t stat_expired; /* 176: cumulative TTL expiration count */
262             uint32_t stat_recoveries; /* 184: cumulative stale lock recovery count */
263             uint32_t lru_skip; /* 188: promotion skip mask (power-of-2 minus 1, 0=strict LRU) */
264              
265             /* ---- Cache line 3 (192-255): arena free lists ---- */
266             uint32_t arena_free[SHM_ARENA_NUM_CLASSES]; /* 192-255 */
267             } ShmHeader;
268              
269             SHM_STATIC_ASSERT(sizeof(ShmHeader) == 256, "ShmHeader must be exactly 256 bytes (4 cache lines)");
270              
271             /* ---- Process-local handle ---- */
272              
273             typedef struct ShmHandle_s {
274             ShmHeader *hdr;
275             void *nodes;
276             uint8_t *states;
277             char *arena;
278             uint32_t *lru_prev; /* NULL if LRU disabled */
279             uint32_t *lru_next; /* NULL if LRU disabled */
280             uint8_t *lru_accessed; /* NULL if LRU disabled — clock second-chance bit */
281             uint32_t *expires_at; /* NULL if TTL disabled */
282             size_t mmap_size;
283             uint32_t max_mask; /* max_table_cap - 1, for seqlock bounds clamping */
284             uint32_t iter_pos;
285             char *copy_buf;
286             uint32_t copy_buf_size;
287             uint32_t iterating; /* active iterator count (each + cursors) */
288             uint32_t iter_gen; /* table_gen snapshot for each() */
289             uint8_t iter_active; /* 1 = built-in each is in progress */
290             uint8_t deferred; /* shrink/compact deferred while iterating */
291             char *path; /* backing file path (strdup'd) */
292             int backing_fd; /* memfd fd to close on destroy, -1 otherwise */
293             /* Sharding: if shard_handles != NULL, this is a sharded map dispatcher */
294             struct ShmHandle_s **shard_handles; /* NULL for single map */
295             uint32_t num_shards;
296             uint32_t shard_mask; /* num_shards - 1 (power of 2) */
297             uint32_t shard_iter; /* current shard for each()/cursor iteration */
298             } ShmHandle;
299              
300             /* ---- Cursor (independent iterator) ---- */
301              
302             typedef struct {
303             ShmHandle *handle; /* for single maps, direct handle; for sharded, the dispatcher */
304             ShmHandle *current; /* current shard handle (== handle for single maps) */
305             uint32_t iter_pos;
306             uint32_t gen; /* table_gen snapshot — reset on mismatch */
307             uint32_t shard_idx; /* current shard index (0 for single maps) */
308             uint32_t shard_count; /* total shards (1 for single maps) */
309             char *copy_buf;
310             uint32_t copy_buf_size;
311             } ShmCursor;
312              
313             /* Grow a copy buffer to hold `needed` bytes; returns 0 on OOM */
314 69           static inline int shm_grow_buf(char **buf, uint32_t *cap, uint32_t needed) {
315 69 100         if (needed == 0) needed = 1;
316 69 100         if (needed <= *cap) return 1;
317 6 50         uint32_t ns = *cap ? *cap : 64;
318 6 50         while (ns < needed) {
319 0           uint32_t next = ns * 2;
320 0 0         if (next <= ns) { ns = needed; break; } /* overflow guard */
321 0           ns = next;
322             }
323 6           char *nb = (char *)realloc(*buf, ns);
324 6 50         if (!nb) return 0;
325 6           *buf = nb;
326 6           *cap = ns;
327 6           return 1;
328             }
329              
330 35           static inline int shm_ensure_copy_buf(ShmHandle *h, uint32_t needed) {
331 35           return shm_grow_buf(&h->copy_buf, &h->copy_buf_size, needed);
332             }
333              
334 34           static inline int shm_cursor_ensure_copy_buf(ShmCursor *c, uint32_t needed) {
335 34           return shm_grow_buf(&c->copy_buf, &c->copy_buf_size, needed);
336             }
337              
338             /* ---- Hash functions (xxHash, XXH3) ---- */
339              
340 81782           static inline uint64_t shm_hash_int64(int64_t key) {
341 81782           return XXH3_64bits(&key, sizeof(key));
342             }
343              
344 32400           static inline uint64_t shm_hash_string(const char *data, uint32_t len) {
345 32400           return XXH3_64bits(data, (size_t)len);
346             }
347              
348             /* ---- Futex-based read-write lock ---- */
349              
350             #define SHM_RWLOCK_SPIN_LIMIT 32
351             #define SHM_LOCK_TIMEOUT_SEC 2 /* FUTEX_WAIT timeout for stale lock detection */
352              
353 100000           static inline void shm_rwlock_spin_pause(void) {
354             #if defined(__x86_64__) || defined(__i386__)
355 100000           __asm__ volatile("pause" ::: "memory");
356             #elif defined(__aarch64__)
357             __asm__ volatile("yield" ::: "memory");
358             #else
359             __asm__ volatile("" ::: "memory");
360             #endif
361 100000           }
362              
363             /* Extract writer PID from rwlock value (lower 31 bits when write-locked). */
364             #define SHM_RWLOCK_WRITER_BIT 0x80000000U
365             #define SHM_RWLOCK_PID_MASK 0x7FFFFFFFU
366             #define SHM_RWLOCK_WR(pid) (SHM_RWLOCK_WRITER_BIT | ((uint32_t)(pid) & SHM_RWLOCK_PID_MASK))
367              
368             /* Check if a PID is alive. Returns 1 if alive or unknown, 0 if definitely dead. */
369 1           static inline int shm_pid_alive(uint32_t pid) {
370 1 50         if (pid == 0) return 1; /* no owner recorded, assume alive */
371 1 50         return !(kill((pid_t)pid, 0) == -1 && errno == ESRCH);
    50          
372             }
373              
374             /* Force-recover a stale write lock left by a dead process.
375             * CAS to OUR pid to hold the lock while fixing seqlock, then release.
376             * Using our pid (not a bare WRITER_BIT sentinel) means a subsequent
377             * recovering process can detect and re-recover if we crash mid-recovery. */
378 1           static inline void shm_recover_stale_lock(ShmHeader *hdr, uint32_t observed_rwlock) {
379 1           uint32_t mypid = SHM_RWLOCK_WR((uint32_t)getpid());
380 1 50         if (!__atomic_compare_exchange_n(&hdr->rwlock, &observed_rwlock,
381             mypid, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
382 0           return;
383             /* Fix seqlock while lock is still held by us */
384 1           uint32_t seq = __atomic_load_n(&hdr->seq, __ATOMIC_RELAXED);
385 1 50         if (seq & 1)
386 1           __atomic_store_n(&hdr->seq, seq + 1, __ATOMIC_RELEASE);
387 1           __atomic_add_fetch(&hdr->stat_recoveries, 1, __ATOMIC_RELAXED);
388             /* Now release the lock */
389 1           __atomic_store_n(&hdr->rwlock, 0, __ATOMIC_RELEASE);
390 1 50         if (__atomic_load_n(&hdr->rwlock_waiters, __ATOMIC_RELAXED) > 0)
391 0           syscall(SYS_futex, &hdr->rwlock, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
392             }
393              
394             static const struct timespec shm_lock_timeout = { SHM_LOCK_TIMEOUT_SEC, 0 };
395              
396 1622           static inline void shm_rwlock_rdlock(ShmHeader *hdr) {
397 1622           uint32_t *lock = &hdr->rwlock;
398 1622           uint32_t *waiters = &hdr->rwlock_waiters;
399 1622           uint32_t *writers_waiting = &hdr->rwlock_writers_waiting;
400 1622           for (int spin = 0; ; spin++) {
401 1622           uint32_t cur = __atomic_load_n(lock, __ATOMIC_RELAXED);
402             /* Write-preferring: when lock is free (cur==0) and writers are
403             * waiting, yield to let the writer acquire. When readers are
404             * already active (cur>=1), new readers may join freely. */
405 1622 50         if (cur > 0 && cur < SHM_RWLOCK_WRITER_BIT) {
    0          
406 0 0         if (__atomic_compare_exchange_n(lock, &cur, cur + 1,
407             1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
408 1622           return;
409 1622 50         } else if (cur == 0 && !__atomic_load_n(writers_waiting, __ATOMIC_RELAXED)) {
    50          
410 1622 50         if (__atomic_compare_exchange_n(lock, &cur, 1,
411             1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
412 1622           return;
413             }
414 0 0         if (__builtin_expect(spin < SHM_RWLOCK_SPIN_LIMIT, 1)) {
415 0           shm_rwlock_spin_pause();
416 0           continue;
417             }
418 0           __atomic_add_fetch(waiters, 1, __ATOMIC_RELAXED);
419 0           cur = __atomic_load_n(lock, __ATOMIC_RELAXED);
420             /* Sleep when write-locked OR when yielding to waiting writers */
421 0 0         if (cur >= SHM_RWLOCK_WRITER_BIT || cur == 0) {
    0          
422 0           long rc = syscall(SYS_futex, lock, FUTEX_WAIT, cur,
423             &shm_lock_timeout, NULL, 0);
424 0 0         if (rc == -1 && errno == ETIMEDOUT) {
    0          
425 0           __atomic_sub_fetch(waiters, 1, __ATOMIC_RELAXED);
426 0 0         if (cur >= SHM_RWLOCK_WRITER_BIT) {
427 0           uint32_t val = __atomic_load_n(lock, __ATOMIC_RELAXED);
428 0 0         if (val >= SHM_RWLOCK_WRITER_BIT) {
429 0           uint32_t pid = val & SHM_RWLOCK_PID_MASK;
430 0 0         if (!shm_pid_alive(pid))
431 0           shm_recover_stale_lock(hdr, val);
432             }
433             } else {
434             /* Yielding to writer timed out — drop one writers_waiting
435             * to recover from a potentially-crashed parked writer. */
436 0           uint32_t wc = __atomic_load_n(writers_waiting, __ATOMIC_RELAXED);
437 0 0         while (wc > 0 && !__atomic_compare_exchange_n(
438 0 0         writers_waiting, &wc, wc - 1,
439             1, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {}
440             }
441 0           spin = 0;
442 0           continue;
443             }
444             }
445 0           __atomic_sub_fetch(waiters, 1, __ATOMIC_RELAXED);
446 0           spin = 0;
447             }
448             }
449              
450 1622           static inline void shm_rwlock_rdunlock(ShmHeader *hdr) {
451 1622           uint32_t prev = __atomic_sub_fetch(&hdr->rwlock, 1, __ATOMIC_RELEASE);
452 1622 50         if (prev == 0 && __atomic_load_n(&hdr->rwlock_waiters, __ATOMIC_RELAXED) > 0)
    50          
453 0           syscall(SYS_futex, &hdr->rwlock, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
454 1622           }
455              
456 45602           static inline void shm_rwlock_wrlock(ShmHeader *hdr) {
457 45602           uint32_t *lock = &hdr->rwlock;
458 45602           uint32_t *waiters = &hdr->rwlock_waiters;
459             /* Encode PID in the rwlock word itself (0x80000000 | pid) to eliminate
460             * any crash window between acquiring the lock and storing the owner. */
461 45602           uint32_t mypid = SHM_RWLOCK_WR((uint32_t)getpid());
462 45602           uint32_t *writers_waiting = &hdr->rwlock_writers_waiting;
463 45602           for (int spin = 0; ; spin++) {
464 45602           uint32_t expected = 0;
465 45602 50         if (__atomic_compare_exchange_n(lock, &expected, mypid,
466             1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
467 45602           return;
468 0 0         if (__builtin_expect(spin < SHM_RWLOCK_SPIN_LIMIT, 1)) {
469 0           shm_rwlock_spin_pause();
470 0           continue;
471             }
472 0           __atomic_add_fetch(waiters, 1, __ATOMIC_RELAXED);
473 0           __atomic_add_fetch(writers_waiting, 1, __ATOMIC_RELAXED);
474 0           uint32_t cur = __atomic_load_n(lock, __ATOMIC_RELAXED);
475 0 0         if (cur != 0) {
476 0           long rc = syscall(SYS_futex, lock, FUTEX_WAIT, cur,
477             &shm_lock_timeout, NULL, 0);
478 0 0         if (rc == -1 && errno == ETIMEDOUT) {
    0          
479 0           __atomic_sub_fetch(waiters, 1, __ATOMIC_RELAXED);
480 0           __atomic_sub_fetch(writers_waiting, 1, __ATOMIC_RELAXED);
481 0           uint32_t val = __atomic_load_n(lock, __ATOMIC_RELAXED);
482 0 0         if (val >= SHM_RWLOCK_WRITER_BIT) {
483 0           uint32_t pid = val & SHM_RWLOCK_PID_MASK;
484 0 0         if (!shm_pid_alive(pid))
485 0           shm_recover_stale_lock(hdr, val);
486             }
487 0           spin = 0;
488 0           continue;
489             }
490             }
491 0           __atomic_sub_fetch(waiters, 1, __ATOMIC_RELAXED);
492 0           __atomic_sub_fetch(writers_waiting, 1, __ATOMIC_RELAXED);
493 0           spin = 0;
494             }
495             }
496              
497 45602           static inline void shm_rwlock_wrunlock(ShmHeader *hdr) {
498 45602           __atomic_store_n(&hdr->rwlock, 0, __ATOMIC_RELEASE);
499 45602 50         if (__atomic_load_n(&hdr->rwlock_waiters, __ATOMIC_RELAXED) > 0)
500 0           syscall(SYS_futex, &hdr->rwlock, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
501 45602           }
502              
503             /* ---- Seqlock (lock-free readers) ---- */
504              
505 14124           static inline uint32_t shm_seqlock_read_begin(ShmHeader *hdr) {
506 14124           int spin = 0;
507 100001           for (;;) {
508 114125           uint32_t s = __atomic_load_n(&hdr->seq, __ATOMIC_ACQUIRE);
509 114125 100         if (__builtin_expect((s & 1) == 0, 1)) return s;
510 100001 100         if (__builtin_expect(spin < 100000, 1)) {
511 100000           shm_rwlock_spin_pause();
512 100000           spin++;
513 100001           continue;
514             }
515             /* Prolonged odd seq — check for dead writer */
516 1           uint32_t val = __atomic_load_n(&hdr->rwlock, __ATOMIC_RELAXED);
517 1 50         if (val >= SHM_RWLOCK_WRITER_BIT) {
518 1           uint32_t pid = val & SHM_RWLOCK_PID_MASK;
519 1 50         if (!shm_pid_alive(pid)) {
520 1           shm_recover_stale_lock(hdr, val);
521 1           spin = 0;
522 1           continue;
523             }
524             }
525             /* Writer is alive, yield CPU */
526 0           struct timespec ts = {0, 1000000}; /* 1ms */
527 0           nanosleep(&ts, NULL);
528 0           spin = 0;
529             }
530             }
531              
532 14124           static inline int shm_seqlock_read_retry(uint32_t *seq, uint32_t start) {
533 14124           __atomic_thread_fence(__ATOMIC_ACQUIRE); /* ensure data loads complete before retry check */
534 14124           return __atomic_load_n(seq, __ATOMIC_RELAXED) != start;
535             }
536              
537 45598           static inline void shm_seqlock_write_begin(uint32_t *seq) {
538 45598           __atomic_add_fetch(seq, 1, __ATOMIC_RELEASE); /* seq becomes odd */
539 45598           }
540              
541 45598           static inline void shm_seqlock_write_end(uint32_t *seq) {
542 45598           __atomic_add_fetch(seq, 1, __ATOMIC_RELEASE); /* seq becomes even */
543 45598           }
544              
545             /* ---- Arena allocator ---- */
546              
547             static inline uint32_t shm_next_pow2(uint32_t v);
548              
549 8779           static inline uint32_t shm_arena_round_up(uint32_t len) {
550 8779 100         if (len < SHM_ARENA_MIN_ALLOC) return SHM_ARENA_MIN_ALLOC;
551 7149           return shm_next_pow2(len);
552             }
553              
554 8779           static inline int shm_arena_class_index(uint32_t alloc_size) {
555 8779 100         if (alloc_size <= SHM_ARENA_MIN_ALLOC) return 0;
556 6949 50         if (alloc_size > (SHM_ARENA_MIN_ALLOC << (SHM_ARENA_NUM_CLASSES - 1))) return -1;
557 6949           return 32 - __builtin_clz(alloc_size - 1) - 4; /* log2(alloc_size) - 4 */
558             }
559              
560 4469           static inline uint32_t shm_arena_alloc(ShmHeader *hdr, char *arena, uint32_t len) {
561 4469           uint32_t asize = shm_arena_round_up(len);
562 4469           int cls = shm_arena_class_index(asize);
563              
564 4469 50         if (cls >= 0 && hdr->arena_free[cls] != 0) {
    100          
565 1           uint32_t head = hdr->arena_free[cls];
566             uint32_t next;
567 1           memcpy(&next, arena + head, sizeof(uint32_t));
568 1           hdr->arena_free[cls] = next;
569 1           return head;
570             }
571              
572 4468           uint64_t off = hdr->arena_bump;
573 4468 100         if (off + asize > hdr->arena_cap || off + asize > (uint64_t)UINT32_MAX)
    50          
574 2           return 0;
575 4466           hdr->arena_bump = off + asize;
576 4466           return (uint32_t)off;
577             }
578              
579 4310           static inline void shm_arena_free_block(ShmHeader *hdr, char *arena,
580             uint32_t off, uint32_t len) {
581 4310           uint32_t asize = shm_arena_round_up(len);
582 4310           int cls = shm_arena_class_index(asize);
583 4310 50         if (cls < 0 || off == 0) return;
    50          
584              
585 4310           uint32_t old_head = hdr->arena_free[cls];
586 4310           memcpy(arena + off, &old_head, sizeof(uint32_t));
587 4310           hdr->arena_free[cls] = off;
588             }
589              
590             /* Store a string: inline if ≤ 7 bytes, arena otherwise. Returns 1 on success, 0 on arena OOM. */
591 16851           static inline int shm_str_store(ShmHeader *hdr, char *arena,
592             uint32_t *off, uint32_t *len_field,
593             const char *str, uint32_t slen, bool utf8) {
594 16851 100         if (slen <= SHM_INLINE_MAX) {
595 12382           shm_inline_pack(off, len_field, str, slen, utf8);
596 12382           return 1;
597             }
598 4469           uint32_t aoff = shm_arena_alloc(hdr, arena, slen);
599 4469 100         if (aoff == 0 && slen > 0) return 0;
    50          
600 4467           memcpy(arena + aoff, str, slen);
601 4467           *off = aoff;
602 4467 100         *len_field = SHM_PACK_LEN(slen, utf8);
603 4467           return 1;
604             }
605              
606             /* Free a string's arena block (no-op for inline strings) */
607 10208           static inline void shm_str_free(ShmHeader *hdr, char *arena,
608             uint32_t off, uint32_t len_field) {
609 10208 100         if (!SHM_IS_INLINE(len_field))
610 4310           shm_arena_free_block(hdr, arena, off, SHM_UNPACK_LEN(len_field));
611 10208           }
612              
613             /* Copy string data (inline or arena) into a destination buffer */
614 108           static inline void shm_str_copy(char *dst, uint32_t off, uint32_t len_field,
615             const char *arena, uint32_t len) {
616 108 50         if (SHM_IS_INLINE(len_field))
617 108           shm_inline_read(off, len_field, dst);
618             else
619 0           memcpy(dst, arena + off, len);
620 108           }
621              
622             /* ---- Utility ---- */
623              
624 7531           static inline uint32_t shm_next_pow2(uint32_t v) {
625 7531 100         if (v < 2) return 2;
626 7530 50         if (v > 0x80000000U) return 0; /* overflow: no valid power of 2 */
627 7530           v--;
628 7530           v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16;
629 7530           return v + 1;
630             }
631              
632             /* ---- LRU helpers ---- */
633              
634 342           static inline void shm_lru_unlink(ShmHandle *h, uint32_t idx) {
635 342           uint32_t *prev = h->lru_prev;
636 342           uint32_t *next = h->lru_next;
637 342           ShmHeader *hdr = h->hdr;
638 342           uint32_t p = prev[idx], n = next[idx];
639 342 100         if (p != SHM_LRU_NONE) next[p] = n;
640 4           else hdr->lru_head = n;
641 342 100         if (n != SHM_LRU_NONE) prev[n] = p;
642 339           else hdr->lru_tail = p;
643 342           prev[idx] = next[idx] = SHM_LRU_NONE;
644 342           }
645              
646 2148           static inline void shm_lru_push_front(ShmHandle *h, uint32_t idx) {
647 2148           uint32_t *prev = h->lru_prev;
648 2148           uint32_t *next = h->lru_next;
649 2148           ShmHeader *hdr = h->hdr;
650 2148 50         if (h->lru_accessed) __atomic_store_n(&h->lru_accessed[idx], 0, __ATOMIC_RELAXED); /* clear stale clock bit */
651 2148           prev[idx] = SHM_LRU_NONE;
652 2148           next[idx] = hdr->lru_head;
653 2148 100         if (hdr->lru_head != SHM_LRU_NONE) prev[hdr->lru_head] = idx;
654 57           else hdr->lru_tail = idx;
655 2148           hdr->lru_head = idx;
656 2148           }
657              
658 5           static inline void shm_lru_promote(ShmHandle *h, uint32_t idx) {
659 5           ShmHeader *hdr = h->hdr;
660 5 100         if (hdr->lru_head == idx) return;
661             /* Counter-based promotion skip: promote every (mask+1)th access.
662             * Branch-predictor friendly — the skip branch is nearly always taken.
663             * Tail entry is never skipped to preserve eviction correctness. */
664 2 50         if (hdr->lru_skip > 0 && idx != hdr->lru_tail) {
    0          
665             static __thread uint32_t promote_ctr = 0;
666 0 0         if ((++promote_ctr & hdr->lru_skip) != 0)
667 0           return;
668             }
669 2           shm_lru_unlink(h, idx);
670 2           shm_lru_push_front(h, idx);
671             }
672              
673             /* ---- Create / Open / Close ---- */
674              
675             /* Error buffer for shm_create_map diagnostics */
676             #define SHM_ERR_BUFLEN 256
677              
678             /* Allocate the process-local ShmHandle given an already-mmap'd base and
679             * a computed layout. Shared by shm_create_map, shm_create_memfd and
680             * shm_open_fd_map; all three differ only in how they acquire `base`. */
681 368           static ShmHandle *shm_alloc_handle(void *base, uint64_t total_size,
682             int has_arena, int has_lru, int has_ttl,
683             uint64_t lru_prev_off, uint64_t lru_next_off,
684             uint64_t lru_accessed_off, uint64_t expires_off,
685             const char *path, int backing_fd, char *errbuf) {
686 368           ShmHeader *hdr = (ShmHeader *)base;
687 368           ShmHandle *h = (ShmHandle *)calloc(1, sizeof(ShmHandle));
688 368 50         if (!h) {
689 0 0         if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "calloc: out of memory");
690 0           munmap(base, (size_t)total_size);
691 0 0         if (backing_fd >= 0) close(backing_fd);
692 0           return NULL;
693             }
694 368           h->hdr = hdr;
695 368           h->nodes = (char *)hdr + hdr->nodes_off;
696 368           h->states = (uint8_t *)((char *)hdr + hdr->states_off);
697 368 100         h->arena = hdr->arena_off ? (char *)hdr + hdr->arena_off : NULL;
698 368 100         h->lru_prev = has_lru ? (uint32_t *)((char *)hdr + lru_prev_off) : NULL;
699 368 100         h->lru_next = has_lru ? (uint32_t *)((char *)hdr + lru_next_off) : NULL;
700 368 100         h->lru_accessed = has_lru ? (uint8_t *)((char *)hdr + lru_accessed_off) : NULL;
701 368 100         h->expires_at = has_ttl ? (uint32_t *)((char *)hdr + expires_off) : NULL;
702 368           h->mmap_size = (size_t)total_size;
703 368           h->max_mask = hdr->max_table_cap - 1;
704 368           h->iter_pos = 0;
705 368           h->backing_fd = backing_fd;
706             /* Hash lookups are random-access: hint the kernel to skip
707             * read-ahead. Best-effort — failure (e.g., MADV_RANDOM not
708             * supported) is harmless. */
709 368           (void)madvise(base, (size_t)total_size, MADV_RANDOM);
710 368 100         if (path) {
711 350           h->path = strdup(path);
712 350 50         if (!h->path) {
713 0 0         if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "strdup: out of memory");
714 0           munmap(base, (size_t)total_size);
715 0 0         if (backing_fd >= 0) close(backing_fd);
716 0           free(h);
717 0           return NULL;
718             }
719             }
720             /* Pre-size copy_buf for string variants to avoid realloc on first access */
721 368 100         if (has_arena) {
722 124           h->copy_buf = (char *)malloc(256);
723 124 50         if (h->copy_buf) h->copy_buf_size = 256;
724             }
725 368           return h;
726             }
727              
728 367           static ShmHandle *shm_create_map(const char *path, uint32_t max_entries,
729             uint32_t node_size, uint32_t variant_id,
730             int has_arena, uint32_t max_size,
731             uint32_t default_ttl, uint32_t lru_skip,
732             char *errbuf) {
733 367 50         if (errbuf) errbuf[0] = '\0';
734 367           uint32_t max_tcap = shm_next_pow2((uint32_t)((uint64_t)max_entries * 4 / 3 + 1));
735 367 100         if (max_tcap < SHM_INITIAL_CAP) max_tcap = SHM_INITIAL_CAP;
736              
737 367           int has_lru = (max_size > 0);
738 367           int has_ttl = (default_ttl > 0);
739              
740 367           uint64_t nodes_off = sizeof(ShmHeader);
741 367           uint64_t states_off = nodes_off + (uint64_t)max_tcap * node_size;
742 367           uint64_t next_off = states_off + max_tcap;
743              
744             /* LRU arrays (between states and arena) */
745 367           uint64_t lru_prev_off = 0, lru_next_off = 0, lru_accessed_off = 0, expires_off = 0;
746 367 100         if (has_lru) {
747 33           next_off = (next_off + 3) & ~(uint64_t)3;
748 33           lru_prev_off = next_off;
749 33           next_off += (uint64_t)max_tcap * sizeof(uint32_t);
750 33           lru_next_off = next_off;
751 33           next_off += (uint64_t)max_tcap * sizeof(uint32_t);
752 33           lru_accessed_off = next_off;
753 33           next_off += max_tcap; /* uint8_t per slot */
754             }
755 367 100         if (has_ttl) {
756 78           next_off = (next_off + 3) & ~(uint64_t)3;
757 78           expires_off = next_off;
758 78           next_off += (uint64_t)max_tcap * sizeof(uint32_t);
759             }
760              
761 367           uint64_t arena_off = 0, arena_cap = 0;
762 367 100         if (has_arena) {
763 125           arena_off = (next_off + 7) & ~(uint64_t)7;
764 125           arena_cap = (uint64_t)max_entries * 128;
765 125 100         if (arena_cap < 4096) arena_cap = 4096;
766             }
767              
768 367 100         uint64_t total_size = has_arena ? arena_off + arena_cap : next_off;
769              
770             #define SHM_ERR(fmt, ...) do { if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, fmt, ##__VA_ARGS__); } while(0)
771              
772 367           int anonymous = (path == NULL);
773 367           int fd = -1;
774             int is_new;
775 367           struct stat st = { 0 };
776             void *base;
777              
778 367 100         if (anonymous) {
779 12           base = mmap(NULL, total_size, PROT_READ | PROT_WRITE,
780             MAP_SHARED | MAP_ANONYMOUS, -1, 0);
781 12 50         if (base == MAP_FAILED) { SHM_ERR("mmap(anon): %s", strerror(errno)); return NULL; }
    0          
782 12           is_new = 1;
783             } else {
784 355           fd = open(path, O_RDWR | O_CREAT, 0666);
785 355 100         if (fd < 0) { SHM_ERR("open(%s): %s", path, strerror(errno)); return NULL; }
    50          
786              
787 354 50         if (flock(fd, LOCK_EX) < 0) { SHM_ERR("flock(%s): %s", path, strerror(errno)); close(fd); return NULL; }
    0          
788              
789 354 50         if (fstat(fd, &st) < 0) { SHM_ERR("fstat(%s): %s", path, strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; }
    0          
790              
791 354           is_new = (st.st_size == 0);
792              
793 354 100         if (!is_new && (uint64_t)st.st_size < sizeof(ShmHeader)) {
    50          
794 0 0         SHM_ERR("%s: file too small (%lld bytes, need %zu)", path,
795             (long long)st.st_size, sizeof(ShmHeader));
796 0           flock(fd, LOCK_UN); close(fd); return NULL;
797             }
798              
799 354 100         if (is_new) {
800 347 50         if (ftruncate(fd, (off_t)total_size) < 0) {
801 0 0         SHM_ERR("ftruncate(%s, %llu): %s", path, (unsigned long long)total_size, strerror(errno));
802 0           flock(fd, LOCK_UN); close(fd); return NULL;
803             }
804             }
805              
806 354 100         base = mmap(NULL, is_new ? total_size : (size_t)st.st_size,
807             PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
808 354 50         if (base == MAP_FAILED) { SHM_ERR("mmap(%s): %s", path, strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; }
    0          
809             }
810              
811 366           ShmHeader *hdr = (ShmHeader *)base;
812              
813 366 100         if (is_new) {
814 359           memset(hdr, 0, sizeof(ShmHeader));
815 359           hdr->magic = SHM_MAGIC;
816 359           hdr->version = SHM_VERSION;
817 359           hdr->variant_id = variant_id;
818 359           hdr->node_size = node_size;
819 359           hdr->max_table_cap = max_tcap;
820 359           hdr->table_cap = SHM_INITIAL_CAP;
821 359           hdr->total_size = total_size;
822 359           hdr->nodes_off = nodes_off;
823 359           hdr->states_off = states_off;
824 359 100         hdr->arena_off = has_arena ? arena_off : 0;
825 359           hdr->arena_cap = arena_cap;
826 359           hdr->arena_bump = SHM_ARENA_MIN_ALLOC; /* reserve offset 0 */
827 359           hdr->max_size = max_size;
828 359           hdr->default_ttl = default_ttl;
829             /* Convert skip percentage (0-99) to power-of-2 mask.
830             * skip=50→mask=1(every 2nd), 75→3(4th), 90→15(16th), 95→31(32nd) */
831             {
832 359           uint32_t mask = 0;
833 359 100         if (lru_skip > 0 && lru_skip < 100) {
    50          
834 1           uint32_t interval = 100 / (100 - lru_skip);
835 1           uint32_t p = 1;
836 5 100         while (p < interval) p <<= 1;
837 1           mask = p - 1;
838             }
839 359           hdr->lru_skip = mask;
840             }
841 359           hdr->lru_head = SHM_LRU_NONE;
842 359           hdr->lru_tail = SHM_LRU_NONE;
843              
844             /* init LRU arrays (full max_tcap, not just initial cap) */
845 359 100         if (has_lru) {
846 32           memset((char *)base + lru_prev_off, 0xFF,
847             max_tcap * sizeof(uint32_t));
848 32           memset((char *)base + lru_next_off, 0xFF,
849             max_tcap * sizeof(uint32_t));
850 32           memset((char *)base + lru_accessed_off, 0, max_tcap);
851             }
852 359 100         if (has_ttl) {
853 77           memset((char *)base + expires_off, 0,
854             max_tcap * sizeof(uint32_t));
855             }
856              
857 359           __atomic_thread_fence(__ATOMIC_SEQ_CST);
858             } else {
859 21           int valid = (hdr->magic == SHM_MAGIC &&
860 7 100         hdr->version == SHM_VERSION &&
861 6 100         hdr->variant_id == variant_id &&
862 3 50         hdr->node_size == node_size &&
863 3 50         hdr->total_size == (uint64_t)st.st_size &&
864 3 50         hdr->nodes_off >= sizeof(ShmHeader) &&
865 3 50         hdr->states_off > hdr->nodes_off &&
866 3 50         hdr->states_off < hdr->total_size &&
867 3 50         (!hdr->arena_off || (hdr->arena_off < hdr->total_size &&
    0          
868 0 0         hdr->arena_off + hdr->arena_cap <= hdr->total_size &&
869 0 0         hdr->arena_bump <= hdr->arena_cap &&
870 0 0         hdr->arena_bump >= SHM_ARENA_MIN_ALLOC)) &&
871 3 50         hdr->max_table_cap > 0 &&
872 3 50         (hdr->max_table_cap & (hdr->max_table_cap - 1)) == 0 &&
873 3 50         hdr->table_cap > 0 &&
874 3 50         (hdr->table_cap & (hdr->table_cap - 1)) == 0 &&
875 3 50         hdr->table_cap <= hdr->max_table_cap &&
876 3 50         hdr->states_off + hdr->max_table_cap <= hdr->total_size &&
877 3 50         hdr->nodes_off + (uint64_t)hdr->max_table_cap * hdr->node_size <= hdr->states_off &&
878 3 50         hdr->size <= hdr->table_cap &&
879 17 50         hdr->tombstones <= hdr->table_cap - hdr->size &&
    50          
880 3 100         (!hdr->max_size ||
881 1 50         ((hdr->lru_head == SHM_LRU_NONE || hdr->lru_head < hdr->max_table_cap) &&
    50          
882 1 50         (hdr->lru_tail == SHM_LRU_NONE || hdr->lru_tail < hdr->max_table_cap))));
    50          
883 7 100         if (!valid) {
884 4 50         if (hdr->magic != SHM_MAGIC)
885 0 0         SHM_ERR("%s: bad magic (not a HashMap::Shared file)", path);
886 4 100         else if (hdr->version != SHM_VERSION)
887 1 50         SHM_ERR("%s: version mismatch (file=%u, expected=%u)", path, hdr->version, SHM_VERSION);
888 3 50         else if (hdr->variant_id != variant_id)
889 3 50         SHM_ERR("%s: variant mismatch (file=%u, expected=%u)", path, hdr->variant_id, variant_id);
890             else
891 0 0         SHM_ERR("%s: corrupt header", path);
892 4           munmap(base, (size_t)st.st_size);
893 4           flock(fd, LOCK_UN); close(fd);
894 4           return NULL;
895             }
896 3           total_size = (uint64_t)st.st_size;
897              
898             /* Recompute LRU/TTL offsets from header fields */
899 3           has_lru = (hdr->max_size > 0);
900 3           has_ttl = (hdr->default_ttl > 0);
901 3           next_off = hdr->states_off + hdr->max_table_cap;
902 3 100         if (has_lru) {
903 1           next_off = (next_off + 3) & ~(uint64_t)3;
904 1           lru_prev_off = next_off;
905 1           next_off += (uint64_t)hdr->max_table_cap * sizeof(uint32_t);
906 1           lru_next_off = next_off;
907 1           next_off += (uint64_t)hdr->max_table_cap * sizeof(uint32_t);
908 1           lru_accessed_off = next_off;
909 1           next_off += hdr->max_table_cap;
910             }
911 3 100         if (has_ttl) {
912 1           next_off = (next_off + 3) & ~(uint64_t)3;
913 1           expires_off = next_off;
914 1           next_off += (uint64_t)hdr->max_table_cap * sizeof(uint32_t);
915             }
916 3 50         if (next_off > total_size) {
917 0 0         SHM_ERR("%s: file too small for LRU/TTL arrays", path);
918 0           munmap(base, (size_t)st.st_size);
919 0           flock(fd, LOCK_UN); close(fd);
920 0           return NULL;
921             }
922             }
923              
924 362 100         if (fd >= 0) { flock(fd, LOCK_UN); close(fd); }
925             #undef SHM_ERR
926              
927 362           return shm_alloc_handle(base, total_size, has_arena, has_lru, has_ttl,
928             lru_prev_off, lru_next_off, lru_accessed_off, expires_off,
929             path, -1, errbuf);
930             }
931              
932             /* ---- memfd-backed map (fd-shareable, no filesystem presence) ---- */
933 4           static ShmHandle *shm_create_memfd(const char *name, uint32_t max_entries,
934             uint32_t node_size, uint32_t variant_id,
935             int has_arena, uint32_t max_size,
936             uint32_t default_ttl, uint32_t lru_skip,
937             char *errbuf) {
938 4 50         if (errbuf) errbuf[0] = '\0';
939              
940 4           uint32_t max_tcap = shm_next_pow2((uint32_t)((uint64_t)max_entries * 4 / 3 + 1));
941 4 50         if (max_tcap < SHM_INITIAL_CAP) max_tcap = SHM_INITIAL_CAP;
942 4           int has_lru = (max_size > 0);
943 4           int has_ttl = (default_ttl > 0);
944              
945 4           uint64_t nodes_off = sizeof(ShmHeader);
946 4           uint64_t states_off = nodes_off + (uint64_t)max_tcap * node_size;
947 4           uint64_t next_off = states_off + max_tcap;
948 4           uint64_t lru_prev_off = 0, lru_next_off = 0, lru_accessed_off = 0, expires_off = 0;
949 4 50         if (has_lru) {
950 0           next_off = (next_off + 3) & ~(uint64_t)3;
951 0           lru_prev_off = next_off;
952 0           next_off += (uint64_t)max_tcap * sizeof(uint32_t);
953 0           lru_next_off = next_off;
954 0           next_off += (uint64_t)max_tcap * sizeof(uint32_t);
955 0           lru_accessed_off = next_off;
956 0           next_off += max_tcap;
957             }
958 4 50         if (has_ttl) {
959 0           next_off = (next_off + 3) & ~(uint64_t)3;
960 0           expires_off = next_off;
961 0           next_off += (uint64_t)max_tcap * sizeof(uint32_t);
962             }
963 4           uint64_t arena_off = 0, arena_cap = 0;
964 4 100         if (has_arena) {
965 1           arena_off = (next_off + 7) & ~(uint64_t)7;
966 1           arena_cap = (uint64_t)max_entries * 128;
967 1 50         if (arena_cap < 4096) arena_cap = 4096;
968             }
969 4 100         uint64_t total_size = has_arena ? arena_off + arena_cap : next_off;
970              
971 4 50         int fd = memfd_create(name ? name : "hashmap", MFD_CLOEXEC | MFD_ALLOW_SEALING);
972 4 50         if (fd < 0) {
973 0 0         if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "memfd_create: %s", strerror(errno));
974 0           return NULL;
975             }
976 4 50         if (ftruncate(fd, (off_t)total_size) < 0) {
977 0 0         if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "ftruncate: %s", strerror(errno));
978 0           close(fd); return NULL;
979             }
980 4           (void)fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW);
981 4           void *base = mmap(NULL, (size_t)total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
982 4 50         if (base == MAP_FAILED) {
983 0 0         if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "mmap: %s", strerror(errno));
984 0           close(fd); return NULL;
985             }
986              
987 4           ShmHeader *hdr = (ShmHeader *)base;
988 4           memset(hdr, 0, sizeof(ShmHeader));
989 4           hdr->magic = SHM_MAGIC;
990 4           hdr->version = SHM_VERSION;
991 4           hdr->variant_id = variant_id;
992 4           hdr->node_size = node_size;
993 4           hdr->max_table_cap = max_tcap;
994 4           hdr->table_cap = SHM_INITIAL_CAP;
995 4           hdr->total_size = total_size;
996 4           hdr->nodes_off = nodes_off;
997 4           hdr->states_off = states_off;
998 4 100         hdr->arena_off = has_arena ? arena_off : 0;
999 4           hdr->arena_cap = arena_cap;
1000 4           hdr->arena_bump = SHM_ARENA_MIN_ALLOC;
1001 4           hdr->max_size = max_size;
1002 4           hdr->default_ttl = default_ttl;
1003             {
1004 4           uint32_t mask = 0;
1005 4 50         if (lru_skip > 0 && lru_skip < 100) {
    0          
1006 0           uint32_t interval = 100 / (100 - lru_skip);
1007 0           uint32_t p = 1;
1008 0 0         while (p < interval) p <<= 1;
1009 0           mask = p - 1;
1010             }
1011 4           hdr->lru_skip = mask;
1012             }
1013 4           hdr->lru_head = SHM_LRU_NONE;
1014 4           hdr->lru_tail = SHM_LRU_NONE;
1015 4 50         if (has_lru) {
1016 0           memset((char *)base + lru_prev_off, 0xFF, max_tcap * sizeof(uint32_t));
1017 0           memset((char *)base + lru_next_off, 0xFF, max_tcap * sizeof(uint32_t));
1018 0           memset((char *)base + lru_accessed_off, 0, max_tcap);
1019             }
1020 4 50         if (has_ttl) memset((char *)base + expires_off, 0, max_tcap * sizeof(uint32_t));
1021 4           __atomic_thread_fence(__ATOMIC_SEQ_CST);
1022              
1023 4           return shm_alloc_handle(base, total_size, has_arena, has_lru, has_ttl,
1024             lru_prev_off, lru_next_off, lru_accessed_off, expires_off,
1025             NULL, fd, errbuf);
1026             }
1027              
1028             /* ---- Re-open a memfd (or any existing SHM-formatted fd) ---- */
1029 3           static ShmHandle *shm_open_fd_map(int fd, uint32_t variant_id, uint32_t node_size,
1030             char *errbuf) {
1031 3 50         if (errbuf) errbuf[0] = '\0';
1032             struct stat st;
1033 3 50         if (fstat(fd, &st) < 0) {
1034 0 0         if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "fstat: %s", strerror(errno));
1035 0           return NULL;
1036             }
1037 3 100         if ((uint64_t)st.st_size < sizeof(ShmHeader)) {
1038 1 50         if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "fd: file too small for header");
1039 1           return NULL;
1040             }
1041 2           size_t ms = (size_t)st.st_size;
1042 2           void *base = mmap(NULL, ms, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1043 2 50         if (base == MAP_FAILED) {
1044 0 0         if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "mmap: %s", strerror(errno));
1045 0           return NULL;
1046             }
1047              
1048 2           ShmHeader *hdr = (ShmHeader *)base;
1049             /* Same predicates as shm_create_map's reopen branch — refuse any fd
1050             * whose header could mis-index into the mapping. */
1051 6           int valid = (hdr->magic == SHM_MAGIC &&
1052 2 50         hdr->version == SHM_VERSION &&
1053 2 50         hdr->variant_id == variant_id &&
1054 2 50         hdr->node_size == node_size &&
1055 2 50         hdr->total_size == (uint64_t)st.st_size &&
1056 2 50         hdr->nodes_off >= sizeof(ShmHeader) &&
1057 2 50         hdr->states_off > hdr->nodes_off &&
1058 2 50         hdr->states_off < hdr->total_size &&
1059 2 100         (!hdr->arena_off || (hdr->arena_off < hdr->total_size &&
    50          
1060 1 50         hdr->arena_off + hdr->arena_cap <= hdr->total_size &&
1061 1 50         hdr->arena_bump <= hdr->arena_cap &&
1062 1 50         hdr->arena_bump >= SHM_ARENA_MIN_ALLOC)) &&
1063 2 50         hdr->max_table_cap > 0 &&
1064 2 50         (hdr->max_table_cap & (hdr->max_table_cap - 1)) == 0 &&
1065 2 50         hdr->table_cap > 0 &&
1066 2 50         (hdr->table_cap & (hdr->table_cap - 1)) == 0 &&
1067 2 50         hdr->table_cap <= hdr->max_table_cap &&
1068 2 50         hdr->states_off + hdr->max_table_cap <= hdr->total_size &&
1069 2 50         hdr->nodes_off + (uint64_t)hdr->max_table_cap * hdr->node_size <= hdr->states_off &&
1070 2 50         hdr->size <= hdr->table_cap &&
1071 6 50         hdr->tombstones <= hdr->table_cap - hdr->size &&
    50          
1072 2 50         (!hdr->max_size ||
1073 0 0         ((hdr->lru_head == SHM_LRU_NONE || hdr->lru_head < hdr->max_table_cap) &&
    0          
1074 0 0         (hdr->lru_tail == SHM_LRU_NONE || hdr->lru_tail < hdr->max_table_cap))));
    0          
1075 2 50         if (!valid) {
1076 0 0         if (errbuf) {
1077 0 0         if (hdr->magic != SHM_MAGIC)
1078 0           snprintf(errbuf, SHM_ERR_BUFLEN, "fd: bad magic (not a HashMap::Shared file)");
1079 0 0         else if (hdr->version != SHM_VERSION)
1080 0           snprintf(errbuf, SHM_ERR_BUFLEN, "fd: version mismatch (file=%u expected=%u)",
1081             hdr->version, SHM_VERSION);
1082 0 0         else if (hdr->variant_id != variant_id)
1083 0           snprintf(errbuf, SHM_ERR_BUFLEN, "fd: variant mismatch (file=%u expected=%u)",
1084             hdr->variant_id, variant_id);
1085             else
1086 0           snprintf(errbuf, SHM_ERR_BUFLEN, "fd: corrupt header");
1087             }
1088 0           munmap(base, ms);
1089 0           return NULL;
1090             }
1091              
1092             /* Recompute derived offsets from stored header */
1093 2           int has_arena = (hdr->arena_off != 0);
1094 2           int has_lru = (hdr->max_size > 0);
1095 2           int has_ttl = (hdr->default_ttl > 0);
1096 2           uint64_t total_size = hdr->total_size;
1097 2           uint64_t next_off = hdr->states_off + hdr->max_table_cap;
1098 2           uint64_t lru_prev_off = 0, lru_next_off = 0, lru_accessed_off = 0, expires_off = 0;
1099 2 50         if (has_lru) {
1100 0           next_off = (next_off + 3) & ~(uint64_t)3;
1101 0           lru_prev_off = next_off;
1102 0           next_off += (uint64_t)hdr->max_table_cap * sizeof(uint32_t);
1103 0           lru_next_off = next_off;
1104 0           next_off += (uint64_t)hdr->max_table_cap * sizeof(uint32_t);
1105 0           lru_accessed_off = next_off;
1106 0           next_off += hdr->max_table_cap;
1107             }
1108 2 50         if (has_ttl) {
1109 0           next_off = (next_off + 3) & ~(uint64_t)3;
1110 0           expires_off = next_off;
1111 0           next_off += (uint64_t)hdr->max_table_cap * sizeof(uint32_t);
1112             }
1113 2 50         if (next_off > total_size) {
1114 0 0         if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "fd: file too small for LRU/TTL arrays");
1115 0           munmap(base, ms);
1116 0           return NULL;
1117             }
1118              
1119 2           int myfd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
1120 2 50         if (myfd < 0) {
1121 0 0         if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "fcntl: %s", strerror(errno));
1122 0           munmap(base, ms);
1123 0           return NULL;
1124             }
1125 2           return shm_alloc_handle(base, total_size, has_arena, has_lru, has_ttl,
1126             lru_prev_off, lru_next_off, lru_accessed_off, expires_off,
1127             NULL, myfd, errbuf);
1128             }
1129              
1130 1           static inline int shm_msync(ShmHandle *h) {
1131 1 50         if (!h) return 0;
1132 1 50         if (h->shard_handles) {
1133 0 0         for (uint32_t i = 0; i < h->num_shards; i++) {
1134 0           int rc = msync(h->shard_handles[i]->hdr,
1135 0           h->shard_handles[i]->mmap_size, MS_SYNC);
1136 0 0         if (rc != 0) return rc;
1137             }
1138 0           return 0;
1139             }
1140 1 50         if (!h->hdr) return 0;
1141 1           return msync(h->hdr, h->mmap_size, MS_SYNC);
1142             }
1143              
1144 390           static void shm_close_map(ShmHandle *h) {
1145 390 50         if (!h) return;
1146 390 100         if (h->shard_handles) {
1147             /* Sharded: close all sub-handles */
1148 122 100         for (uint32_t i = 0; i < h->num_shards; i++)
1149 100           shm_close_map(h->shard_handles[i]);
1150 22           free(h->shard_handles);
1151 22           free(h->path);
1152 22           free(h);
1153 22           return;
1154             }
1155 368 50         if (h->hdr) munmap(h->hdr, h->mmap_size);
1156 368 100         if (h->backing_fd >= 0) close(h->backing_fd);
1157 368           free(h->copy_buf);
1158 368           free(h->path);
1159 368           free(h);
1160             }
1161              
1162             /* Create a sharded map: N independent maps behind one handle */
1163 22           static ShmHandle *shm_create_sharded(const char *path_prefix, uint32_t num_shards,
1164             uint32_t max_entries, uint32_t node_size,
1165             uint32_t variant_id, int has_arena,
1166             uint32_t max_size, uint32_t default_ttl,
1167             uint32_t lru_skip, char *errbuf) {
1168 22 50         if (!path_prefix) {
1169 0 0         if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "new_sharded requires a path_prefix");
1170 0           return NULL;
1171             }
1172             /* Round up to power of 2 */
1173 22           uint32_t ns = 1;
1174 68 100         while (ns < num_shards) ns <<= 1;
1175 22           num_shards = ns;
1176              
1177 22           ShmHandle *h = (ShmHandle *)calloc(1, sizeof(ShmHandle));
1178 22 50         if (!h) { if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "calloc: out of memory"); return NULL; }
    0          
1179              
1180 22           h->shard_handles = (ShmHandle **)calloc(num_shards, sizeof(ShmHandle *));
1181 22 50         if (!h->shard_handles) { free(h); if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "calloc: out of memory"); return NULL; }
    0          
1182              
1183 22           h->num_shards = num_shards;
1184 22           h->shard_mask = num_shards - 1;
1185 22           h->path = strdup(path_prefix);
1186              
1187 122 100         for (uint32_t i = 0; i < num_shards; i++) {
1188             char shard_path[4096];
1189 100           int sn = snprintf(shard_path, sizeof(shard_path), "%s.%u", path_prefix, i);
1190 100 50         if (sn < 0 || sn >= (int)sizeof(shard_path)) {
    50          
1191 0 0         if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, "shard path too long");
1192 0 0         for (uint32_t j = 0; j < i; j++)
1193 0           shm_close_map(h->shard_handles[j]);
1194 0           free(h->shard_handles);
1195 0           free(h->path);
1196 0           free(h);
1197 0           return NULL;
1198             }
1199 100           h->shard_handles[i] = shm_create_map(shard_path, max_entries, node_size,
1200             variant_id, has_arena, max_size,
1201             default_ttl, lru_skip, errbuf);
1202 100 50         if (!h->shard_handles[i]) {
1203             /* Clean up already-created shards */
1204 0 0         for (uint32_t j = 0; j < i; j++)
1205 0           shm_close_map(h->shard_handles[j]);
1206 0           free(h->shard_handles);
1207 0           free(h->path);
1208 0           free(h);
1209 0           return NULL;
1210             }
1211             }
1212 22           return h;
1213             }
1214              
1215             /* Unlink the backing file. Returns 1 on success, 0 on failure. */
1216 3           static int shm_unlink_path(const char *path) {
1217 3           return (unlink(path) == 0) ? 1 : 0;
1218             }
1219              
1220 3           static int shm_unlink_sharded(ShmHandle *h) {
1221 3 100         if (!h->shard_handles) {
1222 2 100         if (!h->path) return 0;
1223 1           return shm_unlink_path(h->path);
1224             }
1225 1           int ok = 1;
1226 5 100         for (uint32_t i = 0; i < h->num_shards; i++) {
1227 4 50         if (h->shard_handles[i] && h->shard_handles[i]->path) {
    50          
1228 4 50         if (unlink(h->shard_handles[i]->path) != 0) ok = 0;
1229             }
1230             }
1231 1           return ok;
1232             }
1233              
1234 24           static inline ShmCursor *shm_cursor_create(ShmHandle *h) {
1235 24           ShmCursor *c = (ShmCursor *)calloc(1, sizeof(ShmCursor));
1236 24 50         if (!c) return NULL;
1237 24           c->handle = h;
1238 24 100         if (h->shard_handles) {
1239 4           c->shard_count = h->num_shards;
1240 4           c->shard_idx = 0;
1241 4           c->current = h->shard_handles[0];
1242             } else {
1243 20           c->shard_count = 1;
1244 20           c->shard_idx = 0;
1245 20           c->current = h;
1246             }
1247 24           c->gen = c->current->hdr->table_gen;
1248 24           c->current->iterating++;
1249 24           return c;
1250             }
1251              
1252 24           static inline void shm_cursor_destroy(ShmCursor *c) {
1253 24 50         if (!c) return;
1254 24           ShmHandle *cur = c->current;
1255 24 50         if (cur && cur->iterating > 0)
    100          
1256 8           cur->iterating--;
1257 24           free(c->copy_buf);
1258 24           free(c);
1259             }
1260              
1261             #endif /* SHM_DEFS_H */
1262              
1263              
1264             /* ================================================================
1265             * Part 2: Template (included per variant)
1266             * ================================================================ */
1267              
1268             #define SHM_PASTE2(a, b) a##_##b
1269             #define SHM_PASTE(a, b) SHM_PASTE2(a, b)
1270             #define SHM_FN(name) SHM_PASTE(SHM_PREFIX, name)
1271              
1272             /* ---- Node struct ---- */
1273              
1274             typedef struct {
1275             #ifdef SHM_KEY_IS_INT
1276             SHM_KEY_INT_TYPE key;
1277             #else
1278             uint32_t key_off;
1279             uint32_t key_len; /* high bit = UTF-8 */
1280             #endif
1281             #ifdef SHM_VAL_IS_STR
1282             uint32_t val_off;
1283             uint32_t val_len; /* high bit = UTF-8 */
1284             #else
1285             SHM_VAL_INT_TYPE value;
1286             #endif
1287             } SHM_NODE_TYPE;
1288              
1289             /* ---- Shard dispatch for entry-point functions ---- */
1290             #undef SHM_SHARD_DISPATCH
1291             #ifdef SHM_KEY_IS_INT
1292             #define SHM_SHARD_DISPATCH(h, key_arg) \
1293             if ((h)->shard_handles) { \
1294             uint32_t _sh_hash = SHM_HASH_KEY(key_arg); \
1295             h = (h)->shard_handles[_sh_hash & (h)->shard_mask]; \
1296             }
1297             #else
1298             #define SHM_SHARD_DISPATCH(h, key_str_arg, key_len_arg) \
1299             if ((h)->shard_handles) { \
1300             uint32_t _sh_hash = SHM_HASH_KEY_STR(key_str_arg, key_len_arg); \
1301             h = (h)->shard_handles[_sh_hash & (h)->shard_mask]; \
1302             }
1303             #endif
1304              
1305             /* ---- Key hashing ---- */
1306              
1307             #ifdef SHM_KEY_IS_INT
1308             #define SHM_HASH_KEY(k) ((uint32_t)(shm_hash_int64((int64_t)(k))))
1309             #define SHM_KEY_EQ(node_ptr, k) ((node_ptr)->key == (k))
1310             #else
1311             #define SHM_HASH_KEY_STR(str, len) ((uint32_t)shm_hash_string((str), (len)))
1312             /* Keys are compared by bytes only. The stored UTF8 flag is metadata for
1313             * flag-restoration on retrieval; it must not affect lookup equality,
1314             * otherwise ASCII strings with toggled flag (easy to produce in Perl via
1315             * utf8::upgrade/downgrade, `use utf8`, or source literal encoding) would
1316             * miss their stored value. */
1317 5181           static inline int SHM_PASTE(SHM_PREFIX, _key_eq_str)(
  5132            
  24            
  11            
  14            
1318             const SHM_NODE_TYPE *np, const char *arena, const char *str, uint32_t len, bool utf8) {
1319             (void)utf8;
1320 5181           uint32_t kl_packed = np->key_len;
  5132            
  24            
  11            
  14            
1321 5181 100         if (SHM_IS_INLINE(kl_packed)) {
  5132 50          
  24 50          
  11 50          
  14            
1322 5179 100         if (shm_inline_len(kl_packed) != len) return 0;
  5130 100          
  24 50          
  11 50          
  14            
1323             char buf[SHM_INLINE_MAX];
1324 5145           shm_inline_read(np->key_off, kl_packed, buf);
  5098            
  22            
  11            
  14            
1325 5145           return memcmp(buf, str, len) == 0;
  5098            
  22            
  11            
  14            
1326             }
1327 2 50         if (SHM_UNPACK_LEN(kl_packed) != len) return 0;
  2 0          
  0 0          
  0 0          
  0            
1328 2           return memcmp(arena + np->key_off, str, len) == 0;
  2            
  0            
  0            
  0            
1329             }
1330             #define SHM_KEY_EQ_STR(node_ptr, arena, str, len, utf8) \
1331             SHM_PASTE(SHM_PREFIX, _key_eq_str)((node_ptr), (arena), (str), (len), (utf8))
1332             #endif
1333              
1334             /* ---- Create / Open ---- */
1335              
1336 267           static ShmHandle *SHM_FN(create)(const char *path, uint32_t max_entries,
  61            
  20            
  4            
  5            
  7            
  4            
  4            
  152            
  5            
  5            
1337             uint32_t max_size, uint32_t default_ttl,
1338             uint32_t lru_skip, char *errbuf) {
1339 267           int has_arena = 0;
  61            
  20            
  4            
  5            
  7            
  4            
  4            
  152            
  5            
  5            
1340             #ifndef SHM_KEY_IS_INT
1341 90           has_arena = 1;
  61            
  20            
  4            
  5            
1342             #endif
1343             #ifdef SHM_VAL_IS_STR
1344 76           has_arena = 1;
  61            
  7            
  4            
  4            
1345             #endif
1346 267           return shm_create_map(path, max_entries,
  61            
  20            
  4            
  5            
  7            
  4            
  4            
  152            
  5            
  5            
1347             (uint32_t)sizeof(SHM_NODE_TYPE),
1348             SHM_VARIANT_ID, has_arena,
1349             max_size, default_ttl, lru_skip, errbuf);
1350             }
1351              
1352 22           static ShmHandle *SHM_FN(create_sharded)(const char *path_prefix,
  5            
  0            
  0            
  0            
  0            
  0            
  0            
  17            
  0            
  0            
1353             uint32_t num_shards,
1354             uint32_t max_entries,
1355             uint32_t max_size, uint32_t default_ttl,
1356             uint32_t lru_skip, char *errbuf) {
1357 22           int has_arena = 0;
  5            
  0            
  0            
  0            
  0            
  0            
  0            
  17            
  0            
  0            
1358             #ifndef SHM_KEY_IS_INT
1359 5           has_arena = 1;
  5            
  0            
  0            
  0            
1360             #endif
1361             #ifdef SHM_VAL_IS_STR
1362 5           has_arena = 1;
  5            
  0            
  0            
  0            
1363             #endif
1364 22           return shm_create_sharded(path_prefix, num_shards, max_entries,
  5            
  0            
  0            
  0            
  0            
  0            
  0            
  17            
  0            
  0            
1365             (uint32_t)sizeof(SHM_NODE_TYPE),
1366             SHM_VARIANT_ID, has_arena,
1367             max_size, default_ttl, lru_skip, errbuf);
1368             }
1369              
1370 4           static ShmHandle *SHM_FN(create_memfd)(const char *name, uint32_t max_entries,
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1371             uint32_t max_size, uint32_t default_ttl,
1372             uint32_t lru_skip, char *errbuf) {
1373 4           int has_arena = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1374             #ifndef SHM_KEY_IS_INT
1375 1           has_arena = 1;
  1            
  0            
  0            
  0            
1376             #endif
1377             #ifdef SHM_VAL_IS_STR
1378 1           has_arena = 1;
  1            
  0            
  0            
  0            
1379             #endif
1380 4           return shm_create_memfd(name, max_entries,
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1381             (uint32_t)sizeof(SHM_NODE_TYPE),
1382             SHM_VARIANT_ID, has_arena,
1383             max_size, default_ttl, lru_skip, errbuf);
1384             }
1385              
1386 3           static ShmHandle *SHM_FN(open_fd)(int fd, char *errbuf) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
1387 3           return shm_open_fd_map(fd, SHM_VARIANT_ID,
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
1388             (uint32_t)sizeof(SHM_NODE_TYPE), errbuf);
1389             }
1390              
1391             /* ---- Rehash helper (used during resize) — returns new index ---- */
1392              
1393 51229           static uint32_t SHM_FN(rehash_insert_raw)(ShmHandle *h, SHM_NODE_TYPE *node) {
  14572            
  762            
  13            
  0            
  0            
  0            
  0            
  33840            
  2042            
  0            
1394 51229           ShmHeader *hdr = h->hdr;
  14572            
  762            
  13            
  0            
  0            
  0            
  0            
  33840            
  2042            
  0            
1395 51229           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  14572            
  762            
  13            
  0            
  0            
  0            
  0            
  33840            
  2042            
  0            
1396 51229           uint8_t *states = h->states;
  14572            
  762            
  13            
  0            
  0            
  0            
  0            
  33840            
  2042            
  0            
1397 51229           uint32_t mask = hdr->table_cap - 1;
  14572            
  762            
  13            
  0            
  0            
  0            
  0            
  33840            
  2042            
  0            
1398              
1399             #ifdef SHM_KEY_IS_INT
1400 35882           uint32_t hash = SHM_HASH_KEY(node->key);
  0            
  0            
  0            
  33840            
  2042            
  0            
1401             #else
1402             char ibuf[SHM_INLINE_MAX];
1403             uint32_t klen;
1404 15347           const char *kptr = shm_str_ptr(node->key_off, node->key_len, h->arena, ibuf, &klen);
  14572            
  762            
  13            
  0            
1405 15347           uint32_t hash = shm_hash_string(kptr, klen);
  14572            
  762            
  13            
  0            
1406             #endif
1407              
1408 51229           uint32_t pos = hash & mask;
  14572            
  762            
  13            
  0            
  0            
  0            
  0            
  33840            
  2042            
  0            
1409 71832 100         while (SHM_IS_LIVE(states[pos]))
  19933 100          
  987 100          
  15 0          
  0 0          
  0 0          
  0 0          
  0 100          
  48091 100          
  2806 0          
  0            
1410 20603           pos = (pos + 1) & mask;
  5361            
  225            
  2            
  0            
  0            
  0            
  0            
  14251            
  764            
  0            
1411              
1412 51229           nodes[pos] = *node;
  14572            
  762            
  13            
  0            
  0            
  0            
  0            
  33840            
  2042            
  0            
1413 51229           states[pos] = SHM_MAKE_TAG(hash);
  14572            
  762            
  13            
  0            
  0            
  0            
  0            
  33840            
  2042            
  0            
1414 51229           return pos;
  14572            
  762            
  13            
  0            
  0            
  0            
  0            
  33840            
  2042            
  0            
1415             }
1416              
1417             /* ---- Tombstone at index (helper for eviction/expiry) ---- */
1418              
1419 18940           static void SHM_FN(tombstone_at)(ShmHandle *h, uint32_t idx) {
  5081            
  4            
  0            
  1            
  1            
  0            
  1            
  13351            
  500            
  1            
1420 18940           ShmHeader *hdr = h->hdr;
  5081            
  4            
  0            
  1            
  1            
  0            
  1            
  13351            
  500            
  1            
1421 18940           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  5081            
  4            
  0            
  1            
  1            
  0            
  1            
  13351            
  500            
  1            
1422             #ifndef SHM_KEY_IS_INT
1423 5086           shm_str_free(hdr, h->arena, nodes[idx].key_off, nodes[idx].key_len);
  5081            
  4            
  0            
  1            
1424             #endif
1425             #ifdef SHM_VAL_IS_STR
1426 5083           shm_str_free(hdr, h->arena, nodes[idx].val_off, nodes[idx].val_len);
  5081            
  1            
  0            
  1            
1427             #endif
1428 18940           h->states[idx] = SHM_TOMBSTONE;
  5081            
  4            
  0            
  1            
  1            
  0            
  1            
  13351            
  500            
  1            
1429 18940           hdr->size--;
  5081            
  4            
  0            
  1            
  1            
  0            
  1            
  13351            
  500            
  1            
1430 18940           hdr->tombstones++;
  5081            
  4            
  0            
  1            
  1            
  0            
  1            
  13351            
  500            
  1            
1431 18940           }
  5081            
  4            
  0            
  1            
  1            
  0            
  1            
  13351            
  500            
  1            
1432              
1433             /* ---- LRU eviction ---- */
1434              
1435 320           static void SHM_FN(lru_evict_one)(ShmHandle *h) {
  53            
  1            
  0            
  0            
  0            
  0            
  0            
  266            
  0            
  0            
1436 320           ShmHeader *hdr = h->hdr;
  53            
  1            
  0            
  0            
  0            
  0            
  0            
  266            
  0            
  0            
1437             /* Second-chance clock: walk from tail, if accessed → clear and
1438             * promote (give second chance), else → evict. */
1439 320           uint32_t victim = hdr->lru_tail;
  53            
  1            
  0            
  0            
  0            
  0            
  0            
  266            
  0            
  0            
1440 327 50         while (victim != SHM_LRU_NONE) {
  53 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  273 0          
  0 0          
  0            
1441 327 50         if (h->lru_accessed && __atomic_load_n(&h->lru_accessed[victim], __ATOMIC_RELAXED)) {
  53 50          
  1 50          
  0 50          
  0 0          
  0 0          
  0 0          
  0 0          
  273 0          
  0 0          
  0 0          
    0          
    0          
    0          
    50          
    100          
    0          
    0          
    0          
    0          
1442 7           __atomic_store_n(&h->lru_accessed[victim], 0, __ATOMIC_RELAXED);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1443             /* Promote: give second chance */
1444 7           uint32_t prev = h->lru_prev[victim];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1445 7           shm_lru_unlink(h, victim);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1446 7           shm_lru_push_front(h, victim);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1447 7           victim = prev; /* continue from where we were */
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1448 7 0         if (victim == SHM_LRU_NONE) victim = hdr->lru_tail;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  7 0          
  0 0          
  0            
1449 7           continue;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1450             }
1451 320           break;
  53            
  1            
  0            
  0            
  0            
  0            
  0            
  266            
  0            
  0            
1452             }
1453 320 50         if (victim == SHM_LRU_NONE) return;
  53 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  266 0          
  0 0          
  0            
1454 320           shm_lru_unlink(h, victim);
  53            
  1            
  0            
  0            
  0            
  0            
  0            
  266            
  0            
  0            
1455 320 50         if (h->expires_at) h->expires_at[victim] = 0;
  53 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  266 0          
  0 0          
  0            
1456 320           SHM_FN(tombstone_at)(h, victim);
  53            
  1            
  0            
  0            
  0            
  0            
  0            
  266            
  0            
  0            
1457 320           __atomic_add_fetch(&hdr->stat_evictions, 1, __ATOMIC_RELAXED);
  53            
  1            
  0            
  0            
  0            
  0            
  0            
  266            
  0            
  0            
1458             }
1459              
1460             /* ---- TTL expiration ---- */
1461              
1462 263           static void SHM_FN(expire_at)(ShmHandle *h, uint32_t idx) {
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  261            
  0            
  0            
1463 263 50         if (h->lru_prev) shm_lru_unlink(h, idx);
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  261 0          
  0 0          
  0            
1464 263           h->expires_at[idx] = 0;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  261            
  0            
  0            
1465 263           SHM_FN(tombstone_at)(h, idx);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  261            
  0            
  0            
1466 263           __atomic_add_fetch(&h->hdr->stat_expired, 1, __ATOMIC_RELAXED);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  261            
  0            
  0            
1467 263           }
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  261            
  0            
  0            
1468              
1469             /* ---- Resize (elastic grow/shrink) ---- */
1470              
1471 258           static int SHM_FN(resize)(ShmHandle *h, uint32_t new_cap) {
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1472 258           ShmHeader *hdr = h->hdr;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1473 258           uint32_t old_cap = hdr->table_cap;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1474 258           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1475 258           uint8_t *states = h->states;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1476              
1477 258           uint32_t live = hdr->size;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1478 258           SHM_NODE_TYPE *saved = NULL;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1479 258           uint32_t *saved_indices = NULL;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1480 258           uint32_t *old_to_new = NULL;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1481 258           uint32_t *saved_exp = NULL;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1482              
1483             /* Save LRU order (tail-to-head) */
1484 258           uint32_t *lru_order = NULL;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1485 258           uint32_t lru_count = 0;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1486 258 100         int need_mapping = (h->lru_prev || h->expires_at);
  53 50          
  6 50          
  1 50          
  0 50          
  0 50          
  0 0          
  0 0          
  190 0          
  8 0          
  0 0          
    0          
    0          
    0          
    100          
    100          
    50          
    50          
    0          
    0          
1487              
1488 258 100         if (live > 0) {
  53 50          
  6 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  190 50          
  8 0          
  0            
1489 241           saved = (SHM_NODE_TYPE *)malloc((size_t)live * sizeof(SHM_NODE_TYPE));
  50            
  6            
  1            
  0            
  0            
  0            
  0            
  176            
  8            
  0            
1490 241 50         if (!saved) return 0;
  50 50          
  6 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  176 50          
  8 0          
  0            
1491              
1492 241 100         if (need_mapping) {
  50 50          
  6 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  176 50          
  8 0          
  0            
1493 37           saved_indices = (uint32_t *)malloc((size_t)live * sizeof(uint32_t));
  4            
  0            
  0            
  0            
  0            
  0            
  0            
  33            
  0            
  0            
1494 37           old_to_new = (uint32_t *)malloc((size_t)old_cap * sizeof(uint32_t));
  4            
  0            
  0            
  0            
  0            
  0            
  0            
  33            
  0            
  0            
1495 37 50         if (!saved_indices || !old_to_new) {
  4 50          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  33 0          
  0 0          
  0 0          
    0          
    0          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
1496 0           free(saved); free(saved_indices); free(old_to_new);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1497 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1498             }
1499 37           memset(old_to_new, 0xFF, old_cap * sizeof(uint32_t));
  4            
  0            
  0            
  0            
  0            
  0            
  0            
  33            
  0            
  0            
1500             }
1501              
1502 241 100         if (h->lru_prev) {
  50 50          
  6 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  176 50          
  8 0          
  0            
1503 24           lru_order = (uint32_t *)malloc((size_t)live * sizeof(uint32_t));
  4            
  0            
  0            
  0            
  0            
  0            
  0            
  20            
  0            
  0            
1504 24 50         if (!lru_order) {
  4 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  20 0          
  0 0          
  0            
1505 0           free(saved); free(saved_indices); free(old_to_new);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1506 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1507             }
1508 24           uint32_t idx = hdr->lru_tail;
  4            
  0            
  0            
  0            
  0            
  0            
  0            
  20            
  0            
  0            
1509 1128 100         while (idx != SHM_LRU_NONE && lru_count < live) {
  188 50          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  940 0          
  0 0          
  0 0          
    0          
    0          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
1510 1104           lru_order[lru_count++] = idx;
  184            
  0            
  0            
  0            
  0            
  0            
  0            
  920            
  0            
  0            
1511 1104           idx = h->lru_prev[idx];
  184            
  0            
  0            
  0            
  0            
  0            
  0            
  920            
  0            
  0            
1512             }
1513             }
1514              
1515 241 50         if (h->expires_at) {
  50 50          
  6 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  176 50          
  8 0          
  0            
1516 13           saved_exp = (uint32_t *)malloc(old_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  13            
  0            
  0            
1517 13 0         if (!saved_exp) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  13 0          
  0 0          
  0            
1518 0           free(saved); free(saved_indices); free(old_to_new); free(lru_order);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1519 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1520             }
1521 13           memcpy(saved_exp, h->expires_at, old_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  13            
  0            
  0            
1522             }
1523              
1524 241           uint32_t j = 0;
  50            
  6            
  1            
  0            
  0            
  0            
  0            
  176            
  8            
  0            
1525 110003 100         for (uint32_t i = 0; i < old_cap && j < live; i++) {
  30272 100          
  1009 100          
  17 100          
  0 100          
  0 50          
  0 0          
  0 0          
  74617 0          
  4088 0          
  0 0          
    0          
    0          
    0          
    100          
    100          
    100          
    50          
    0          
    0          
1526 109762 100         if (SHM_IS_LIVE(states[i])) {
  30222 100          
  1003 100          
  16 0          
  0 0          
  0 0          
  0 0          
  0 100          
  74441 100          
  4080 0          
  0            
1527 51229           saved[j] = nodes[i];
  14572            
  762            
  13            
  0            
  0            
  0            
  0            
  33840            
  2042            
  0            
1528 51229 100         if (saved_indices) saved_indices[j] = i;
  14572 50          
  762 50          
  13 0          
  0 0          
  0 0          
  0 0          
  0 100          
  33840 50          
  2042 0          
  0            
1529 51229           j++;
  14572            
  762            
  13            
  0            
  0            
  0            
  0            
  33840            
  2042            
  0            
1530             }
1531             }
1532 241           live = j;
  50            
  6            
  1            
  0            
  0            
  0            
  0            
  176            
  8            
  0            
1533             }
1534              
1535 258           memset(states, SHM_EMPTY, new_cap);
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1536 258           hdr->table_cap = new_cap;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1537 258           hdr->tombstones = 0;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1538              
1539             /* Reset LRU arrays */
1540 258 100         if (h->lru_prev) {
  53 50          
  6 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  190 50          
  8 0          
  0            
1541 24           memset(h->lru_prev, 0xFF, new_cap * sizeof(uint32_t));
  4            
  0            
  0            
  0            
  0            
  0            
  0            
  20            
  0            
  0            
1542 24           memset(h->lru_next, 0xFF, new_cap * sizeof(uint32_t));
  4            
  0            
  0            
  0            
  0            
  0            
  0            
  20            
  0            
  0            
1543 24 50         if (h->lru_accessed) memset(h->lru_accessed, 0, new_cap);
  4 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  20 0          
  0 0          
  0            
1544 24           hdr->lru_head = SHM_LRU_NONE;
  4            
  0            
  0            
  0            
  0            
  0            
  0            
  20            
  0            
  0            
1545 24           hdr->lru_tail = SHM_LRU_NONE;
  4            
  0            
  0            
  0            
  0            
  0            
  0            
  20            
  0            
  0            
1546             }
1547 258 50         if (h->expires_at) {
  53 50          
  6 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  190 50          
  8 0          
  0            
1548 18           memset(h->expires_at, 0, new_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  18            
  0            
  0            
1549             }
1550              
1551 51487 100         for (uint32_t k = 0; k < live; k++) {
  14625 100          
  768 100          
  14 0          
  0 0          
  0 0          
  0 0          
  0 100          
  34030 100          
  2050 0          
  0            
1552 51229           uint32_t new_idx = SHM_FN(rehash_insert_raw)(h, &saved[k]);
  14572            
  762            
  13            
  0            
  0            
  0            
  0            
  33840            
  2042            
  0            
1553 51229 100         if (old_to_new) old_to_new[saved_indices[k]] = new_idx;
  14572 50          
  762 50          
  13 0          
  0 0          
  0 0          
  0 0          
  0 100          
  33840 50          
  2042 0          
  0            
1554             }
1555              
1556             /* Rebuild LRU chain in original order (lru_order[0]=tail/LRU, last=head/MRU).
1557             * Push front from LRU to MRU so the last push (MRU) ends up at head. */
1558 258 100         if (h->lru_prev && lru_order) {
  53 50          
  6 50          
  1 0          
  0 50          
  0 0          
  0 0          
  0 0          
  190 0          
  8 0          
  0 0          
    0          
    0          
    0          
    100          
    50          
    50          
    0          
    0          
    0          
1559 1128 100         for (uint32_t i = 0; i < lru_count; i++) {
  188 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  940 0          
  0 0          
  0            
1560 1104           uint32_t new_idx = old_to_new[lru_order[i]];
  184            
  0            
  0            
  0            
  0            
  0            
  0            
  920            
  0            
  0            
1561 1104 50         if (new_idx != SHM_LRU_NONE)
  184 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  920 0          
  0 0          
  0            
1562 1104           shm_lru_push_front(h, new_idx);
  184            
  0            
  0            
  0            
  0            
  0            
  0            
  920            
  0            
  0            
1563             }
1564             }
1565              
1566             /* Restore expires_at */
1567 258 50         if (h->expires_at && saved_exp) {
  53 0          
  6 50          
  1 0          
  0 50          
  0 0          
  0 0          
  0 0          
  190 0          
  8 0          
  0 0          
    0          
    0          
    0          
    100          
    100          
    50          
    0          
    0          
    0          
1568 422 0         for (uint32_t k = 0; k < live; k++) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  422 0          
  0 0          
  0            
1569 409           uint32_t new_idx = old_to_new[saved_indices[k]];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  409            
  0            
  0            
1570 409 0         if (new_idx != SHM_LRU_NONE)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  409 0          
  0 0          
  0            
1571 409           h->expires_at[new_idx] = saved_exp[saved_indices[k]];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  409            
  0            
  0            
1572             }
1573             }
1574              
1575 258 100         if (new_cap < old_cap) {
  53 50          
  6 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  190 100          
  8 0          
  0            
1576 39           size_t node_shrink = (size_t)(old_cap - new_cap) * sizeof(SHM_NODE_TYPE);
  9            
  0            
  0            
  0            
  0            
  0            
  0            
  29            
  1            
  0            
1577 39           madvise((char *)nodes + (size_t)new_cap * sizeof(SHM_NODE_TYPE),
  9            
  0            
  0            
  0            
  0            
  0            
  0            
  29            
  1            
  0            
1578             node_shrink, MADV_DONTNEED);
1579 39           madvise(states + new_cap, old_cap - new_cap, MADV_DONTNEED);
  9            
  0            
  0            
  0            
  0            
  0            
  0            
  29            
  1            
  0            
1580             }
1581              
1582 258           hdr->table_gen++;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1583              
1584 258           free(saved);
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1585 258           free(saved_indices);
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1586 258           free(old_to_new);
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1587 258           free(lru_order);
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1588 258           free(saved_exp);
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1589 258           return 1;
  53            
  6            
  1            
  0            
  0            
  0            
  0            
  190            
  8            
  0            
1590             }
1591              
1592 28081           static inline void SHM_FN(maybe_grow)(ShmHandle *h) {
  8124            
  530            
  20            
  15            
  19            
  21            
  13            
  18296            
  1022            
  21            
1593 28081           ShmHeader *hdr = h->hdr;
  8124            
  530            
  20            
  15            
  19            
  21            
  13            
  18296            
  1022            
  21            
1594 28081           uint32_t size = hdr->size, tomb = hdr->tombstones, cap = hdr->table_cap;
  8124            
  530            
  20            
  15            
  19            
  21            
  13            
  18296            
  1022            
  21            
1595 28081 100         if (__builtin_expect((uint64_t)(size + tomb) * 4 > (uint64_t)cap * 3, 0)) {
  8124 100          
  530 100          
  20 50          
  15 50          
  19 50          
  21 50          
  13 100          
  18296 100          
  1022 50          
  21            
1596 285 50         if (h->iterating > 0) { h->deferred = 1; return; }
  41 50          
  6 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  230 50          
  7 0          
  0            
1597 285           uint32_t new_cap = cap * 2;
  41            
  6            
  1            
  0            
  0            
  0            
  0            
  230            
  7            
  0            
1598 285 50         if (new_cap <= hdr->max_table_cap)
  41 50          
  6 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  230 50          
  7 0          
  0            
1599 206           SHM_FN(resize)(h, new_cap);
  41            
  6            
  1            
  0            
  0            
  0            
  0            
  151            
  7            
  0            
1600 27796 100         } else if (__builtin_expect(tomb > size || tomb > cap / 4, 0)) {
  8083 50          
  524 50          
  19 50          
  15 50          
  19 50          
  21 50          
  13 50          
  18066 50          
  1015 50          
  21 50          
    50          
    50          
    50          
    100          
    50          
    50          
    50          
    50          
    50          
1601 4 50         if (h->iterating > 0) { h->deferred = 1; return; }
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
1602 4           SHM_FN(resize)(h, cap);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
1603             }
1604             }
1605              
1606 18352           static inline void SHM_FN(maybe_shrink)(ShmHandle *h) {
  5020            
  3            
  0            
  1            
  1            
  0            
  1            
  12825            
  500            
  1            
1607 18352           ShmHeader *hdr = h->hdr;
  5020            
  3            
  0            
  1            
  1            
  0            
  1            
  12825            
  500            
  1            
1608 18352 100         if (hdr->table_cap <= SHM_INITIAL_CAP) return;
  5020 50          
  3 0          
  0 50          
  1 50          
  1 0          
  0 50          
  1 100          
  12825 50          
  500 50          
  1            
1609 18278 100         if (__builtin_expect((uint64_t)hdr->size * 4 < hdr->table_cap, 0)) {
  4999 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  12779 100          
  500 0          
  0            
1610 185 50         if (h->iterating > 0) { h->deferred = 1; return; }
  9 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  175 50          
  1 0          
  0            
1611 36           uint32_t new_cap = hdr->table_cap / 2;
  9            
  0            
  0            
  0            
  0            
  0            
  0            
  26            
  1            
  0            
1612 36 50         if (new_cap < SHM_INITIAL_CAP) new_cap = SHM_INITIAL_CAP;
  9 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  26 50          
  1 0          
  0            
1613 36           SHM_FN(resize)(h, new_cap);
  9            
  0            
  0            
  0            
  0            
  0            
  0            
  26            
  1            
  0            
1614             }
1615             }
1616              
1617 93           static inline void SHM_FN(flush_deferred)(ShmHandle *h) {
  10            
  2            
  0            
  0            
  0            
  0            
  2            
  79            
  0            
  0            
1618 93 50         if (!h->deferred || h->iterating > 0) return;
  10 0          
  2 50          
  0 0          
  0 0          
  0 0          
  0 0          
  2 0          
  79 0          
  0 0          
  0 0          
    0          
    50          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
1619 3           h->deferred = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1620 3           ShmHeader *hdr = h->hdr;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1621 3           shm_rwlock_wrlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1622 3           shm_seqlock_write_begin(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1623 3           uint32_t size = hdr->size, tomb = hdr->tombstones, cap = hdr->table_cap;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1624 3 0         if ((uint64_t)(size + tomb) * 4 > (uint64_t)cap * 3) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
1625 0           uint32_t new_cap = cap * 2;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1626 0 0         if (new_cap <= hdr->max_table_cap)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
1627 0           SHM_FN(resize)(h, new_cap);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1628 6 0         } else if (cap > SHM_INITIAL_CAP && (uint64_t)size * 4 < cap) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  6 0          
  0 0          
  0 0          
    0          
    0          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
1629 3           uint32_t new_cap = cap / 2;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1630 3 0         if (new_cap < SHM_INITIAL_CAP) new_cap = SHM_INITIAL_CAP;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
1631 3           SHM_FN(resize)(h, new_cap);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1632 0 0         } else if (tomb > size || tomb > cap / 4) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1633 0           SHM_FN(resize)(h, cap);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1634             }
1635 3           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1636 3           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1637             }
1638              
1639             /* ---- Put ---- */
1640              
1641             /* put_inner: probe+insert/update WITHOUT locking. Caller holds wrlock+seqlock.
1642             * Returns 1 on success, 0 on failure (arena full / table full). */
1643 27966           static int SHM_FN(put_inner)(ShmHandle *h,
  8107            
  522            
  14            
  7            
  13            
  16            
  6            
  18255            
  1014            
  12            
1644             #ifdef SHM_KEY_IS_INT
1645             SHM_KEY_INT_TYPE key,
1646             #else
1647             const char *key_str, uint32_t key_len, bool key_utf8,
1648             #endif
1649             #ifdef SHM_VAL_IS_STR
1650             const char *val_str, uint32_t val_len, bool val_utf8,
1651             #else
1652             SHM_VAL_INT_TYPE value,
1653             #endif
1654             uint32_t ttl_sec
1655             ) {
1656 27966           ShmHeader *hdr = h->hdr;
  8107            
  522            
  14            
  7            
  13            
  16            
  6            
  18255            
  1014            
  12            
1657 27966           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  8107            
  522            
  14            
  7            
  13            
  16            
  6            
  18255            
  1014            
  12            
1658 27966           uint8_t *states = h->states;
  8107            
  522            
  14            
  7            
  13            
  16            
  6            
  18255            
  1014            
  12            
1659              
1660 27966           SHM_FN(maybe_grow)(h);
  8107            
  522            
  14            
  7            
  13            
  16            
  6            
  18255            
  1014            
  12            
1661 27966           uint32_t mask = hdr->table_cap - 1;
  8107            
  522            
  14            
  7            
  13            
  16            
  6            
  18255            
  1014            
  12            
1662             #ifdef SHM_KEY_IS_INT
1663 19316           uint32_t hash = SHM_HASH_KEY(key);
  13            
  16            
  6            
  18255            
  1014            
  12            
1664             #else
1665 8650           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  8107            
  522            
  14            
  7            
1666             #endif
1667 27966           uint32_t pos = hash & mask;
  8107            
  522            
  14            
  7            
  13            
  16            
  6            
  18255            
  1014            
  12            
1668 27966           uint32_t insert_pos = UINT32_MAX;
  8107            
  522            
  14            
  7            
  13            
  16            
  6            
  18255            
  1014            
  12            
1669              
1670             /* Resolve effective expiry timestamp */
1671 27966           uint32_t exp_ts = 0;
  8107            
  522            
  14            
  7            
  13            
  16            
  6            
  18255            
  1014            
  12            
1672 27966 100         if (h->expires_at) {
  8107 100          
  522 100          
  14 100          
  7 100          
  13 100          
  16 50          
  6 100          
  18255 50          
  1014 100          
  12            
1673 386 100         uint32_t ttl = (ttl_sec == SHM_TTL_USE_DEFAULT) ? hdr->default_ttl : ttl_sec;
  7 100          
  3 50          
  1 50          
  2 100          
  4 50          
  1 0          
  0 100          
  366 0          
  0 50          
  2            
1674 386 50         if (ttl > 0)
  7 50          
  3 50          
  1 50          
  2 50          
  4 50          
  1 0          
  0 100          
  366 0          
  0 50          
  2            
1675 374           exp_ts = shm_expiry_ts(ttl);
  7            
  3            
  1            
  2            
  4            
  1            
  0            
  354            
  0            
  2            
1676             }
1677              
1678 27966           uint8_t tag = SHM_MAKE_TAG(hash);
  8107            
  522            
  14            
  7            
  13            
  16            
  6            
  18255            
  1014            
  12            
1679 95728 50         for (uint32_t i = 0; i <= mask; i++) {
  25620 50          
  1567 50          
  22 50          
  11 50          
  15 50          
  26 50          
  8 100          
  65079 50          
  3366 50          
  14            
1680 95659           uint32_t idx = (pos + i) & mask;
  25620            
  1567            
  22            
  11            
  15            
  26            
  8            
  65010            
  3366            
  14            
1681 95659           uint8_t st = states[idx];
  25620            
  1567            
  22            
  11            
  15            
  26            
  8            
  65010            
  3366            
  14            
1682 95659           __builtin_prefetch(&nodes[idx], 0, 1);
  25620            
  1567            
  22            
  11            
  15            
  26            
  8            
  65010            
  3366            
  14            
1683 95659           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  25620            
  1567            
  22            
  11            
  15            
  26            
  8            
  65010            
  3366            
  14            
1684              
1685 95659 100         if (st == SHM_EMPTY) {
  25620 100          
  1567 100          
  22 100          
  11 100          
  15 100          
  26 100          
  8 100          
  65010 100          
  3366 100          
  14            
1686 27886 100         if (insert_pos == UINT32_MAX) insert_pos = idx;
  8100 50          
  522 50          
  14 100          
  7 50          
  13 50          
  16 50          
  6 100          
  18182 50          
  1014 50          
  12            
1687 27886           break;
  8100            
  522            
  14            
  7            
  13            
  16            
  6            
  18182            
  1014            
  12            
1688             }
1689 67773 100         if (st == SHM_TOMBSTONE) {
  17520 50          
  1045 50          
  8 100          
  4 50          
  2 50          
  10 50          
  2 100          
  46828 50          
  2352 50          
  2            
1690 100 100         if (insert_pos == UINT32_MAX) insert_pos = idx;
  14 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 100          
  85 0          
  0 0          
  0            
1691 100           continue;
  14            
  0            
  0            
  1            
  0            
  0            
  0            
  85            
  0            
  0            
1692             }
1693             /* SHM_IS_LIVE — check tag then key match */
1694 67673 100         if (st != tag) continue;
  17506 100          
  1045 50          
  8 50          
  3 50          
  2 50          
  10 50          
  2 100          
  46743 100          
  2352 50          
  2            
1695             #ifdef SHM_KEY_IS_INT
1696 191 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 0          
  0 100          
  184 50          
  7 0          
  0            
1697             #else
1698 78 100         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  75 50          
  3 0          
  0 0          
  0            
1699             #endif
1700             /* update existing value */
1701             #ifdef SHM_VAL_IS_STR
1702             {
1703 7           uint32_t old_off = nodes[idx].val_off;
  7            
  0            
  0            
  0            
1704 7           uint32_t old_lf = nodes[idx].val_len;
  7            
  0            
  0            
  0            
1705 7 50         if (!shm_str_store(hdr, h->arena, &nodes[idx].val_off, &nodes[idx].val_len, val_str, val_len, val_utf8))
  7 0          
  0 0          
  0 0          
  0            
1706 0           return 0;
  0            
  0            
  0            
  0            
1707 7           shm_str_free(hdr, h->arena, old_off, old_lf);
  7            
  0            
  0            
  0            
1708             }
1709             #else
1710 4           nodes[idx].value = value;
  0            
  0            
  0            
  4            
  0            
  0            
1711             #endif
1712 11 50         if (h->lru_prev) shm_lru_promote(h, idx);
  7 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  4 0          
  0 0          
  0            
1713 11 50         if (h->expires_at) {
  7 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  4 0          
  0 0          
  0            
1714 2 0         if (ttl_sec != SHM_TTL_USE_DEFAULT || h->expires_at[idx] != 0)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  2 0          
  0 0          
  0 0          
    0          
    0          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
1715 1           h->expires_at[idx] = exp_ts;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1716             }
1717 11           return 1;
  7            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
1718             }
1719             }
1720              
1721 27955 50         if (insert_pos == UINT32_MAX) return 0;
  8100 50          
  522 50          
  14 50          
  7 50          
  13 50          
  16 50          
  6 100          
  18251 50          
  1014 50          
  12            
1722              
1723             /* LRU eviction only when actually inserting */
1724 27886 100         if (hdr->max_size > 0 && hdr->size >= hdr->max_size)
  8100 100          
  522 100          
  14 100          
  7 50          
  13 0          
  16 50          
  6 0          
  18182 50          
  1014 0          
  12 50          
    0          
    50          
    0          
    100          
    100          
    50          
    0          
    50          
    0          
1725 318           SHM_FN(lru_evict_one)(h);
  53            
  1            
  0            
  0            
  0            
  0            
  0            
  264            
  0            
  0            
1726              
1727             /* insert new entry */
1728 27886           int was_tombstone = (states[insert_pos] == SHM_TOMBSTONE);
  8100            
  522            
  14            
  7            
  13            
  16            
  6            
  18182            
  1014            
  12            
1729              
1730             #ifdef SHM_KEY_IS_INT
1731 19243           nodes[insert_pos].key = key;
  13            
  16            
  6            
  18182            
  1014            
  12            
1732             #else
1733 8643 50         if (!shm_str_store(hdr, h->arena, &nodes[insert_pos].key_off, &nodes[insert_pos].key_len, key_str, key_len, key_utf8))
  8100 50          
  522 50          
  14 50          
  7            
1734 0           return 0;
  0            
  0            
  0            
  0            
1735             #endif
1736              
1737             #ifdef SHM_VAL_IS_STR
1738 8135 100         if (!shm_str_store(hdr, h->arena, &nodes[insert_pos].val_off, &nodes[insert_pos].val_len, val_str, val_len, val_utf8)) {
  8100 50          
  13 50          
  16 50          
  6            
1739             #ifndef SHM_KEY_IS_INT
1740 2           shm_str_free(hdr, h->arena, nodes[insert_pos].key_off, nodes[insert_pos].key_len);
  2            
1741             #endif
1742 2           return 0;
  2            
  0            
  0            
  0            
1743             }
1744             #else
1745 19751           nodes[insert_pos].value = value;
  522            
  14            
  7            
  18182            
  1014            
  12            
1746             #endif
1747              
1748 27884           states[insert_pos] = SHM_MAKE_TAG(hash);
  8098            
  522            
  14            
  7            
  13            
  16            
  6            
  18182            
  1014            
  12            
1749 27884           hdr->size++;
  8098            
  522            
  14            
  7            
  13            
  16            
  6            
  18182            
  1014            
  12            
1750 27884 100         if (was_tombstone) hdr->tombstones--;
  8098 50          
  522 50          
  14 100          
  7 50          
  13 50          
  16 50          
  6 100          
  18182 50          
  1014 50          
  12            
1751              
1752 27884 100         if (h->lru_prev) shm_lru_push_front(h, insert_pos);
  8098 100          
  522 50          
  14 50          
  7 50          
  13 50          
  16 50          
  6 100          
  18182 50          
  1014 50          
  12            
1753 27884 100         if (h->expires_at) h->expires_at[insert_pos] = exp_ts;
  8098 100          
  522 100          
  14 100          
  7 100          
  13 100          
  16 50          
  6 100          
  18182 50          
  1014 100          
  12            
1754              
1755 27884           return 1;
  8098            
  522            
  14            
  7            
  13            
  16            
  6            
  18182            
  1014            
  12            
1756             }
1757              
1758 26952           static int SHM_FN(put_impl)(ShmHandle *h,
  8101            
  522            
  14            
  7            
  13            
  16            
  6            
  17247            
  1014            
  12            
1759             #ifdef SHM_KEY_IS_INT
1760             SHM_KEY_INT_TYPE key,
1761             #else
1762             const char *key_str, uint32_t key_len, bool key_utf8,
1763             #endif
1764             #ifdef SHM_VAL_IS_STR
1765             const char *val_str, uint32_t val_len, bool val_utf8,
1766             #else
1767             SHM_VAL_INT_TYPE value,
1768             #endif
1769             uint32_t ttl_sec
1770             ) {
1771             #ifdef SHM_KEY_IS_INT
1772 18308 50         SHM_SHARD_DISPATCH(h, key);
  13 50          
  16 50          
  6 100          
  17247 50          
  1014 50          
  12            
1773             #else
1774 8644 100         SHM_SHARD_DISPATCH(h, key_str, key_len);
  8101 50          
  522 50          
  14 50          
  7            
1775             #endif
1776 26952           shm_rwlock_wrlock(h->hdr);
  8101            
  522            
  14            
  7            
  13            
  16            
  6            
  17247            
  1014            
  12            
1777 26952           shm_seqlock_write_begin(&h->hdr->seq);
  8101            
  522            
  14            
  7            
  13            
  16            
  6            
  17247            
  1014            
  12            
1778 26952           int rc = SHM_FN(put_inner)(h,
  8101            
  522            
  14            
  7            
  13            
  16            
  6            
  17247            
  1014            
  12            
1779             #ifdef SHM_KEY_IS_INT
1780             key,
1781             #else
1782             key_str, key_len, key_utf8,
1783             #endif
1784             #ifdef SHM_VAL_IS_STR
1785             val_str, val_len, val_utf8,
1786             #else
1787             value,
1788             #endif
1789             ttl_sec);
1790 26952           shm_seqlock_write_end(&h->hdr->seq);
  8101            
  522            
  14            
  7            
  13            
  16            
  6            
  17247            
  1014            
  12            
1791 26952           shm_rwlock_wrunlock(h->hdr);
  8101            
  522            
  14            
  7            
  13            
  16            
  6            
  17247            
  1014            
  12            
1792 26952           return rc;
  8101            
  522            
  14            
  7            
  13            
  16            
  6            
  17247            
  1014            
  12            
1793             }
1794              
1795 26927           static inline int SHM_FN(put)(ShmHandle *h,
  8100            
  521            
  14            
  7            
  12            
  16            
  6            
  17225            
  1014            
  12            
1796             #ifdef SHM_KEY_IS_INT
1797             SHM_KEY_INT_TYPE key,
1798             #else
1799             const char *key_str, uint32_t key_len, bool key_utf8,
1800             #endif
1801             #ifdef SHM_VAL_IS_STR
1802             const char *val_str, uint32_t val_len, bool val_utf8
1803             #else
1804             SHM_VAL_INT_TYPE value
1805             #endif
1806             ) {
1807 26927           return SHM_FN(put_impl)(h,
  8100            
  521            
  14            
  7            
  12            
  16            
  6            
  17225            
  1014            
  12            
1808             #ifdef SHM_KEY_IS_INT
1809             key,
1810             #else
1811             key_str, key_len, key_utf8,
1812             #endif
1813             #ifdef SHM_VAL_IS_STR
1814             val_str, val_len, val_utf8,
1815             #else
1816             value,
1817             #endif
1818             SHM_TTL_USE_DEFAULT);
1819             }
1820              
1821 25           static inline int SHM_FN(put_ttl)(ShmHandle *h,
  1            
  1            
  0            
  0            
  1            
  0            
  0            
  22            
  0            
  0            
1822             #ifdef SHM_KEY_IS_INT
1823             SHM_KEY_INT_TYPE key,
1824             #else
1825             const char *key_str, uint32_t key_len, bool key_utf8,
1826             #endif
1827             #ifdef SHM_VAL_IS_STR
1828             const char *val_str, uint32_t val_len, bool val_utf8,
1829             #else
1830             SHM_VAL_INT_TYPE value,
1831             #endif
1832             uint32_t ttl_sec
1833             ) {
1834 25 50         if (ttl_sec >= SHM_TTL_USE_DEFAULT - 1) ttl_sec = SHM_TTL_USE_DEFAULT - 2;
  1 50          
  1 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  22 0          
  0 0          
  0            
1835 25           return SHM_FN(put_impl)(h,
  1            
  1            
  0            
  0            
  1            
  0            
  0            
  22            
  0            
  0            
1836             #ifdef SHM_KEY_IS_INT
1837             key,
1838             #else
1839             key_str, key_len, key_utf8,
1840             #endif
1841             #ifdef SHM_VAL_IS_STR
1842             val_str, val_len, val_utf8,
1843             #else
1844             value,
1845             #endif
1846             ttl_sec);
1847             }
1848              
1849             /* ---- Get (seqlock — lock-free read path) ---- */
1850              
1851 14048           static int SHM_FN(get)(ShmHandle *h,
  2565            
  515            
  5            
  8            
  13            
  8            
  10            
  10906            
  8            
  10            
1852             #ifdef SHM_KEY_IS_INT
1853             SHM_KEY_INT_TYPE key,
1854             #else
1855             const char *key_str, uint32_t key_len, bool key_utf8,
1856             #endif
1857             #ifdef SHM_VAL_IS_STR
1858             const char **out_str, uint32_t *out_len, bool *out_utf8
1859             #else
1860             SHM_VAL_INT_TYPE *out_value
1861             #endif
1862             ) {
1863             #ifdef SHM_KEY_IS_INT
1864 10955 50         SHM_SHARD_DISPATCH(h, key);
  13 50          
  8 50          
  10 100          
  10906 50          
  8 50          
  10            
1865             #else
1866 3093 100         SHM_SHARD_DISPATCH(h, key_str, key_len);
  2565 50          
  515 50          
  5 50          
  8            
1867             #endif
1868             /* Unified seqlock path — lock-free for ALL maps including LRU/TTL.
1869             * LRU uses clock/second-chance: just set accessed bit, no wrlock.
1870             * TTL check is done under seqlock retry protection. */
1871 14048           ShmHeader *hdr = h->hdr;
  2565            
  515            
  5            
  8            
  13            
  8            
  10            
  10906            
  8            
  10            
1872 14048           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  2565            
  515            
  5            
  8            
  13            
  8            
  10            
  10906            
  8            
  10            
1873 14048           uint8_t *states = h->states;
  2565            
  515            
  5            
  8            
  13            
  8            
  10            
  10906            
  8            
  10            
1874 14048           uint32_t max_mask = h->max_mask;
  2565            
  515            
  5            
  8            
  13            
  8            
  10            
  10906            
  8            
  10            
1875             #if !defined(SHM_KEY_IS_INT) || defined(SHM_VAL_IS_STR)
1876 3124           uint64_t arena_cap = hdr->arena_cap; /* immutable after create */
  2565            
  515            
  5            
  8            
  13            
  8            
  10            
1877             #endif
1878             #ifndef SHM_KEY_IS_INT
1879 3093           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  2565            
  515            
  5            
  8            
1880             #else
1881 10955           uint32_t hash = SHM_HASH_KEY(key);
  13            
  8            
  10            
  10906            
  8            
  10            
1882             #endif
1883              
1884 0           for (;;) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1885 14048           uint32_t seq = shm_seqlock_read_begin(hdr);
  2565            
  515            
  5            
  8            
  13            
  8            
  10            
  10906            
  8            
  10            
1886              
1887 14048           uint32_t mask = (hdr->table_cap - 1) & max_mask;
  2565            
  515            
  5            
  8            
  13            
  8            
  10            
  10906            
  8            
  10            
1888 14048           uint32_t pos = hash & mask;
  2565            
  515            
  5            
  8            
  13            
  8            
  10            
  10906            
  8            
  10            
1889 14048           int found = 0;
  2565            
  515            
  5            
  8            
  13            
  8            
  10            
  10906            
  8            
  10            
1890 14048           uint32_t local_idx = 0;
  2565            
  515            
  5            
  8            
  13            
  8            
  10            
  10906            
  8            
  10            
1891              
1892             /* local copies of result data */
1893             #ifdef SHM_VAL_IS_STR
1894 2596           uint32_t local_vl = 0, local_voff = 0, local_vlen_packed = 0;
  2565            
  13            
  8            
  10            
1895             #else
1896 11452           SHM_VAL_INT_TYPE local_value = 0;
  515            
  5            
  8            
  10906            
  8            
  10            
1897             #endif
1898              
1899 14048           uint8_t tag = SHM_MAKE_TAG(hash);
  2565            
  515            
  5            
  8            
  13            
  8            
  10            
  10906            
  8            
  10            
1900 14048           uint32_t probe_start = 0; /* scalar loop starts here */
  2565            
  515            
  5            
  8            
  13            
  8            
  10            
  10906            
  8            
  10            
1901              
1902             #ifdef __SSE2__
1903             /* SIMD fast path: check first 16 states in one shot */
1904 14048 100         if (pos + 16 <= hdr->table_cap) {
  2565 100          
  515 100          
  5 50          
  8 50          
  13 50          
  8 50          
  10 100          
  10906 100          
  8 100          
  10            
1905             uint16_t mmask, emask;
1906 13565           shm_probe_group(states, pos, tag, &mmask, &emask);
  2493            
  495            
  1            
  0            
  0            
  0            
  0            
  10572            
  1            
  3            
1907             /* Only consider matches before first empty */
1908 13565 100         uint16_t cutoff = emask ? (uint16_t)((1U << __builtin_ctz(emask)) - 1) : 0xFFFF;
  2493 50          
  495 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  10572 50          
  1 50          
  3            
1909 13565           uint16_t relevant = mmask & cutoff;
  2493            
  495            
  1            
  0            
  0            
  0            
  0            
  10572            
  1            
  3            
1910 13609 100         while (relevant) {
  2499 50          
  496 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  10609 50          
  1 100          
  3            
1911 13520           int bit = __builtin_ctz(relevant);
  2498            
  496            
  1            
  0            
  0            
  0            
  0            
  10522            
  1            
  2            
1912 13520           uint32_t idx = pos + bit;
  2498            
  496            
  1            
  0            
  0            
  0            
  0            
  10522            
  1            
  2            
1913 13520           __builtin_prefetch(&nodes[idx], 0, 1);
  2498            
  496            
  1            
  0            
  0            
  0            
  0            
  10522            
  1            
  2            
1914             #ifdef SHM_KEY_IS_INT
1915 10525 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 0          
  0 100          
  10522 50          
  1 50          
  2            
1916             #else
1917             {
1918 2995           uint32_t kl_packed = nodes[idx].key_len;
  2498            
  496            
  1            
  0            
1919 2995 50         uint32_t kl = SHM_STR_LEN(kl_packed);
  2498 50          
  496 50          
  1 0          
  0            
1920             (void)key_utf8; /* flag is metadata for retrieval, not part of key identity */
1921 2995 100         if (kl != key_len) goto simd_next;
  2498 100          
  496 50          
  1 0          
  0            
1922 2990 50         if (SHM_IS_INLINE(kl_packed)) {
  2494 50          
  495 50          
  1 0          
  0            
1923             char ibuf[SHM_INLINE_MAX];
1924 2990           shm_inline_read(nodes[idx].key_off, kl_packed, ibuf);
  2494            
  495            
  1            
  0            
1925 2990 100         if (memcmp(ibuf, key_str, kl) != 0) goto simd_next;
  2494 50          
  495 50          
  1 0          
  0            
1926             } else {
1927 0           uint32_t koff = nodes[idx].key_off;
  0            
  0            
  0            
  0            
1928 2988 0         if ((uint64_t)koff + kl > arena_cap) goto simd_done;
  2492 0          
  495 0          
  1 0          
  0            
1929 0 0         if (memcmp(h->arena + koff, key_str, kl) != 0) goto simd_next;
  0 0          
  0 0          
  0 0          
  0            
1930             }
1931             }
1932             {
1933             #endif
1934             #ifdef SHM_VAL_IS_STR
1935 2492           local_vlen_packed = nodes[idx].val_len;
  2492            
  0            
  0            
  0            
1936 2492 100         local_vl = SHM_STR_LEN(local_vlen_packed);
  2492 0          
  0 0          
  0 0          
  0            
1937 2492           local_voff = nodes[idx].val_off;
  2492            
  0            
  0            
  0            
1938             #else
1939 10984           local_value = __atomic_load_n(&nodes[idx].value, __ATOMIC_RELAXED);
  495            
  1            
  0            
  10485            
  1            
  2            
1940             #endif
1941 13476           local_idx = idx;
  2492            
  495            
  1            
  0            
  0            
  0            
  0            
  10485            
  1            
  2            
1942 13476           found = 1;
  2492            
  495            
  1            
  0            
  0            
  0            
  0            
  10485            
  1            
  2            
1943 13533           goto simd_done;
  2492            
  495            
  1            
  0            
  0            
  0            
  0            
  10541            
  1            
  3            
1944             }
1945 44           simd_next:
  6            
  1            
  0            
  0            
  0            
  0            
  0            
  37            
  0            
  0            
1946 44           relevant &= relevant - 1;
  6            
  1            
  0            
  0            
  0            
  0            
  0            
  37            
  0            
  0            
1947             }
1948 89 50         if (emask) goto simd_done; /* hit empty — key absent */
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  87 0          
  0 50          
  1            
1949 32           probe_start = 16; /* all 16 occupied, continue scalar from pos+16 */
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  31            
  0            
  0            
1950             }
1951 483           simd_done:
  72            
  20            
  4            
  8            
  13            
  8            
  10            
  334            
  7            
  7            
1952             #endif
1953 14048 100         if (!found) {
  2565 100          
  515 100          
  5 50          
  8 50          
  13 50          
  8 50          
  10 100          
  10906 100          
  8 100          
  10            
1954 1982 50         for (uint32_t i = probe_start; i <= mask; i++) {
  109 50          
  22 50          
  4 50          
  9 50          
  15 50          
  11 50          
  13 50          
  1780 50          
  8 50          
  11            
1955 1982           uint32_t idx = (pos + i) & mask;
  109            
  22            
  4            
  9            
  15            
  11            
  13            
  1780            
  8            
  11            
1956 1982           uint8_t st = states[idx];
  109            
  22            
  4            
  9            
  15            
  11            
  13            
  1780            
  8            
  11            
1957 1982           __builtin_prefetch(&nodes[idx], 0, 1);
  109            
  22            
  4            
  9            
  15            
  11            
  13            
  1780            
  8            
  11            
1958 1982           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  109            
  22            
  4            
  9            
  15            
  11            
  13            
  1780            
  8            
  11            
1959              
1960 1982 100         if (st == SHM_EMPTY) break;
  109 100          
  22 50          
  4 50          
  9 50          
  15 50          
  11 100          
  13 100          
  1780 50          
  8 100          
  11            
1961 1895 100         if (st != tag) continue;
  104 100          
  20 50          
  4 100          
  9 100          
  15 100          
  11 100          
  12 100          
  1702 100          
  8 100          
  10            
1962              
1963             #ifdef SHM_KEY_IS_INT
1964 390 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  13 50          
  8 50          
  9 100          
  346 50          
  7 50          
  7            
1965             #else
1966             {
1967 98           uint32_t kl_packed = nodes[idx].key_len;
  68            
  18            
  4            
  8            
1968 98           uint32_t koff = nodes[idx].key_off;
  68            
  18            
  4            
  8            
1969 98 100         uint32_t kl = SHM_STR_LEN(kl_packed);
  68 50          
  18 50          
  4 50          
  8            
1970 98 50         if (kl != key_len) continue;
  68 50          
  18 50          
  4 50          
  8            
1971             /* key_utf8 is metadata for retrieval, not part of identity */
1972 98 100         if (SHM_IS_INLINE(kl_packed)) {
  68 50          
  18 50          
  4 50          
  8            
1973             char ibuf[SHM_INLINE_MAX];
1974 95           shm_inline_read(koff, kl_packed, ibuf);
  65            
  18            
  4            
  8            
1975 95 50         if (memcmp(ibuf, key_str, kl) != 0) continue;
  65 50          
  18 50          
  4 50          
  8            
1976             } else {
1977 3 50         if ((uint64_t)koff + kl > arena_cap) break;
  3 0          
  0 0          
  0 0          
  0            
1978 3 50         if (memcmp(h->arena + koff, key_str, kl) != 0) continue;
  3 0          
  0 0          
  0 0          
  0            
1979             }
1980             }
1981             {
1982             #endif
1983             #ifdef SHM_VAL_IS_STR
1984 98           local_vlen_packed = nodes[idx].val_len;
  68            
  13            
  8            
  9            
1985 98 100         local_vl = SHM_STR_LEN(local_vlen_packed);
  68 100          
  13 100          
  8 100          
  9            
1986 98           local_voff = nodes[idx].val_off;
  68            
  13            
  8            
  9            
1987             #else
1988 387           local_value = __atomic_load_n(&nodes[idx].value, __ATOMIC_RELAXED);
  18            
  4            
  8            
  343            
  7            
  7            
1989             #endif
1990 485           local_idx = idx;
  68            
  18            
  4            
  8            
  13            
  8            
  9            
  343            
  7            
  7            
1991 485           found = 1;
  68            
  18            
  4            
  8            
  13            
  8            
  9            
  343            
  7            
  7            
1992 485           break;
  68            
  18            
  4            
  8            
  13            
  8            
  9            
  343            
  7            
  7            
1993             }
1994             }
1995             }
1996              
1997 14048 100         if (found) {
  2565 100          
  515 50          
  5 50          
  8 50          
  13 50          
  8 100          
  10 100          
  10906 50          
  8 100          
  10            
1998             /* TTL check under seqlock (torn expiry caught by retry) */
1999 13961 100         if (h->expires_at) {
  2560 100          
  513 100          
  5 100          
  8 100          
  13 100          
  8 100          
  9 100          
  10828 100          
  8 100          
  9            
2000 56           uint32_t exp = h->expires_at[local_idx];
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  41            
  1            
  1            
2001 56 50         if (exp != 0 && shm_now() >= exp) {
  5 100          
  2 50          
  1 50          
  1 50          
  2 50          
  1 50          
  1 50          
  41 50          
  1 50          
  1 50          
    50          
    50          
    50          
    100          
    100          
    50          
    50          
    50          
    50          
2002 10           found = 0; /* expired — treat as absent */
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  9            
  0            
  0            
2003             }
2004             }
2005             }
2006              
2007 14048 100         if (found) {
  2565 100          
  515 50          
  5 50          
  8 50          
  13 50          
  8 100          
  10 100          
  10906 50          
  8 100          
  10            
2008             #ifdef SHM_VAL_IS_STR
2009 2589 100         if (SHM_IS_INLINE(local_vlen_packed)) {
  2559 100          
  13 100          
  8 100          
  9            
2010             /* Inline value — data is in local_voff + local_vlen_packed, no arena access */
2011 2572 50         if (local_vl > h->copy_buf_size || !h->copy_buf) {
  2548 50          
  12 50          
  4 50          
  8 50          
    50          
    50          
    50          
2012 0 0         if (!shm_ensure_copy_buf(h, local_vl > 0 ? local_vl : 1)) return 0;
  0 0          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
2013 0           continue;
  0            
  0            
  0            
  0            
2014             }
2015 2572           shm_inline_read(local_voff, local_vlen_packed, h->copy_buf);
  2548            
  12            
  4            
  8            
2016             } else {
2017             /* Arena value — bounds check before copy */
2018 17 50         if ((uint64_t)local_voff + local_vl > arena_cap) continue;
  11 50          
  1 50          
  4 50          
  1            
2019 17 50         if (local_vl > h->copy_buf_size || !h->copy_buf) {
  11 50          
  1 50          
  4 50          
  1 50          
    50          
    50          
    50          
2020 0 0         if (!shm_ensure_copy_buf(h, local_vl > 0 ? local_vl : 1)) return 0;
  0 0          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
2021 0           continue;
  0            
  0            
  0            
  0            
2022             }
2023 17           memcpy(h->copy_buf, h->arena + local_voff, local_vl);
  11            
  1            
  4            
  1            
2024             }
2025             #endif
2026 13951 50         if (shm_seqlock_read_retry(&hdr->seq, seq)) continue;
  2559 50          
  513 50          
  5 50          
  8 50          
  13 50          
  8 50          
  9 50          
  10819 50          
  8 50          
  9            
2027              
2028             /* validated — set clock accessed bit and commit results */
2029 13951 100         if (h->lru_accessed)
  2559 100          
  513 50          
  5 50          
  8 50          
  13 50          
  8 50          
  9 100          
  10819 50          
  8 50          
  9            
2030 44           __atomic_store_n(&h->lru_accessed[local_idx], 1, __ATOMIC_RELAXED);
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  41            
  0            
  0            
2031             #ifdef SHM_VAL_IS_STR
2032 2589           *out_str = h->copy_buf;
  2559            
  13            
  8            
  9            
2033 2589           *out_len = local_vl;
  2559            
  13            
  8            
  9            
2034 2589           *out_utf8 = SHM_UNPACK_UTF8(local_vlen_packed);
  2559            
  13            
  8            
  9            
2035             #else
2036 11362           *out_value = local_value;
  513            
  5            
  8            
  10819            
  8            
  9            
2037             #endif
2038 13951           return 1;
  2559            
  513            
  5            
  8            
  13            
  8            
  9            
  10819            
  8            
  9            
2039             }
2040              
2041 97 50         if (shm_seqlock_read_retry(&hdr->seq, seq)) continue;
  6 50          
  2 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 50          
  87 0          
  0 50          
  1            
2042 97           return 0;
  6            
  2            
  0            
  0            
  0            
  0            
  1            
  87            
  0            
  1            
2043             }
2044             }
2045              
2046             /* ---- Exists (with TTL check under rdlock) ---- */
2047              
2048 2           static int SHM_FN(exists_ttl)(ShmHandle *h,
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2049             #ifdef SHM_KEY_IS_INT
2050             SHM_KEY_INT_TYPE key
2051             #else
2052             const char *key_str, uint32_t key_len, bool key_utf8
2053             #endif
2054             ) {
2055             #ifdef SHM_KEY_IS_INT
2056 2 0         SHM_SHARD_DISPATCH(h, key);
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
2057             #else
2058 0 0         SHM_SHARD_DISPATCH(h, key_str, key_len);
  0 0          
  0 0          
  0 0          
  0            
2059             #endif
2060 2           ShmHeader *hdr = h->hdr;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2061 2           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2062 2           uint8_t *states = h->states;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2063 2           uint32_t now = shm_now();
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2064              
2065 2           shm_rwlock_rdlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2066              
2067 2           uint32_t mask = hdr->table_cap - 1;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2068             #ifdef SHM_KEY_IS_INT
2069 2           uint32_t hash = SHM_HASH_KEY(key);
  0            
  0            
  0            
  2            
  0            
  0            
2070             #else
2071 0           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  0            
  0            
  0            
  0            
2072             #endif
2073 2           uint32_t pos = hash & mask;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2074 2           uint8_t tag = SHM_MAKE_TAG(hash);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2075              
2076 2 0         for (uint32_t i = 0; i <= mask; i++) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
2077 2           uint32_t idx = (pos + i) & mask;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2078 2           uint8_t st = states[idx];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2079 2           __builtin_prefetch(&nodes[idx], 0, 1);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2080 2           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2081 2 0         if (st == SHM_EMPTY) break;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
2082 2 0         if (st != tag) continue; /* tombstone or tag mismatch */
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
2083             #ifdef SHM_KEY_IS_INT
2084 2 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
2085             #else
2086 0 0         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  0 0          
  0 0          
  0 0          
  0            
2087             #endif
2088 2 0         int found = !SHM_IS_EXPIRED(h, idx, now);
  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          
    0          
    0          
    0          
    50          
    50          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
2089 2           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2090 2           return found;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2091             }
2092             }
2093              
2094 0           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2095 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2096             }
2097              
2098             /* ---- Exists (seqlock — lock-free read path) ---- */
2099              
2100 78           static int SHM_FN(exists)(ShmHandle *h,
  11            
  2            
  0            
  1            
  0            
  0            
  1            
  61            
  0            
  2            
2101             #ifdef SHM_KEY_IS_INT
2102             SHM_KEY_INT_TYPE key
2103             #else
2104             const char *key_str, uint32_t key_len, bool key_utf8
2105             #endif
2106             ) {
2107             #ifdef SHM_KEY_IS_INT
2108 64 0         SHM_SHARD_DISPATCH(h, key);
  0 0          
  0 50          
  1 100          
  61 0          
  0 50          
  2            
2109             #else
2110 14 50         SHM_SHARD_DISPATCH(h, key_str, key_len);
  11 50          
  2 0          
  0 50          
  1            
2111             #endif
2112             /* TTL active: use rdlock path for expiry check */
2113 78 50         if (h->expires_at) {
  11 50          
  2 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 100          
  61 0          
  0 50          
  2            
2114             #ifdef SHM_KEY_IS_INT
2115 2           return SHM_FN(exists_ttl)(h, key);
  0            
  0            
  0            
  2            
  0            
  0            
2116             #else
2117 0           return SHM_FN(exists_ttl)(h, key_str, key_len, key_utf8);
  0            
  0            
  0            
  0            
2118             #endif
2119             }
2120              
2121 76           ShmHeader *hdr = h->hdr;
  11            
  2            
  0            
  1            
  0            
  0            
  1            
  59            
  0            
  2            
2122 76           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  11            
  2            
  0            
  1            
  0            
  0            
  1            
  59            
  0            
  2            
2123 76           uint8_t *states = h->states;
  11            
  2            
  0            
  1            
  0            
  0            
  1            
  59            
  0            
  2            
2124 76           uint32_t max_mask = h->max_mask;
  11            
  2            
  0            
  1            
  0            
  0            
  1            
  59            
  0            
  2            
2125             #ifndef SHM_KEY_IS_INT
2126 14           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  11            
  2            
  0            
  1            
2127             #else
2128 62           uint32_t hash = SHM_HASH_KEY(key);
  0            
  0            
  1            
  59            
  0            
  2            
2129             #endif
2130              
2131 0           for (;;) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2132 76           uint32_t seq = shm_seqlock_read_begin(hdr);
  11            
  2            
  0            
  1            
  0            
  0            
  1            
  59            
  0            
  2            
2133              
2134 76           uint32_t mask = (hdr->table_cap - 1) & max_mask;
  11            
  2            
  0            
  1            
  0            
  0            
  1            
  59            
  0            
  2            
2135 76           uint32_t pos = hash & mask;
  11            
  2            
  0            
  1            
  0            
  0            
  1            
  59            
  0            
  2            
2136 76           int found = 0;
  11            
  2            
  0            
  1            
  0            
  0            
  1            
  59            
  0            
  2            
2137              
2138 76           uint8_t tag = SHM_MAKE_TAG(hash);
  11            
  2            
  0            
  1            
  0            
  0            
  1            
  59            
  0            
  2            
2139 143 50         for (uint32_t i = 0; i <= mask; i++) {
  16 50          
  3 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  120 0          
  0 50          
  2            
2140 143           uint32_t idx = (pos + i) & mask;
  16            
  3            
  0            
  1            
  0            
  0            
  1            
  120            
  0            
  2            
2141 143           uint8_t st = states[idx];
  16            
  3            
  0            
  1            
  0            
  0            
  1            
  120            
  0            
  2            
2142 143           __builtin_prefetch(&nodes[idx], 0, 1);
  16            
  3            
  0            
  1            
  0            
  0            
  1            
  120            
  0            
  2            
2143 143           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  16            
  3            
  0            
  1            
  0            
  0            
  1            
  120            
  0            
  2            
2144 143 100         if (st == SHM_EMPTY) break;
  16 100          
  3 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 100          
  120 0          
  0 100          
  2            
2145 107 100         if (st != tag) continue; /* tombstone or tag mismatch */
  13 100          
  2 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 100          
  89 0          
  0 50          
  1            
2146             #ifdef SHM_KEY_IS_INT
2147 31 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 50          
  1 100          
  29 0          
  0 50          
  1            
2148             #else
2149 10 50         if (!SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) continue;
  8 50          
  1 0          
  0 50          
  1            
2150             {
2151             #endif
2152 40           found = 1;
  8            
  1            
  0            
  1            
  0            
  0            
  1            
  28            
  0            
  1            
2153 40           break;
  8            
  1            
  0            
  1            
  0            
  0            
  1            
  28            
  0            
  1            
2154             }
2155             }
2156              
2157 76 50         if (shm_seqlock_read_retry(&hdr->seq, seq)) continue;
  11 50          
  2 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  59 0          
  0 50          
  2            
2158 76           return found;
  11            
  2            
  0            
  1            
  0            
  0            
  1            
  59            
  0            
  2            
2159             }
2160             }
2161              
2162             /* ---- Remove ---- */
2163              
2164             /* Lockless remove body — caller must hold writer lock + seqlock and have
2165             * already shard-dispatched. Used by remove() and remove_multi. */
2166 18300           static int SHM_FN(remove_inner)(ShmHandle *h,
  5008            
  1            
  0            
  1            
  0            
  0            
  1            
  12788            
  500            
  1            
2167             #ifdef SHM_KEY_IS_INT
2168             SHM_KEY_INT_TYPE key
2169             #else
2170             const char *key_str, uint32_t key_len, bool key_utf8
2171             #endif
2172             ) {
2173 18300           ShmHeader *hdr = h->hdr;
  5008            
  1            
  0            
  1            
  0            
  0            
  1            
  12788            
  500            
  1            
2174 18300           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  5008            
  1            
  0            
  1            
  0            
  0            
  1            
  12788            
  500            
  1            
2175 18300           uint8_t *states = h->states;
  5008            
  1            
  0            
  1            
  0            
  0            
  1            
  12788            
  500            
  1            
2176 18300           uint32_t mask = hdr->table_cap - 1;
  5008            
  1            
  0            
  1            
  0            
  0            
  1            
  12788            
  500            
  1            
2177             #ifdef SHM_KEY_IS_INT
2178 13290           uint32_t hash = SHM_HASH_KEY(key);
  0            
  0            
  1            
  12788            
  500            
  1            
2179             #else
2180 5010           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  5008            
  1            
  0            
  1            
2181             #endif
2182 18300           uint32_t pos = hash & mask;
  5008            
  1            
  0            
  1            
  0            
  0            
  1            
  12788            
  500            
  1            
2183 18300           uint8_t tag = SHM_MAKE_TAG(hash);
  5008            
  1            
  0            
  1            
  0            
  0            
  1            
  12788            
  500            
  1            
2184 24270 50         for (uint32_t i = 0; i <= mask; i++) {
  6770 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  16906 50          
  590 50          
  1            
2185 24270           uint32_t idx = (pos + i) & mask;
  6770            
  1            
  0            
  1            
  0            
  0            
  1            
  16906            
  590            
  1            
2186 24270           uint8_t st = states[idx];
  6770            
  1            
  0            
  1            
  0            
  0            
  1            
  16906            
  590            
  1            
2187 24270           __builtin_prefetch(&nodes[idx], 0, 1);
  6770            
  1            
  0            
  1            
  0            
  0            
  1            
  16906            
  590            
  1            
2188 24270           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  6770            
  1            
  0            
  1            
  0            
  0            
  1            
  16906            
  590            
  1            
2189 24270 100         if (st == SHM_EMPTY) return 0;
  6770 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 100          
  16906 50          
  590 50          
  1            
2190 24268 100         if (st != tag) continue;
  6769 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 100          
  16905 100          
  590 50          
  1            
2191             #ifdef SHM_KEY_IS_INT
2192 13297 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 50          
  1 100          
  12795 50          
  500 50          
  1            
2193             #else
2194 5009 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  5007 50          
  1 0          
  0 50          
  1            
2195             #endif
2196 18298 50         if (h->lru_prev) shm_lru_unlink(h, idx);
  5007 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 100          
  12787 50          
  500 50          
  1            
2197 18298 50         if (h->expires_at) h->expires_at[idx] = 0;
  5007 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  12787 50          
  500 50          
  1            
2198 18298           SHM_FN(tombstone_at)(h, idx);
  5007            
  1            
  0            
  1            
  0            
  0            
  1            
  12787            
  500            
  1            
2199 18298           return 1;
  5007            
  1            
  0            
  1            
  0            
  0            
  1            
  12787            
  500            
  1            
2200             }
2201             }
2202 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2203             }
2204              
2205 18294           static int SHM_FN(remove)(ShmHandle *h,
  5005            
  1            
  0            
  1            
  0            
  0            
  1            
  12785            
  500            
  1            
2206             #ifdef SHM_KEY_IS_INT
2207             SHM_KEY_INT_TYPE key
2208             #else
2209             const char *key_str, uint32_t key_len, bool key_utf8
2210             #endif
2211             ) {
2212             #ifdef SHM_KEY_IS_INT
2213 13287 0         SHM_SHARD_DISPATCH(h, key);
  0 0          
  0 50          
  1 100          
  12785 50          
  500 50          
  1            
2214             #else
2215 5007 100         SHM_SHARD_DISPATCH(h, key_str, key_len);
  5005 50          
  1 0          
  0 50          
  1            
2216             #endif
2217 18294           ShmHeader *hdr = h->hdr;
  5005            
  1            
  0            
  1            
  0            
  0            
  1            
  12785            
  500            
  1            
2218 18294           shm_rwlock_wrlock(hdr);
  5005            
  1            
  0            
  1            
  0            
  0            
  1            
  12785            
  500            
  1            
2219 18294           shm_seqlock_write_begin(&hdr->seq);
  5005            
  1            
  0            
  1            
  0            
  0            
  1            
  12785            
  500            
  1            
2220 18294           int rc = SHM_FN(remove_inner)(h,
  5005            
  1            
  0            
  1            
  0            
  0            
  1            
  12785            
  500            
  1            
2221             #ifdef SHM_KEY_IS_INT
2222             key
2223             #else
2224             key_str, key_len, key_utf8
2225             #endif
2226             );
2227 18294 50         if (rc) SHM_FN(maybe_shrink)(h);
  5005 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  12785 50          
  500 50          
  1            
2228 18294           shm_seqlock_write_end(&hdr->seq);
  5005            
  1            
  0            
  1            
  0            
  0            
  1            
  12785            
  500            
  1            
2229 18294           shm_rwlock_wrunlock(hdr);
  5005            
  1            
  0            
  1            
  0            
  0            
  1            
  12785            
  500            
  1            
2230 18294           return rc;
  5005            
  1            
  0            
  1            
  0            
  0            
  1            
  12785            
  500            
  1            
2231             }
2232              
2233             /* ---- Add (insert only if key absent, returns 1=inserted, 0=already exists or full) ---- */
2234              
2235 53           static int SHM_FN(add_impl)(ShmHandle *h,
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  16            
  4            
  4            
2236             #ifdef SHM_KEY_IS_INT
2237             SHM_KEY_INT_TYPE key,
2238             #else
2239             const char *key_str, uint32_t key_len, bool key_utf8,
2240             #endif
2241             #ifdef SHM_VAL_IS_STR
2242             const char *val_str, uint32_t val_len, bool val_utf8,
2243             #else
2244             SHM_VAL_INT_TYPE value,
2245             #endif
2246             uint32_t ttl_sec
2247             ) {
2248             #ifdef SHM_KEY_IS_INT
2249 36 50         SHM_SHARD_DISPATCH(h, key);
  4 50          
  4 50          
  4 100          
  16 50          
  4 50          
  4            
2250             #else
2251 17 50         SHM_SHARD_DISPATCH(h, key_str, key_len);
  4 50          
  5 50          
  4 50          
  4            
2252             #endif
2253 53           ShmHeader *hdr = h->hdr;
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  16            
  4            
  4            
2254 53           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  16            
  4            
  4            
2255 53           uint8_t *states = h->states;
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  16            
  4            
  4            
2256 53 100         uint32_t now = h->expires_at ? shm_now() : 0;
  4 100          
  5 100          
  4 100          
  4 100          
  4 100          
  4 100          
  4 100          
  16 100          
  4 100          
  4            
2257              
2258 53           shm_rwlock_wrlock(hdr);
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  16            
  4            
  4            
2259 53           shm_seqlock_write_begin(&hdr->seq);
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  16            
  4            
  4            
2260              
2261 53           SHM_FN(maybe_grow)(h);
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  16            
  4            
  4            
2262 53           uint32_t mask = hdr->table_cap - 1;
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  16            
  4            
  4            
2263             #ifdef SHM_KEY_IS_INT
2264 36           uint32_t hash = SHM_HASH_KEY(key);
  4            
  4            
  4            
  16            
  4            
  4            
2265             #else
2266 17           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  4            
  5            
  4            
  4            
2267             #endif
2268 53           uint32_t pos = hash & mask;
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  16            
  4            
  4            
2269 53           uint8_t tag = SHM_MAKE_TAG(hash);
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  16            
  4            
  4            
2270 53           uint32_t insert_pos = UINT32_MAX;
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  16            
  4            
  4            
2271              
2272 54 50         for (uint32_t i = 0; i <= mask; i++) {
  4 50          
  5 50          
  4 50          
  4 50          
  4 50          
  4 50          
  4 50          
  17 50          
  4 50          
  4            
2273 54           uint32_t idx = (pos + i) & mask;
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  17            
  4            
  4            
2274 54           uint8_t st = states[idx];
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  17            
  4            
  4            
2275 54           __builtin_prefetch(&nodes[idx], 0, 1);
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  17            
  4            
  4            
2276 54           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  4            
  5            
  4            
  4            
  4            
  4            
  4            
  17            
  4            
  4            
2277 54 100         if (st == SHM_EMPTY) {
  4 100          
  5 100          
  4 100          
  4 100          
  4 100          
  4 100          
  4 100          
  17 100          
  4 100          
  4            
2278 28 50         if (insert_pos == UINT32_MAX) insert_pos = idx;
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  10 50          
  2 50          
  2            
2279 28           break;
  2            
  2            
  2            
  2            
  2            
  2            
  2            
  10            
  2            
  2            
2280             }
2281 26 50         if (st == SHM_TOMBSTONE) {
  2 50          
  3 50          
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  7 50          
  2 50          
  2            
2282 0 0         if (insert_pos == UINT32_MAX) insert_pos = idx;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
2283 0           continue;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2284             }
2285 26 50         if (st != tag) continue;
  2 50          
  3 50          
  2 50          
  2 50          
  2 50          
  2 50          
  2 100          
  7 50          
  2 50          
  2            
2286             #ifdef SHM_KEY_IS_INT
2287 16 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  2 50          
  2 50          
  2 50          
  6 50          
  2 50          
  2            
2288             #else
2289 9 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  2 50          
  3 50          
  2 50          
  2            
2290             #endif
2291             /* Live key blocks add; expired entries are reclaimed */
2292 25 100         if (SHM_IS_EXPIRED(h, idx, now)) {
  2 50          
  3 50          
  2 100          
  2 50          
  2 50          
  2 100          
  2 50          
  6 50          
  2 100          
  2 50          
    50          
    100          
    50          
    50          
    100          
    50          
    50          
    100          
    50          
    50          
    100          
    50          
    100          
    100          
    50          
    50          
    100          
    50          
    50          
2293 2           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2294 2 0         if (insert_pos == UINT32_MAX) insert_pos = idx;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
2295 2           break;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2296             }
2297 23           shm_seqlock_write_end(&hdr->seq);
  2            
  3            
  2            
  2            
  2            
  2            
  2            
  4            
  2            
  2            
2298 23           shm_rwlock_wrunlock(hdr);
  2            
  3            
  2            
  2            
  2            
  2            
  2            
  4            
  2            
  2            
2299 23           return 0;
  2            
  3            
  2            
  2            
  2            
  2            
  2            
  4            
  2            
  2            
2300             }
2301             }
2302              
2303 30 50         if (insert_pos == UINT32_MAX) {
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  12 50          
  2 50          
  2            
2304 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2305 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2306 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2307             }
2308              
2309 30 50         if (hdr->max_size > 0 && hdr->size >= hdr->max_size)
  2 0          
  2 50          
  2 0          
  2 50          
  2 0          
  2 50          
  2 0          
  12 50          
  2 0          
  2 50          
    0          
    50          
    0          
    100          
    50          
    50          
    0          
    50          
    0          
2310 1           SHM_FN(lru_evict_one)(h);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2311              
2312 30           int was_tombstone = (states[insert_pos] == SHM_TOMBSTONE);
  2            
  2            
  2            
  2            
  2            
  2            
  2            
  12            
  2            
  2            
2313             #ifdef SHM_KEY_IS_INT
2314 22           nodes[insert_pos].key = key;
  2            
  2            
  2            
  12            
  2            
  2            
2315             #else
2316 8 50         if (!shm_str_store(hdr, h->arena, &nodes[insert_pos].key_off, &nodes[insert_pos].key_len, key_str, key_len, key_utf8)) {
  2 50          
  2 50          
  2 50          
  2            
2317 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2318 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2319 0           return 0;
  0            
  0            
  0            
  0            
2320             }
2321             #endif
2322             #ifdef SHM_VAL_IS_STR
2323 8 50         if (!shm_str_store(hdr, h->arena, &nodes[insert_pos].val_off, &nodes[insert_pos].val_len, val_str, val_len, val_utf8)) {
  2 50          
  2 50          
  2 50          
  2            
2324             #ifndef SHM_KEY_IS_INT
2325 0           shm_str_free(hdr, h->arena, nodes[insert_pos].key_off, nodes[insert_pos].key_len);
  0            
2326             #endif
2327 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2328 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2329 0           return 0;
  0            
  0            
  0            
  0            
2330             }
2331             #else
2332 22           nodes[insert_pos].value = value;
  2            
  2            
  2            
  12            
  2            
  2            
2333             #endif
2334 30           states[insert_pos] = SHM_MAKE_TAG(hash);
  2            
  2            
  2            
  2            
  2            
  2            
  2            
  12            
  2            
  2            
2335 30           hdr->size++;
  2            
  2            
  2            
  2            
  2            
  2            
  2            
  12            
  2            
  2            
2336 30 50         if (was_tombstone) hdr->tombstones--;
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  2 100          
  12 50          
  2 50          
  2            
2337              
2338 30 50         if (h->lru_prev) shm_lru_push_front(h, insert_pos);
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  2 100          
  12 50          
  2 50          
  2            
2339 30 100         if (h->expires_at) {
  2 100          
  2 100          
  2 100          
  2 100          
  2 100          
  2 100          
  2 100          
  12 100          
  2 100          
  2            
2340 15 50         uint32_t ttl = (ttl_sec == SHM_TTL_USE_DEFAULT) ? hdr->default_ttl : ttl_sec;
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 100          
  6 50          
  1 50          
  1            
2341 15 50         h->expires_at[insert_pos] = ttl > 0 ? shm_expiry_ts(ttl) : 0;
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 100          
  6 50          
  1 50          
  1            
2342             }
2343              
2344 30           shm_seqlock_write_end(&hdr->seq);
  2            
  2            
  2            
  2            
  2            
  2            
  2            
  12            
  2            
  2            
2345 30           shm_rwlock_wrunlock(hdr);
  2            
  2            
  2            
  2            
  2            
  2            
  2            
  12            
  2            
  2            
2346 30           return 1;
  2            
  2            
  2            
  2            
  2            
  2            
  2            
  12            
  2            
  2            
2347             }
2348              
2349 30           static inline int SHM_FN(add)(ShmHandle *h,
  2            
  3            
  2            
  2            
  2            
  2            
  2            
  11            
  2            
  2            
2350             #ifdef SHM_KEY_IS_INT
2351             SHM_KEY_INT_TYPE key,
2352             #else
2353             const char *key_str, uint32_t key_len, bool key_utf8,
2354             #endif
2355             #ifdef SHM_VAL_IS_STR
2356             const char *val_str, uint32_t val_len, bool val_utf8
2357             #else
2358             SHM_VAL_INT_TYPE value
2359             #endif
2360             ) {
2361 30           return SHM_FN(add_impl)(h,
  2            
  3            
  2            
  2            
  2            
  2            
  2            
  11            
  2            
  2            
2362             #ifdef SHM_KEY_IS_INT
2363             key,
2364             #else
2365             key_str, key_len, key_utf8,
2366             #endif
2367             #ifdef SHM_VAL_IS_STR
2368             val_str, val_len, val_utf8,
2369             #else
2370             value,
2371             #endif
2372             SHM_TTL_USE_DEFAULT);
2373             }
2374              
2375 23           static inline int SHM_FN(add_ttl)(ShmHandle *h,
  2            
  2            
  2            
  2            
  2            
  2            
  2            
  5            
  2            
  2            
2376             #ifdef SHM_KEY_IS_INT
2377             SHM_KEY_INT_TYPE key,
2378             #else
2379             const char *key_str, uint32_t key_len, bool key_utf8,
2380             #endif
2381             #ifdef SHM_VAL_IS_STR
2382             const char *val_str, uint32_t val_len, bool val_utf8,
2383             #else
2384             SHM_VAL_INT_TYPE value,
2385             #endif
2386             uint32_t ttl_sec
2387             ) {
2388 23 50         if (ttl_sec >= SHM_TTL_USE_DEFAULT - 1) ttl_sec = SHM_TTL_USE_DEFAULT - 2;
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  2 50          
  5 50          
  2 50          
  2            
2389 23           return SHM_FN(add_impl)(h,
  2            
  2            
  2            
  2            
  2            
  2            
  2            
  5            
  2            
  2            
2390             #ifdef SHM_KEY_IS_INT
2391             key,
2392             #else
2393             key_str, key_len, key_utf8,
2394             #endif
2395             #ifdef SHM_VAL_IS_STR
2396             val_str, val_len, val_utf8,
2397             #else
2398             value,
2399             #endif
2400             ttl_sec);
2401             }
2402              
2403             /* ---- Update (overwrite only if key exists, returns 1=updated, 0=not found) ---- */
2404              
2405 23           static int SHM_FN(update_impl)(ShmHandle *h,
  4            
  2            
  1            
  1            
  1            
  1            
  1            
  9            
  1            
  2            
2406             #ifdef SHM_KEY_IS_INT
2407             SHM_KEY_INT_TYPE key,
2408             #else
2409             const char *key_str, uint32_t key_len, bool key_utf8,
2410             #endif
2411             #ifdef SHM_VAL_IS_STR
2412             const char *val_str, uint32_t val_len, bool val_utf8,
2413             #else
2414             SHM_VAL_INT_TYPE value,
2415             #endif
2416             uint32_t ttl_sec
2417             ) {
2418             #ifdef SHM_KEY_IS_INT
2419 15 50         SHM_SHARD_DISPATCH(h, key);
  1 50          
  1 50          
  1 100          
  9 50          
  1 50          
  2            
2420             #else
2421 8 50         SHM_SHARD_DISPATCH(h, key_str, key_len);
  4 50          
  2 50          
  1 50          
  1            
2422             #endif
2423 23           ShmHeader *hdr = h->hdr;
  4            
  2            
  1            
  1            
  1            
  1            
  1            
  9            
  1            
  2            
2424 23           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  4            
  2            
  1            
  1            
  1            
  1            
  1            
  9            
  1            
  2            
2425 23           uint8_t *states = h->states;
  4            
  2            
  1            
  1            
  1            
  1            
  1            
  9            
  1            
  2            
2426 23 100         uint32_t now = h->expires_at ? shm_now() : 0;
  4 50          
  2 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 100          
  9 50          
  1 50          
  2            
2427              
2428 23           shm_rwlock_wrlock(hdr);
  4            
  2            
  1            
  1            
  1            
  1            
  1            
  9            
  1            
  2            
2429 23           shm_seqlock_write_begin(&hdr->seq);
  4            
  2            
  1            
  1            
  1            
  1            
  1            
  9            
  1            
  2            
2430              
2431 23           uint32_t mask = hdr->table_cap - 1;
  4            
  2            
  1            
  1            
  1            
  1            
  1            
  9            
  1            
  2            
2432             #ifdef SHM_KEY_IS_INT
2433 15           uint32_t hash = SHM_HASH_KEY(key);
  1            
  1            
  1            
  9            
  1            
  2            
2434             #else
2435 8           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  4            
  2            
  1            
  1            
2436             #endif
2437 23           uint32_t pos = hash & mask;
  4            
  2            
  1            
  1            
  1            
  1            
  1            
  9            
  1            
  2            
2438 23           uint8_t tag = SHM_MAKE_TAG(hash);
  4            
  2            
  1            
  1            
  1            
  1            
  1            
  9            
  1            
  2            
2439              
2440 24 50         for (uint32_t i = 0; i <= mask; i++) {
  4 50          
  2 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  10 50          
  1 50          
  2            
2441 24           uint32_t idx = (pos + i) & mask;
  4            
  2            
  1            
  1            
  1            
  1            
  1            
  10            
  1            
  2            
2442 24           uint8_t st = states[idx];
  4            
  2            
  1            
  1            
  1            
  1            
  1            
  10            
  1            
  2            
2443 24           __builtin_prefetch(&nodes[idx], 0, 1);
  4            
  2            
  1            
  1            
  1            
  1            
  1            
  10            
  1            
  2            
2444 24           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  4            
  2            
  1            
  1            
  1            
  1            
  1            
  10            
  1            
  2            
2445 24 100         if (st == SHM_EMPTY) break;
  4 50          
  2 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 100          
  10 50          
  1 100          
  2            
2446 18 50         if (st != tag) continue;
  2 50          
  2 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 100          
  7 50          
  1 50          
  1            
2447             #ifdef SHM_KEY_IS_INT
2448 11 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  1 50          
  1 50          
  1 50          
  6 50          
  1 50          
  1            
2449             #else
2450 6 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  2 50          
  2 50          
  1 50          
  1            
2451             #endif
2452 17 100         if (SHM_IS_EXPIRED(h, idx, now)) {
  2 50          
  2 50          
  1 50          
  1 0          
  1 0          
  1 50          
  1 0          
  6 0          
  1 50          
  1 0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    100          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
    0          
2453 0           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2454 0           SHM_FN(maybe_shrink)(h);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2455 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2456 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2457 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2458             }
2459             #ifdef SHM_VAL_IS_STR
2460             {
2461 5           uint32_t old_off = nodes[idx].val_off;
  2            
  1            
  1            
  1            
2462 5           uint32_t old_lf = nodes[idx].val_len;
  2            
  1            
  1            
  1            
2463 5 50         if (!shm_str_store(hdr, h->arena, &nodes[idx].val_off, &nodes[idx].val_len, val_str, val_len, val_utf8)) {
  2 50          
  1 50          
  1 50          
  1            
2464 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2465 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2466 0           return 0;
  0            
  0            
  0            
  0            
2467             }
2468 5           shm_str_free(hdr, h->arena, old_off, old_lf);
  2            
  1            
  1            
  1            
2469             }
2470             #else
2471 12           nodes[idx].value = value;
  2            
  1            
  1            
  6            
  1            
  1            
2472             #endif
2473 17 50         if (h->lru_prev) shm_lru_promote(h, idx);
  2 50          
  2 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  6 50          
  1 50          
  1            
2474 17 100         if (h->expires_at) {
  2 50          
  2 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 100          
  6 50          
  1 50          
  1            
2475 4 50         if (ttl_sec == SHM_TTL_USE_DEFAULT) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  3 0          
  0 0          
  0            
2476 1 0         if (hdr->default_ttl > 0 && h->expires_at[idx] != 0)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  1 0          
  0 0          
  0 0          
    0          
    0          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
2477 1           h->expires_at[idx] = shm_expiry_ts(hdr->default_ttl);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2478             } else {
2479 3 50         h->expires_at[idx] = ttl_sec > 0 ? shm_expiry_ts(ttl_sec) : 0;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  2 0          
  0 0          
  0            
2480             }
2481             }
2482              
2483 17           shm_seqlock_write_end(&hdr->seq);
  2            
  2            
  1            
  1            
  1            
  1            
  1            
  6            
  1            
  1            
2484 17           shm_rwlock_wrunlock(hdr);
  2            
  2            
  1            
  1            
  1            
  1            
  1            
  6            
  1            
  1            
2485 17           return 1;
  2            
  2            
  1            
  1            
  1            
  1            
  1            
  6            
  1            
  1            
2486             }
2487             }
2488              
2489 6           shm_seqlock_write_end(&hdr->seq);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  1            
2490 6           shm_rwlock_wrunlock(hdr);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  1            
2491 6           return 0;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  1            
2492             }
2493              
2494 18           static inline int SHM_FN(update)(ShmHandle *h,
  2            
  2            
  1            
  1            
  1            
  1            
  1            
  6            
  1            
  2            
2495             #ifdef SHM_KEY_IS_INT
2496             SHM_KEY_INT_TYPE key,
2497             #else
2498             const char *key_str, uint32_t key_len, bool key_utf8,
2499             #endif
2500             #ifdef SHM_VAL_IS_STR
2501             const char *val_str, uint32_t val_len, bool val_utf8
2502             #else
2503             SHM_VAL_INT_TYPE value
2504             #endif
2505             ) {
2506 18           return SHM_FN(update_impl)(h,
  2            
  2            
  1            
  1            
  1            
  1            
  1            
  6            
  1            
  2            
2507             #ifdef SHM_KEY_IS_INT
2508             key,
2509             #else
2510             key_str, key_len, key_utf8,
2511             #endif
2512             #ifdef SHM_VAL_IS_STR
2513             val_str, val_len, val_utf8,
2514             #else
2515             value,
2516             #endif
2517             SHM_TTL_USE_DEFAULT);
2518             }
2519              
2520 5           static inline int SHM_FN(update_ttl)(ShmHandle *h,
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
2521             #ifdef SHM_KEY_IS_INT
2522             SHM_KEY_INT_TYPE key,
2523             #else
2524             const char *key_str, uint32_t key_len, bool key_utf8,
2525             #endif
2526             #ifdef SHM_VAL_IS_STR
2527             const char *val_str, uint32_t val_len, bool val_utf8,
2528             #else
2529             SHM_VAL_INT_TYPE value,
2530             #endif
2531             uint32_t ttl_sec
2532             ) {
2533 5 50         if (ttl_sec >= SHM_TTL_USE_DEFAULT - 1) ttl_sec = SHM_TTL_USE_DEFAULT - 2;
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
2534 5           return SHM_FN(update_impl)(h,
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
2535             #ifdef SHM_KEY_IS_INT
2536             key,
2537             #else
2538             key_str, key_len, key_utf8,
2539             #endif
2540             #ifdef SHM_VAL_IS_STR
2541             val_str, val_len, val_utf8,
2542             #else
2543             value,
2544             #endif
2545             ttl_sec);
2546             }
2547              
2548             /* ---- Swap (put + return old value; returns 1=swapped existing, 2=inserted new, 0=full) ---- */
2549              
2550 24           static int SHM_FN(swap)(ShmHandle *h,
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  8            
  1            
  2            
2551             #ifdef SHM_KEY_IS_INT
2552             SHM_KEY_INT_TYPE key,
2553             #else
2554             const char *key_str, uint32_t key_len, bool key_utf8,
2555             #endif
2556             #ifdef SHM_VAL_IS_STR
2557             const char *val_str, uint32_t val_len, bool val_utf8,
2558             const char **out_str, uint32_t *out_len, bool *out_utf8
2559             #else
2560             SHM_VAL_INT_TYPE value,
2561             SHM_VAL_INT_TYPE *out_value
2562             #endif
2563             ) {
2564             #ifdef SHM_KEY_IS_INT
2565 15 50         SHM_SHARD_DISPATCH(h, key);
  2 50          
  1 50          
  1 100          
  8 50          
  1 50          
  2            
2566             #else
2567 9 50         SHM_SHARD_DISPATCH(h, key_str, key_len);
  5 50          
  2 50          
  1 50          
  1            
2568             #endif
2569 24           ShmHeader *hdr = h->hdr;
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  8            
  1            
  2            
2570 24           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  8            
  1            
  2            
2571 24           uint8_t *states = h->states;
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  8            
  1            
  2            
2572 24 50         uint32_t now = h->expires_at ? shm_now() : 0;
  5 50          
  2 50          
  1 50          
  1 50          
  2 50          
  1 50          
  1 100          
  8 50          
  1 50          
  2            
2573              
2574 24           shm_rwlock_wrlock(hdr);
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  8            
  1            
  2            
2575 24           shm_seqlock_write_begin(&hdr->seq);
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  8            
  1            
  2            
2576              
2577 24           SHM_FN(maybe_grow)(h);
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  8            
  1            
  2            
2578 24           uint32_t mask = hdr->table_cap - 1;
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  8            
  1            
  2            
2579             #ifdef SHM_KEY_IS_INT
2580 15           uint32_t hash = SHM_HASH_KEY(key);
  2            
  1            
  1            
  8            
  1            
  2            
2581             #else
2582 9           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  5            
  2            
  1            
  1            
2583             #endif
2584 24           uint32_t pos = hash & mask;
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  8            
  1            
  2            
2585 24           uint8_t tag = SHM_MAKE_TAG(hash);
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  8            
  1            
  2            
2586 24           uint32_t insert_pos = UINT32_MAX;
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  8            
  1            
  2            
2587              
2588 25 50         for (uint32_t i = 0; i <= mask; i++) {
  5 50          
  2 50          
  1 50          
  1 50          
  2 50          
  1 50          
  1 50          
  9 50          
  1 50          
  2            
2589 25           uint32_t idx = (pos + i) & mask;
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  9            
  1            
  2            
2590 25           uint8_t st = states[idx];
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  9            
  1            
  2            
2591 25           __builtin_prefetch(&nodes[idx], 0, 1);
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  9            
  1            
  2            
2592 25           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  5            
  2            
  1            
  1            
  2            
  1            
  1            
  9            
  1            
  2            
2593 25 100         if (st == SHM_EMPTY) {
  5 50          
  2 50          
  1 50          
  1 100          
  2 50          
  1 50          
  1 100          
  9 50          
  1 100          
  2            
2594 7 50         if (insert_pos == UINT32_MAX) insert_pos = idx;
  2 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  3 0          
  0 50          
  1            
2595 7           break;
  2            
  0            
  0            
  0            
  1            
  0            
  0            
  3            
  0            
  1            
2596             }
2597 18 50         if (st == SHM_TOMBSTONE) {
  3 50          
  2 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  6 50          
  1 50          
  1            
2598 0 0         if (insert_pos == UINT32_MAX) insert_pos = idx;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
2599 0           continue;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2600             }
2601 18 50         if (st != tag) continue;
  3 50          
  2 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 100          
  6 50          
  1 50          
  1            
2602             #ifdef SHM_KEY_IS_INT
2603 10 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  1 50          
  1 50          
  1 50          
  5 50          
  1 50          
  1            
2604             #else
2605 7 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  3 50          
  2 50          
  1 50          
  1            
2606             #endif
2607 17 50         if (SHM_IS_EXPIRED(h, idx, now)) {
  3 0          
  2 0          
  1 50          
  1 0          
  1 0          
  1 50          
  1 0          
  5 0          
  1 50          
  1 0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    100          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
    0          
2608 0           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2609 0 0         if (insert_pos == UINT32_MAX) insert_pos = idx;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
2610 0           break;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2611             }
2612             /* Copy old value out, then overwrite */
2613             #ifdef SHM_VAL_IS_STR
2614             {
2615 6 50         uint32_t old_vl = SHM_STR_LEN(nodes[idx].val_len);
  3 50          
  1 50          
  1 50          
  1            
2616 6 50         if (!shm_ensure_copy_buf(h, old_vl)) {
  3 50          
  1 50          
  1 50          
  1            
2617 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2618 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2619 0           return 0;
  0            
  0            
  0            
  0            
2620             }
2621 6           shm_str_copy(h->copy_buf, nodes[idx].val_off, nodes[idx].val_len, h->arena, old_vl);
  3            
  1            
  1            
  1            
2622 6           *out_str = h->copy_buf;
  3            
  1            
  1            
  1            
2623 6           *out_len = old_vl;
  3            
  1            
  1            
  1            
2624 6           *out_utf8 = SHM_UNPACK_UTF8(nodes[idx].val_len);
  3            
  1            
  1            
  1            
2625              
2626             {
2627 6           uint32_t old_off = nodes[idx].val_off;
  3            
  1            
  1            
  1            
2628 6           uint32_t old_lf = nodes[idx].val_len;
  3            
  1            
  1            
  1            
2629 6 50         if (!shm_str_store(hdr, h->arena, &nodes[idx].val_off, &nodes[idx].val_len, val_str, val_len, val_utf8)) {
  3 50          
  1 50          
  1 50          
  1            
2630 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2631 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2632 0           return 0;
  0            
  0            
  0            
  0            
2633             }
2634 6           shm_str_free(hdr, h->arena, old_off, old_lf);
  3            
  1            
  1            
  1            
2635             }
2636             }
2637             #else
2638 11           *out_value = nodes[idx].value;
  2            
  1            
  1            
  5            
  1            
  1            
2639 11           nodes[idx].value = value;
  2            
  1            
  1            
  5            
  1            
  1            
2640             #endif
2641 17 50         if (h->lru_prev) shm_lru_promote(h, idx);
  3 50          
  2 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  5 50          
  1 50          
  1            
2642 17 50         if (h->expires_at && hdr->default_ttl > 0 && h->expires_at[idx] != 0)
  3 0          
  2 0          
  1 50          
  1 0          
  1 0          
  1 50          
  1 0          
  5 0          
  1 50          
  1 0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    100          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
    0          
2643 1           h->expires_at[idx] = shm_expiry_ts(hdr->default_ttl);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2644              
2645 17           shm_seqlock_write_end(&hdr->seq);
  3            
  2            
  1            
  1            
  1            
  1            
  1            
  5            
  1            
  1            
2646 17           shm_rwlock_wrunlock(hdr);
  3            
  2            
  1            
  1            
  1            
  1            
  1            
  5            
  1            
  1            
2647 17           return 1; /* swapped existing */
  3            
  2            
  1            
  1            
  1            
  1            
  1            
  5            
  1            
  1            
2648             }
2649             }
2650              
2651             /* Key not found — insert new */
2652 7 50         if (insert_pos == UINT32_MAX) {
  2 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  3 0          
  0 50          
  1            
2653 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2654 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2655 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2656             }
2657              
2658             /* LRU eviction if at capacity */
2659 7 50         if (hdr->max_size > 0 && hdr->size >= hdr->max_size)
  2 0          
  0 0          
  0 0          
  0 0          
  1 0          
  0 0          
  0 0          
  3 50          
  0 0          
  1 0          
    0          
    0          
    0          
    100          
    50          
    0          
    0          
    50          
    0          
2660 1           SHM_FN(lru_evict_one)(h);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2661              
2662 7           int was_tombstone = (states[insert_pos] == SHM_TOMBSTONE);
  2            
  0            
  0            
  0            
  1            
  0            
  0            
  3            
  0            
  1            
2663             #ifdef SHM_KEY_IS_INT
2664 5           nodes[insert_pos].key = key;
  1            
  0            
  0            
  3            
  0            
  1            
2665             #else
2666 2 50         if (!shm_str_store(hdr, h->arena, &nodes[insert_pos].key_off, &nodes[insert_pos].key_len, key_str, key_len, key_utf8)) {
  2 0          
  0 0          
  0 0          
  0            
2667 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2668 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2669 0           return 0;
  0            
  0            
  0            
  0            
2670             }
2671             #endif
2672             #ifdef SHM_VAL_IS_STR
2673 3 50         if (!shm_str_store(hdr, h->arena, &nodes[insert_pos].val_off, &nodes[insert_pos].val_len, val_str, val_len, val_utf8)) {
  2 50          
  1 0          
  0 0          
  0            
2674             #ifndef SHM_KEY_IS_INT
2675 0           shm_str_free(hdr, h->arena, nodes[insert_pos].key_off, nodes[insert_pos].key_len);
  0            
2676             #endif
2677 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2678 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2679 0           return 0;
  0            
  0            
  0            
  0            
2680             }
2681             #else
2682 4           nodes[insert_pos].value = value;
  0            
  0            
  0            
  3            
  0            
  1            
2683             #endif
2684 7           states[insert_pos] = SHM_MAKE_TAG(hash);
  2            
  0            
  0            
  0            
  1            
  0            
  0            
  3            
  0            
  1            
2685 7           hdr->size++;
  2            
  0            
  0            
  0            
  1            
  0            
  0            
  3            
  0            
  1            
2686 7 50         if (was_tombstone) hdr->tombstones--;
  2 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  3 0          
  0 50          
  1            
2687              
2688 7 50         if (h->lru_prev) shm_lru_push_front(h, insert_pos);
  2 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 100          
  3 0          
  0 50          
  1            
2689 7 50         if (h->expires_at) {
  2 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  3 0          
  0 50          
  1            
2690 0           uint32_t ttl = hdr->default_ttl;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2691 0 0         h->expires_at[insert_pos] = ttl > 0 ? shm_expiry_ts(ttl) : 0;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
2692             }
2693              
2694 7           shm_seqlock_write_end(&hdr->seq);
  2            
  0            
  0            
  0            
  1            
  0            
  0            
  3            
  0            
  1            
2695 7           shm_rwlock_wrunlock(hdr);
  2            
  0            
  0            
  0            
  1            
  0            
  0            
  3            
  0            
  1            
2696 7           return 2; /* inserted new (no old value) */
  2            
  0            
  0            
  0            
  1            
  0            
  0            
  3            
  0            
  1            
2697             }
2698              
2699             /* ---- Take (remove and return value) ---- */
2700              
2701 14           static int SHM_FN(take)(ShmHandle *h,
  6            
  2            
  0            
  0            
  1            
  0            
  0            
  5            
  0            
  0            
2702             #ifdef SHM_KEY_IS_INT
2703             SHM_KEY_INT_TYPE key,
2704             #else
2705             const char *key_str, uint32_t key_len, bool key_utf8,
2706             #endif
2707             #ifdef SHM_VAL_IS_STR
2708             const char **out_str, uint32_t *out_len, bool *out_utf8
2709             #else
2710             SHM_VAL_INT_TYPE *out_value
2711             #endif
2712             ) {
2713             #ifdef SHM_KEY_IS_INT
2714 6 50         SHM_SHARD_DISPATCH(h, key);
  1 0          
  0 0          
  0 100          
  5 0          
  0 0          
  0            
2715             #else
2716 8 100         SHM_SHARD_DISPATCH(h, key_str, key_len);
  6 50          
  2 0          
  0 0          
  0            
2717             #endif
2718 14           ShmHeader *hdr = h->hdr;
  6            
  2            
  0            
  0            
  1            
  0            
  0            
  5            
  0            
  0            
2719 14           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  6            
  2            
  0            
  0            
  1            
  0            
  0            
  5            
  0            
  0            
2720 14           uint8_t *states = h->states;
  6            
  2            
  0            
  0            
  1            
  0            
  0            
  5            
  0            
  0            
2721 14 50         uint32_t now = h->expires_at ? shm_now() : 0;
  6 50          
  2 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 100          
  5 0          
  0 0          
  0            
2722              
2723 14           shm_rwlock_wrlock(hdr);
  6            
  2            
  0            
  0            
  1            
  0            
  0            
  5            
  0            
  0            
2724 14           shm_seqlock_write_begin(&hdr->seq);
  6            
  2            
  0            
  0            
  1            
  0            
  0            
  5            
  0            
  0            
2725              
2726 14           uint32_t mask = hdr->table_cap - 1;
  6            
  2            
  0            
  0            
  1            
  0            
  0            
  5            
  0            
  0            
2727             #ifdef SHM_KEY_IS_INT
2728 6           uint32_t hash = SHM_HASH_KEY(key);
  1            
  0            
  0            
  5            
  0            
  0            
2729             #else
2730 8           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  6            
  2            
  0            
  0            
2731             #endif
2732 14           uint32_t pos = hash & mask;
  6            
  2            
  0            
  0            
  1            
  0            
  0            
  5            
  0            
  0            
2733 14           uint8_t tag = SHM_MAKE_TAG(hash);
  6            
  2            
  0            
  0            
  1            
  0            
  0            
  5            
  0            
  0            
2734              
2735 15 50         for (uint32_t i = 0; i <= mask; i++) {
  7 50          
  2 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  5 0          
  0 0          
  0            
2736 15           uint32_t idx = (pos + i) & mask;
  7            
  2            
  0            
  0            
  1            
  0            
  0            
  5            
  0            
  0            
2737 15           uint8_t st = states[idx];
  7            
  2            
  0            
  0            
  1            
  0            
  0            
  5            
  0            
  0            
2738 15           __builtin_prefetch(&nodes[idx], 0, 1);
  7            
  2            
  0            
  0            
  1            
  0            
  0            
  5            
  0            
  0            
2739 15           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  7            
  2            
  0            
  0            
  1            
  0            
  0            
  5            
  0            
  0            
2740 15 100         if (st == SHM_EMPTY) break;
  7 50          
  2 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 100          
  5 0          
  0 0          
  0            
2741 13 100         if (st != tag) continue; /* tombstone or tag mismatch */
  6 50          
  2 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  4 0          
  0 0          
  0            
2742              
2743             #ifdef SHM_KEY_IS_INT
2744 5 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  1 0          
  0 0          
  0 50          
  4 0          
  0 0          
  0            
2745             #else
2746 7 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  5 50          
  2 0          
  0 0          
  0            
2747             #endif
2748             /* check TTL */
2749 12 50         if (SHM_IS_EXPIRED(h, idx, now)) {
  5 0          
  2 0          
  0 50          
  0 0          
  1 0          
  0 0          
  0 0          
  4 0          
  0 0          
  0 0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
2750 1           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2751 1           SHM_FN(maybe_shrink)(h);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2752 1           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2753 1           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2754 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2755             }
2756              
2757             /* copy value out before tombstoning */
2758             #ifdef SHM_VAL_IS_STR
2759             {
2760 6 50         uint32_t vl = SHM_STR_LEN(nodes[idx].val_len);
  5 50          
  1 0          
  0 0          
  0            
2761 6 50         if (!shm_ensure_copy_buf(h, vl)) {
  5 50          
  1 0          
  0 0          
  0            
2762 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2763 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2764 0           return 0;
  0            
  0            
  0            
  0            
2765             }
2766 6           shm_str_copy(h->copy_buf, nodes[idx].val_off, nodes[idx].val_len, h->arena, vl);
  5            
  1            
  0            
  0            
2767 6           *out_str = h->copy_buf;
  5            
  1            
  0            
  0            
2768 6           *out_len = vl;
  5            
  1            
  0            
  0            
2769 6           *out_utf8 = SHM_UNPACK_UTF8(nodes[idx].val_len);
  5            
  1            
  0            
  0            
2770             }
2771             #else
2772 5           *out_value = nodes[idx].value;
  2            
  0            
  0            
  3            
  0            
  0            
2773             #endif
2774 11 50         if (h->lru_prev) shm_lru_unlink(h, idx);
  5 50          
  2 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
2775 11 50         if (h->expires_at) h->expires_at[idx] = 0;
  5 50          
  2 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
2776 11           SHM_FN(tombstone_at)(h, idx);
  5            
  2            
  0            
  0            
  1            
  0            
  0            
  3            
  0            
  0            
2777              
2778 11           SHM_FN(maybe_shrink)(h);
  5            
  2            
  0            
  0            
  1            
  0            
  0            
  3            
  0            
  0            
2779 11           shm_seqlock_write_end(&hdr->seq);
  5            
  2            
  0            
  0            
  1            
  0            
  0            
  3            
  0            
  0            
2780 11           shm_rwlock_wrunlock(hdr);
  5            
  2            
  0            
  0            
  1            
  0            
  0            
  3            
  0            
  0            
2781 11           return 1;
  5            
  2            
  0            
  0            
  1            
  0            
  0            
  3            
  0            
  0            
2782             }
2783             }
2784              
2785 2           shm_seqlock_write_end(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2786 2           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2787 2           return 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2788             }
2789              
2790             /* ---- Compare-and-take (atomic remove if value matches expected) ----
2791             * Returns 1 if matched and removed (old value copied out); 0 if key missing,
2792             * expired, or value did not match. Integer variants compare by integer
2793             * equality; string-value variants compare bytes only. */
2794 7           static int SHM_FN(cas_take)(ShmHandle *h,
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2795             #ifdef SHM_KEY_IS_INT
2796             SHM_KEY_INT_TYPE key,
2797             #else
2798             const char *key_str, uint32_t key_len, bool key_utf8,
2799             #endif
2800             #ifdef SHM_VAL_IS_STR
2801             const char *expected_str, uint32_t expected_len,
2802             const char **out_str, uint32_t *out_len, bool *out_utf8
2803             #else
2804             SHM_VAL_INT_TYPE expected, SHM_VAL_INT_TYPE *out_value
2805             #endif
2806             ) {
2807             #ifdef SHM_KEY_IS_INT
2808 4 0         SHM_SHARD_DISPATCH(h, key);
  0 0          
  0 0          
  0 50          
  4 0          
  0 0          
  0            
2809             #else
2810 3 50         SHM_SHARD_DISPATCH(h, key_str, key_len);
  3 0          
  0 0          
  0 0          
  0            
2811             #endif
2812 7           ShmHeader *hdr = h->hdr;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2813 7           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2814 7           uint8_t *states = h->states;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2815 7 50         uint32_t now = h->expires_at ? shm_now() : 0;
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  4 0          
  0 0          
  0            
2816              
2817 7           shm_rwlock_wrlock(hdr);
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2818 7           shm_seqlock_write_begin(&hdr->seq);
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2819              
2820 7           uint32_t mask = hdr->table_cap - 1;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2821             #ifdef SHM_KEY_IS_INT
2822 4           uint32_t hash = SHM_HASH_KEY(key);
  0            
  0            
  0            
  4            
  0            
  0            
2823             #else
2824 3           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  3            
  0            
  0            
  0            
2825             #endif
2826 7           uint32_t pos = hash & mask;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2827 7           uint8_t tag = SHM_MAKE_TAG(hash);
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2828              
2829 7 50         for (uint32_t i = 0; i <= mask; i++) {
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  4 0          
  0 0          
  0            
2830 7           uint32_t idx = (pos + i) & mask;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2831 7           uint8_t st = states[idx];
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2832 7           __builtin_prefetch(&nodes[idx], 0, 1);
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2833 7           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2834 7 50         if (st == SHM_EMPTY) break;
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  4 0          
  0 0          
  0            
2835 6 50         if (st != tag) continue;
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
2836             #ifdef SHM_KEY_IS_INT
2837 3 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
2838             #else
2839 3 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  3 0          
  0 0          
  0 0          
  0            
2840             #endif
2841 6 50         if (SHM_IS_EXPIRED(h, idx, now)) {
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  3 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
2842 0           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2843 0           SHM_FN(maybe_shrink)(h);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2844 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2845 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2846 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2847             }
2848             #ifdef SHM_VAL_IS_STR
2849             char ibuf[SHM_INLINE_MAX];
2850             uint32_t cur_len;
2851 3           const char *cur_str = shm_str_ptr(nodes[idx].val_off, nodes[idx].val_len,
  3            
  0            
  0            
  0            
2852 3           h->arena, ibuf, &cur_len);
  3            
  0            
  0            
  0            
2853 3 100         if (cur_len != expected_len || memcmp(cur_str, expected_str, cur_len) != 0) {
  3 50          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
2854 1           shm_seqlock_write_end(&hdr->seq);
  1            
  0            
  0            
  0            
2855 1           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
2856 1           return 0;
  1            
  0            
  0            
  0            
2857             }
2858 2 50         if (!shm_ensure_copy_buf(h, cur_len)) {
  2 0          
  0 0          
  0 0          
  0            
2859 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2860 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2861 0           return 0;
  0            
  0            
  0            
  0            
2862             }
2863 2           memcpy(h->copy_buf, cur_str, cur_len);
  2            
  0            
  0            
  0            
2864 2           *out_str = h->copy_buf;
  2            
  0            
  0            
  0            
2865 2           *out_len = cur_len;
  2            
  0            
  0            
  0            
2866 2           *out_utf8 = SHM_UNPACK_UTF8(nodes[idx].val_len);
  2            
  0            
  0            
  0            
2867             #else
2868 3 0         if (nodes[idx].value != expected) {
  0 0          
  0 0          
  0 100          
  3 0          
  0 0          
  0            
2869 1           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  1            
  0            
  0            
2870 1           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  1            
  0            
  0            
2871 1           return 0;
  0            
  0            
  0            
  1            
  0            
  0            
2872             }
2873 2           *out_value = nodes[idx].value;
  0            
  0            
  0            
  2            
  0            
  0            
2874             #endif
2875 4 50         if (h->lru_prev) shm_lru_unlink(h, idx);
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
2876 4 50         if (h->expires_at) h->expires_at[idx] = 0;
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
2877 4           SHM_FN(tombstone_at)(h, idx);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2878 4           SHM_FN(maybe_shrink)(h);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2879 4           shm_seqlock_write_end(&hdr->seq);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2880 4           shm_rwlock_wrunlock(hdr);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2881 4           return 1;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2882             }
2883             }
2884              
2885 1           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2886 1           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2887 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2888             }
2889              
2890             /* ---- Pop (remove and return one entry: LRU tail if LRU, else first found) ---- */
2891              
2892 15           static int SHM_FN(pop)(ShmHandle *h,
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  13            
  0            
  0            
2893             #ifdef SHM_KEY_IS_INT
2894             SHM_KEY_INT_TYPE *out_key,
2895             #else
2896             const char **out_key_str, uint32_t *out_key_len, bool *out_key_utf8,
2897             #endif
2898             #ifdef SHM_VAL_IS_STR
2899             const char **out_val_str, uint32_t *out_val_len, bool *out_val_utf8
2900             #else
2901             SHM_VAL_INT_TYPE *out_value
2902             #endif
2903             ) {
2904 15 50         if (h->shard_handles) {
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  13 0          
  0 0          
  0            
2905 1 0         for (uint32_t i = 0; i < h->num_shards; i++) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
2906 1           uint32_t si = (h->shard_iter + i) % h->num_shards;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2907 1           int rc = SHM_FN(pop)(h->shard_handles[si],
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2908             #ifdef SHM_KEY_IS_INT
2909             out_key,
2910             #else
2911             out_key_str, out_key_len, out_key_utf8,
2912             #endif
2913             #ifdef SHM_VAL_IS_STR
2914             out_val_str, out_val_len, out_val_utf8
2915             #else
2916             out_value
2917             #endif
2918             );
2919 1 0         if (rc) { h->shard_iter = (si + 1) % h->num_shards; return 1; }
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
2920             }
2921 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2922             }
2923 14           ShmHeader *hdr = h->hdr;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
2924 14           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
2925 14           uint8_t *states = h->states;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
2926 14 50         uint32_t now = h->expires_at ? shm_now() : 0;
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  12 0          
  0 0          
  0            
2927              
2928 14           shm_rwlock_wrlock(hdr);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
2929 14           shm_seqlock_write_begin(&hdr->seq);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
2930              
2931             /* Find victim: LRU tail if available, else linear scan */
2932 14           uint32_t idx = UINT32_MAX;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
2933 19 50         if (h->lru_prev && hdr->lru_tail != SHM_LRU_NONE) {
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  17 0          
  0 0          
  0 0          
    0          
    0          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
2934             /* Walk from tail, skipping expired */
2935 5           uint32_t pos = hdr->lru_tail;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
2936 5 0         while (pos != SHM_LRU_NONE) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  5 0          
  0 0          
  0            
2937 5 0         if (!SHM_IS_EXPIRED(h, pos, now)) { idx = pos; break; }
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  5 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
2938 0           uint32_t prev = h->lru_prev[pos];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2939 0           SHM_FN(expire_at)(h, pos);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2940 0           pos = prev;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2941             }
2942             } else {
2943             /* No LRU: scan from slot 0, expire stale entries along the way */
2944 83 50         for (uint32_t i = 0; i < hdr->table_cap; i++) {
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  81 0          
  0 0          
  0            
2945 81 50         if (!SHM_IS_LIVE(states[i])) continue;
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  79 0          
  0 0          
  0            
2946 12 50         if (SHM_IS_EXPIRED(h, i, now)) {
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  10 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
2947 5           SHM_FN(expire_at)(h, i);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
2948 5           continue;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
2949             }
2950 7           idx = i; break;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
2951             }
2952             }
2953              
2954 14 50         if (idx == UINT32_MAX) {
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  12 0          
  0 0          
  0            
2955 2           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2956 2           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2957 2           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2958             }
2959              
2960             /* Copy key+value out */
2961             #ifdef SHM_KEY_IS_INT
2962 10           *out_key = nodes[idx].key;
  0            
  0            
  0            
  10            
  0            
  0            
2963             #else
2964             {
2965 2 50         uint32_t kl = SHM_STR_LEN(nodes[idx].key_len);
  2 0          
  0 0          
  0 0          
  0            
2966 2 50         if (!shm_ensure_copy_buf(h, kl + 1)) {
  2 0          
  0 0          
  0 0          
  0            
2967 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2968 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2969 0           return 0;
  0            
  0            
  0            
  0            
2970             }
2971 2           shm_str_copy(h->copy_buf, nodes[idx].key_off, nodes[idx].key_len, h->arena, kl);
  2            
  0            
  0            
  0            
2972 2           *out_key_str = h->copy_buf;
  2            
  0            
  0            
  0            
2973 2           *out_key_len = kl;
  2            
  0            
  0            
  0            
2974 2           *out_key_utf8 = SHM_UNPACK_UTF8(nodes[idx].key_len);
  2            
  0            
  0            
  0            
2975             }
2976             #endif
2977             #ifdef SHM_VAL_IS_STR
2978             {
2979 2 50         uint32_t vl = SHM_STR_LEN(nodes[idx].val_len);
  2 0          
  0 0          
  0 0          
  0            
2980             #ifndef SHM_KEY_IS_INT
2981 2 50         uint32_t kl = SHM_STR_LEN(nodes[idx].key_len);
  2            
2982             /* key is in copy_buf[0..kl), put value after it.
2983             * Reassign out_key_str in case realloc moved the buffer. */
2984 2 50         if (!shm_ensure_copy_buf(h, kl + vl + 1)) {
  2            
2985 0           shm_seqlock_write_end(&hdr->seq);
  0            
2986 0           shm_rwlock_wrunlock(hdr);
  0            
2987 0           return 0;
  0            
2988             }
2989 2           *out_key_str = h->copy_buf;
  2            
2990 2           shm_str_copy(h->copy_buf + kl, nodes[idx].val_off, nodes[idx].val_len, h->arena, vl);
  2            
2991 2           *out_val_str = h->copy_buf + kl;
  2            
2992             #else
2993 0 0         if (!shm_ensure_copy_buf(h, vl + 1)) {
  0 0          
  0 0          
  0            
2994 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
2995 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
2996 0           return 0;
  0            
  0            
  0            
2997             }
2998 0           shm_str_copy(h->copy_buf, nodes[idx].val_off, nodes[idx].val_len, h->arena, vl);
  0            
  0            
  0            
2999 0           *out_val_str = h->copy_buf;
  0            
  0            
  0            
3000             #endif
3001 2           *out_val_len = vl;
  2            
  0            
  0            
  0            
3002 2           *out_val_utf8 = SHM_UNPACK_UTF8(nodes[idx].val_len);
  2            
  0            
  0            
  0            
3003             }
3004             #else
3005 10           *out_value = nodes[idx].value;
  0            
  0            
  0            
  10            
  0            
  0            
3006             #endif
3007              
3008 12 50         if (h->lru_prev) shm_lru_unlink(h, idx);
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  10 0          
  0 0          
  0            
3009 12 50         if (h->expires_at) h->expires_at[idx] = 0;
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  10 0          
  0 0          
  0            
3010 12           SHM_FN(tombstone_at)(h, idx);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
3011 12           SHM_FN(maybe_shrink)(h);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
3012              
3013 12           shm_seqlock_write_end(&hdr->seq);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
3014 12           shm_rwlock_wrunlock(hdr);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
3015 12           return 1;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
3016             }
3017              
3018             /* ---- Shift (remove and return one entry: LRU head if LRU, else last found) ---- */
3019              
3020 10           static int SHM_FN(shift)(ShmHandle *h,
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  9            
  0            
  0            
3021             #ifdef SHM_KEY_IS_INT
3022             SHM_KEY_INT_TYPE *out_key,
3023             #else
3024             const char **out_key_str, uint32_t *out_key_len, bool *out_key_utf8,
3025             #endif
3026             #ifdef SHM_VAL_IS_STR
3027             const char **out_val_str, uint32_t *out_val_len, bool *out_val_utf8
3028             #else
3029             SHM_VAL_INT_TYPE *out_value
3030             #endif
3031             ) {
3032 10 50         if (h->shard_handles) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  9 0          
  0 0          
  0            
3033 1 0         for (uint32_t i = 0; i < h->num_shards; i++) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
3034 1           uint32_t si = (h->shard_iter + i) % h->num_shards;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3035 1           int rc = SHM_FN(shift)(h->shard_handles[si],
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3036             #ifdef SHM_KEY_IS_INT
3037             out_key,
3038             #else
3039             out_key_str, out_key_len, out_key_utf8,
3040             #endif
3041             #ifdef SHM_VAL_IS_STR
3042             out_val_str, out_val_len, out_val_utf8
3043             #else
3044             out_value
3045             #endif
3046             );
3047 1 0         if (rc) { h->shard_iter = (si + 1) % h->num_shards; return 1; }
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
3048             }
3049 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3050             }
3051 9           ShmHeader *hdr = h->hdr;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
3052 9           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
3053 9           uint8_t *states = h->states;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
3054 9 50         uint32_t now = h->expires_at ? shm_now() : 0;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  8 0          
  0 0          
  0            
3055              
3056 9           shm_rwlock_wrlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
3057 9           shm_seqlock_write_begin(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
3058              
3059             /* Find victim: LRU head if available, else scan backward */
3060 9           uint32_t idx = UINT32_MAX;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
3061 12 50         if (h->lru_prev && hdr->lru_head != SHM_LRU_NONE) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  11 0          
  0 0          
  0 0          
    0          
    0          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
3062             /* Walk from head, skipping expired */
3063 3           uint32_t pos = hdr->lru_head;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
3064 3 0         while (pos != SHM_LRU_NONE) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
3065 3 0         if (!SHM_IS_EXPIRED(h, pos, now)) { idx = pos; break; }
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  3 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
3066 0           uint32_t nxt = h->lru_next[pos];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3067 0           SHM_FN(expire_at)(h, pos);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3068 0           pos = nxt;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3069             }
3070             } else {
3071             /* No LRU: scan backward from last slot, expire stale entries */
3072 41 50         for (uint32_t i = hdr->table_cap; i > 0; i--) {
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  38 0          
  0 0          
  0            
3073 40 100         if (!SHM_IS_LIVE(states[i - 1])) continue;
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  37 0          
  0 0          
  0            
3074 5 50         if (SHM_IS_EXPIRED(h, i - 1, now)) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  4 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
3075 0           SHM_FN(expire_at)(h, i - 1);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3076 0           continue;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3077             }
3078 5           idx = i - 1; break;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
3079             }
3080             }
3081              
3082 9 50         if (idx == UINT32_MAX) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  8 0          
  0 0          
  0            
3083 1           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3084 1           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3085 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3086             }
3087              
3088             /* Copy key+value out */
3089             #ifdef SHM_KEY_IS_INT
3090 7           *out_key = nodes[idx].key;
  0            
  0            
  0            
  7            
  0            
  0            
3091             #else
3092             {
3093 1 50         uint32_t kl = SHM_STR_LEN(nodes[idx].key_len);
  1 0          
  0 0          
  0 0          
  0            
3094 1 50         if (!shm_ensure_copy_buf(h, kl + 1)) {
  1 0          
  0 0          
  0 0          
  0            
3095 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
3096 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
3097 0           return 0;
  0            
  0            
  0            
  0            
3098             }
3099 1           shm_str_copy(h->copy_buf, nodes[idx].key_off, nodes[idx].key_len, h->arena, kl);
  1            
  0            
  0            
  0            
3100 1           *out_key_str = h->copy_buf;
  1            
  0            
  0            
  0            
3101 1           *out_key_len = kl;
  1            
  0            
  0            
  0            
3102 1           *out_key_utf8 = SHM_UNPACK_UTF8(nodes[idx].key_len);
  1            
  0            
  0            
  0            
3103             }
3104             #endif
3105             #ifdef SHM_VAL_IS_STR
3106             {
3107 1 50         uint32_t vl = SHM_STR_LEN(nodes[idx].val_len);
  1 0          
  0 0          
  0 0          
  0            
3108             #ifndef SHM_KEY_IS_INT
3109 1 50         uint32_t kl = SHM_STR_LEN(nodes[idx].key_len);
  1            
3110             /* Reassign out_key_str in case realloc moved the buffer. */
3111 1 50         if (!shm_ensure_copy_buf(h, kl + vl + 1)) {
  1            
3112 0           shm_seqlock_write_end(&hdr->seq);
  0            
3113 0           shm_rwlock_wrunlock(hdr);
  0            
3114 0           return 0;
  0            
3115             }
3116 1           *out_key_str = h->copy_buf;
  1            
3117 1           shm_str_copy(h->copy_buf + kl, nodes[idx].val_off, nodes[idx].val_len, h->arena, vl);
  1            
3118 1           *out_val_str = h->copy_buf + kl;
  1            
3119             #else
3120 0 0         if (!shm_ensure_copy_buf(h, vl + 1)) {
  0 0          
  0 0          
  0            
3121 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
3122 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
3123 0           return 0;
  0            
  0            
  0            
3124             }
3125 0           shm_str_copy(h->copy_buf, nodes[idx].val_off, nodes[idx].val_len, h->arena, vl);
  0            
  0            
  0            
3126 0           *out_val_str = h->copy_buf;
  0            
  0            
  0            
3127             #endif
3128 1           *out_val_len = vl;
  1            
  0            
  0            
  0            
3129 1           *out_val_utf8 = SHM_UNPACK_UTF8(nodes[idx].val_len);
  1            
  0            
  0            
  0            
3130             }
3131             #else
3132 7           *out_value = nodes[idx].value;
  0            
  0            
  0            
  7            
  0            
  0            
3133             #endif
3134              
3135 8 50         if (h->lru_prev) shm_lru_unlink(h, idx);
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  7 0          
  0 0          
  0            
3136 8 50         if (h->expires_at) h->expires_at[idx] = 0;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  7 0          
  0 0          
  0            
3137 8           SHM_FN(tombstone_at)(h, idx);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
3138 8           SHM_FN(maybe_shrink)(h);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
3139              
3140 8           shm_seqlock_write_end(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
3141 8           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
3142 8           return 1;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
3143             }
3144              
3145             /* ---- Drain (pop up to N entries, returns count) ---- */
3146              
3147             typedef struct {
3148             #ifdef SHM_KEY_IS_INT
3149             SHM_KEY_INT_TYPE key;
3150             #else
3151             uint32_t key_off; /* offset into drain_buf */
3152             uint32_t key_len;
3153             bool key_utf8;
3154             #endif
3155             #ifdef SHM_VAL_IS_STR
3156             uint32_t val_off; /* offset into drain_buf */
3157             uint32_t val_len;
3158             bool val_utf8;
3159             #else
3160             SHM_VAL_INT_TYPE value;
3161             #endif
3162             } SHM_PASTE(SHM_PREFIX, drain_entry);
3163              
3164 9           static uint32_t SHM_FN(drain_inner)(ShmHandle *h, uint32_t limit,
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3165             SHM_PASTE(SHM_PREFIX, drain_entry) *out, char **buf, uint32_t *buf_cap,
3166             uint32_t *buf_used_p) {
3167 9           ShmHeader *hdr = h->hdr;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3168 9           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3169 9           uint8_t *states = h->states;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3170 9 50         uint32_t now = h->expires_at ? shm_now() : 0;
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  6 0          
  0 0          
  0            
3171 9           uint32_t count = 0;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3172 9           uint32_t buf_used = *buf_used_p;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3173              
3174 9 50         if (limit == 0) return 0;
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  6 0          
  0 0          
  0            
3175              
3176 9           shm_rwlock_wrlock(hdr);
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3177 9           shm_seqlock_write_begin(&hdr->seq);
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3178              
3179 9           uint32_t scan_pos = 0; /* non-LRU scan cursor to avoid O(N^2) rescan */
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3180 33 100         while (count < limit) {
  12 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  21 0          
  0 0          
  0            
3181 28           uint32_t idx = UINT32_MAX;
  10            
  0            
  0            
  0            
  0            
  0            
  0            
  18            
  0            
  0            
3182              
3183 32 50         if (h->lru_prev && hdr->lru_tail != SHM_LRU_NONE) {
  10 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  22 0          
  0 0          
  0 0          
    0          
    0          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
3184 4           uint32_t pos = hdr->lru_tail;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
3185 4 0         while (pos != SHM_LRU_NONE) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  4 0          
  0 0          
  0            
3186 4 0         if (!SHM_IS_EXPIRED(h, pos, now)) { idx = pos; break; }
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  4 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
3187 0           uint32_t prev = h->lru_prev[pos];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3188 0           SHM_FN(expire_at)(h, pos);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3189 0           pos = prev;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3190             }
3191             } else {
3192 101 100         for (; scan_pos < hdr->table_cap; scan_pos++) {
  26 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  75 0          
  0 0          
  0            
3193 97 100         if (!SHM_IS_LIVE(states[scan_pos])) continue;
  25 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  72 0          
  0 0          
  0            
3194 23 50         if (SHM_IS_EXPIRED(h, scan_pos, now)) {
  9 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  14 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
3195 3           SHM_FN(expire_at)(h, scan_pos);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
3196 3           continue;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
3197             }
3198 20           idx = scan_pos++;
  9            
  0            
  0            
  0            
  0            
  0            
  0            
  11            
  0            
  0            
3199 20           break;
  9            
  0            
  0            
  0            
  0            
  0            
  0            
  11            
  0            
  0            
3200             }
3201             }
3202              
3203 28 100         if (idx == UINT32_MAX) break;
  10 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  18 0          
  0 0          
  0            
3204              
3205             /* Copy key */
3206             #ifdef SHM_KEY_IS_INT
3207 15           out[count].key = nodes[idx].key;
  0            
  0            
  0            
  15            
  0            
  0            
3208             #else
3209             {
3210 9 50         uint32_t kl = SHM_STR_LEN(nodes[idx].key_len);
  9 0          
  0 0          
  0 0          
  0            
3211 9           uint32_t need = buf_used + kl;
  9            
  0            
  0            
  0            
3212 9 100         if (need > *buf_cap) {
  9 0          
  0 0          
  0 0          
  0            
3213 3 50         uint32_t ns = *buf_cap ? *buf_cap * 2 : 4096;
  3 0          
  0 0          
  0 0          
  0            
3214 3 50         while (ns < need) ns *= 2;
  3 0          
  0 0          
  0 0          
  0            
3215 3           char *nb = (char *)realloc(*buf, ns);
  3            
  0            
  0            
  0            
3216 3 50         if (!nb) break;
  3 0          
  0 0          
  0 0          
  0            
3217 3           *buf = nb; *buf_cap = ns;
  3            
  0            
  0            
  0            
3218             }
3219 9           shm_str_copy(*buf + buf_used, nodes[idx].key_off, nodes[idx].key_len, h->arena, kl);
  9            
  0            
  0            
  0            
3220 9           out[count].key_off = buf_used;
  9            
  0            
  0            
  0            
3221 9           out[count].key_len = kl;
  9            
  0            
  0            
  0            
3222 9           out[count].key_utf8 = SHM_UNPACK_UTF8(nodes[idx].key_len);
  9            
  0            
  0            
  0            
3223 9           buf_used += kl;
  9            
  0            
  0            
  0            
3224             }
3225             #endif
3226             /* Copy value */
3227             #ifdef SHM_VAL_IS_STR
3228             {
3229 9 50         uint32_t vl = SHM_STR_LEN(nodes[idx].val_len);
  9 0          
  0 0          
  0 0          
  0            
3230 9           uint32_t need = buf_used + vl;
  9            
  0            
  0            
  0            
3231 9 50         if (need > *buf_cap) {
  9 0          
  0 0          
  0 0          
  0            
3232 0 0         uint32_t ns = *buf_cap ? *buf_cap * 2 : 4096;
  0 0          
  0 0          
  0 0          
  0            
3233 0 0         while (ns < need) ns *= 2;
  0 0          
  0 0          
  0 0          
  0            
3234 0           char *nb = (char *)realloc(*buf, ns);
  0            
  0            
  0            
  0            
3235 0 0         if (!nb) break;
  0 0          
  0 0          
  0 0          
  0            
3236 0           *buf = nb; *buf_cap = ns;
  0            
  0            
  0            
  0            
3237             }
3238 9           shm_str_copy(*buf + buf_used, nodes[idx].val_off, nodes[idx].val_len, h->arena, vl);
  9            
  0            
  0            
  0            
3239 9           out[count].val_off = buf_used;
  9            
  0            
  0            
  0            
3240 9           out[count].val_len = vl;
  9            
  0            
  0            
  0            
3241 9           out[count].val_utf8 = SHM_UNPACK_UTF8(nodes[idx].val_len);
  9            
  0            
  0            
  0            
3242 9           buf_used += vl;
  9            
  0            
  0            
  0            
3243             }
3244             #else
3245 15           out[count].value = nodes[idx].value;
  0            
  0            
  0            
  15            
  0            
  0            
3246             #endif
3247              
3248 24 50         if (h->lru_prev) shm_lru_unlink(h, idx);
  9 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  15 0          
  0 0          
  0            
3249 24 50         if (h->expires_at) h->expires_at[idx] = 0;
  9 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  15 0          
  0 0          
  0            
3250 24           SHM_FN(tombstone_at)(h, idx);
  9            
  0            
  0            
  0            
  0            
  0            
  0            
  15            
  0            
  0            
3251 24           count++;
  9            
  0            
  0            
  0            
  0            
  0            
  0            
  15            
  0            
  0            
3252             }
3253              
3254 9 50         if (count > 0) SHM_FN(maybe_shrink)(h);
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  6 0          
  0 0          
  0            
3255              
3256 9           shm_seqlock_write_end(&hdr->seq);
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3257 9           shm_rwlock_wrunlock(hdr);
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3258 9           *buf_used_p = buf_used;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3259 9           return count;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3260             }
3261              
3262 9           static uint32_t SHM_FN(drain)(ShmHandle *h, uint32_t limit,
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3263             SHM_PASTE(SHM_PREFIX, drain_entry) *out, char **buf, uint32_t *buf_cap) {
3264 9           uint32_t buf_used = 0;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3265 9 100         if (h->shard_handles) {
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  6 0          
  0 0          
  0            
3266 2           uint32_t total = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3267 4 50         for (uint32_t i = 0; i < h->num_shards && total < limit; i++) {
  2 100          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  2 0          
  0 0          
  0 0          
    0          
    0          
    0          
    50          
    100          
    0          
    0          
    0          
    0          
3268 2           uint32_t si = (h->shard_iter + i) % h->num_shards;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3269 2           uint32_t got = SHM_FN(drain_inner)(h->shard_handles[si], limit - total,
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3270 2           out + total, buf, buf_cap, &buf_used);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3271 2           total += got;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3272             }
3273 2 50         if (total > 0) h->shard_iter = (h->shard_iter + 1) % h->num_shards;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
3274 2           return total;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3275             }
3276 7           return SHM_FN(drain_inner)(h, limit, out, buf, buf_cap, &buf_used);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
3277             }
3278              
3279             /* ---- Counter operations and integer-value cas ---- */
3280              
3281             #ifdef SHM_HAS_COUNTERS
3282              
3283 23           static inline int SHM_FN(find_slot)(ShmHandle *h,
  4            
  2            
  3            
  8            
  2            
  4            
3284             #ifdef SHM_KEY_IS_INT
3285             SHM_KEY_INT_TYPE key,
3286             #else
3287             const char *key_str, uint32_t key_len, bool key_utf8,
3288             #endif
3289             uint32_t *out_idx) {
3290 23           ShmHeader *hdr = h->hdr;
  4            
  2            
  3            
  8            
  2            
  4            
3291 23           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  4            
  2            
  3            
  8            
  2            
  4            
3292 23           uint8_t *states = h->states;
  4            
  2            
  3            
  8            
  2            
  4            
3293 23           uint32_t mask = hdr->table_cap - 1;
  4            
  2            
  3            
  8            
  2            
  4            
3294             #ifdef SHM_KEY_IS_INT
3295 14           uint32_t hash = SHM_HASH_KEY(key);
  8            
  2            
  4            
3296             #else
3297 9           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  4            
  2            
  3            
3298             #endif
3299 23           uint32_t pos = hash & mask;
  4            
  2            
  3            
  8            
  2            
  4            
3300 23           uint8_t tag = SHM_MAKE_TAG(hash);
  4            
  2            
  3            
  8            
  2            
  4            
3301 23           uint32_t probe_start = 0;
  4            
  2            
  3            
  8            
  2            
  4            
3302              
3303             #ifdef __SSE2__
3304 23 50         if (pos + 16 <= hdr->table_cap) {
  4 50          
  2 50          
  3 50          
  8 50          
  2 50          
  4            
3305             uint16_t mmask, emask;
3306 4           shm_probe_group(states, pos, tag, &mmask, &emask);
  4            
  0            
  0            
  0            
  0            
  0            
3307 4 50         uint16_t cutoff = emask ? (uint16_t)((1U << __builtin_ctz(emask)) - 1) : 0xFFFF;
  4 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
3308 4           uint16_t relevant = mmask & cutoff;
  4            
  0            
  0            
  0            
  0            
  0            
3309 4 100         while (relevant) {
  4 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
3310 3           int bit = __builtin_ctz(relevant);
  3            
  0            
  0            
  0            
  0            
  0            
3311 3           uint32_t idx = pos + bit;
  3            
  0            
  0            
  0            
  0            
  0            
3312             #ifdef SHM_KEY_IS_INT
3313 0 0         if (SHM_KEY_EQ(&nodes[idx], key)) { *out_idx = idx; return 1; }
  0 0          
  0 0          
  0            
3314             #else
3315 4 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) { *out_idx = idx; return 1; }
  4 0          
  0 0          
  0            
3316             #endif
3317 0           relevant &= relevant - 1;
  0            
  0            
  0            
  0            
  0            
  0            
3318             }
3319 1 50         if (emask) return 0;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
3320 0           probe_start = 16;
  0            
  0            
  0            
  0            
  0            
  0            
3321             }
3322             #endif
3323              
3324 19 0         for (uint32_t i = probe_start; i <= mask; i++) {
  0 50          
  2 50          
  3 50          
  8 50          
  2 50          
  4            
3325 19           uint32_t idx = (pos + i) & mask;
  0            
  2            
  3            
  8            
  2            
  4            
3326 19           uint8_t st = states[idx];
  0            
  2            
  3            
  8            
  2            
  4            
3327 19           __builtin_prefetch(&nodes[idx], 0, 1);
  0            
  2            
  3            
  8            
  2            
  4            
3328 19           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  0            
  2            
  3            
  8            
  2            
  4            
3329 19 0         if (st == SHM_EMPTY) return 0;
  0 100          
  2 100          
  3 100          
  8 100          
  2 100          
  4            
3330 14 0         if (st != tag) continue;
  0 50          
  1 50          
  2 50          
  7 50          
  1 50          
  3            
3331             #ifdef SHM_KEY_IS_INT
3332 11 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  7 50          
  1 50          
  3            
3333             #else
3334 3 0         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  0 50          
  1 50          
  2            
3335             #endif
3336 14           *out_idx = idx;
  0            
  1            
  2            
  7            
  1            
  3            
3337 14           return 1;
  0            
  1            
  2            
  7            
  1            
  3            
3338             }
3339             }
3340 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
3341             }
3342              
3343 32           static SHM_VAL_INT_TYPE SHM_FN(incr_by)(ShmHandle *h,
  4            
  2            
  3            
  17            
  2            
  4            
3344             #ifdef SHM_KEY_IS_INT
3345             SHM_KEY_INT_TYPE key,
3346             #else
3347             const char *key_str, uint32_t key_len, bool key_utf8,
3348             #endif
3349             SHM_VAL_INT_TYPE delta, int *ok) {
3350             #ifdef SHM_KEY_IS_INT
3351 23 100         SHM_SHARD_DISPATCH(h, key);
  17 50          
  2 50          
  4            
3352             #else
3353 9 50         SHM_SHARD_DISPATCH(h, key_str, key_len);
  4 50          
  2 50          
  3            
3354             #endif
3355 32           ShmHeader *hdr = h->hdr;
  4            
  2            
  3            
  17            
  2            
  4            
3356 32           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  4            
  2            
  3            
  17            
  2            
  4            
3357 32 50         uint32_t now = h->expires_at ? shm_now() : 0;
  4 50          
  2 50          
  3 100          
  17 50          
  2 50          
  4            
3358              
3359             /* fast path: existing key under read lock + atomic add (no LRU/TTL) */
3360 32 50         if (!h->lru_prev && !h->expires_at) {
  4 50          
  2 50          
  3 50          
  17 50          
  2 50          
  4 100          
    100          
    50          
    50          
    50          
    50          
3361 23           shm_rwlock_rdlock(hdr);
  4            
  2            
  3            
  8            
  2            
  4            
3362             uint32_t idx;
3363             #ifdef SHM_KEY_IS_INT
3364 14 100         if (SHM_FN(find_slot)(h, key, &idx)) {
  8 100          
  2 100          
  4            
3365             #else
3366 9 100         if (SHM_FN(find_slot)(h, key_str, key_len, key_utf8, &idx)) {
  4 100          
  2 100          
  3            
3367             #endif
3368 17           SHM_VAL_INT_TYPE result =
  3            
  1            
  2            
  7            
  1            
  3            
3369 17           __atomic_add_fetch(&nodes[idx].value, delta, __ATOMIC_ACQ_REL);
  3            
  1            
  2            
  7            
  1            
  3            
3370 17           shm_rwlock_rdunlock(hdr);
  3            
  1            
  2            
  7            
  1            
  3            
3371 17           *ok = 1;
  3            
  1            
  2            
  7            
  1            
  3            
3372 17           return result;
  3            
  1            
  2            
  7            
  1            
  3            
3373             }
3374 6           shm_rwlock_rdunlock(hdr);
  1            
  1            
  1            
  1            
  1            
  1            
3375             }
3376              
3377             /* slow path: find-or-insert under write lock */
3378 15           shm_rwlock_wrlock(hdr);
  1            
  1            
  1            
  10            
  1            
  1            
3379 15           shm_seqlock_write_begin(&hdr->seq);
  1            
  1            
  1            
  10            
  1            
  1            
3380              
3381 15           SHM_FN(maybe_grow)(h);
  1            
  1            
  1            
  10            
  1            
  1            
3382 15           uint32_t mask = hdr->table_cap - 1;
  1            
  1            
  1            
  10            
  1            
  1            
3383             #ifdef SHM_KEY_IS_INT
3384 12           uint32_t hash = SHM_HASH_KEY(key);
  10            
  1            
  1            
3385             #else
3386 3           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  1            
  1            
  1            
3387             #endif
3388 15           uint32_t pos = hash & mask;
  1            
  1            
  1            
  10            
  1            
  1            
3389 15           uint32_t insert_pos = UINT32_MAX;
  1            
  1            
  1            
  10            
  1            
  1            
3390              
3391 15           uint8_t tag = SHM_MAKE_TAG(hash);
  1            
  1            
  1            
  10            
  1            
  1            
3392 15 50         for (uint32_t i = 0; i <= mask; i++) {
  1 50          
  1 50          
  1 50          
  10 50          
  1 50          
  1            
3393 15           uint32_t slot = (pos + i) & mask;
  1            
  1            
  1            
  10            
  1            
  1            
3394 15           uint8_t st = h->states[slot];
  1            
  1            
  1            
  10            
  1            
  1            
3395 15           __builtin_prefetch(&nodes[slot], 0, 1);
  1            
  1            
  1            
  10            
  1            
  1            
3396 15           __builtin_prefetch(&nodes[(slot + 1) & mask], 0, 1);
  1            
  1            
  1            
  10            
  1            
  1            
3397 15 50         if (st == SHM_EMPTY) {
  1 50          
  1 50          
  1 100          
  10 50          
  1 50          
  1            
3398 9 50         if (insert_pos == UINT32_MAX) insert_pos = slot;
  1 50          
  1 50          
  1 50          
  4 50          
  1 50          
  1            
3399 9           break;
  1            
  1            
  1            
  4            
  1            
  1            
3400             }
3401 6 0         if (st == SHM_TOMBSTONE) {
  0 0          
  0 0          
  0 50          
  6 0          
  0 0          
  0            
3402 0 0         if (insert_pos == UINT32_MAX) insert_pos = slot;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
3403 0           continue;
  0            
  0            
  0            
  0            
  0            
  0            
3404             }
3405 6 0         if (st != tag) continue;
  0 0          
  0 0          
  0 50          
  6 0          
  0 0          
  0            
3406             #ifdef SHM_KEY_IS_INT
3407 6 50         if (SHM_KEY_EQ(&nodes[slot], key)) {
  6 0          
  0 0          
  0            
3408             #else
3409 0 0         if (SHM_KEY_EQ_STR(&nodes[slot], h->arena, key_str, key_len, key_utf8)) {
  0 0          
  0 0          
  0            
3410             #endif
3411             /* TTL check */
3412 6 0         if (SHM_IS_EXPIRED(h, slot, now)) {
  0 0          
  0 0          
  0 0          
  6 0          
  0 0          
  0 0          
    0          
    0          
    100          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
3413 2           SHM_FN(expire_at)(h, slot);
  0            
  0            
  0            
  2            
  0            
  0            
3414             /* treat as not found — will insert below */
3415 2 0         if (insert_pos == UINT32_MAX) insert_pos = slot;
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
3416 2           break;
  0            
  0            
  0            
  2            
  0            
  0            
3417             }
3418              
3419 4           nodes[slot].value += delta;
  0            
  0            
  0            
  4            
  0            
  0            
3420 4           SHM_VAL_INT_TYPE result = nodes[slot].value;
  0            
  0            
  0            
  4            
  0            
  0            
3421 4 0         if (h->lru_prev) shm_lru_promote(h, slot);
  0 0          
  0 0          
  0 100          
  4 0          
  0 0          
  0            
3422 4 0         if (h->expires_at && hdr->default_ttl > 0 && h->expires_at[slot] != 0)
  0 0          
  0 0          
  0 0          
  4 0          
  0 0          
  0 0          
    0          
    0          
    100          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
3423 0           h->expires_at[slot] = shm_expiry_ts(hdr->default_ttl);
  0            
  0            
  0            
  0            
  0            
  0            
3424 4           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  4            
  0            
  0            
3425 4           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  4            
  0            
  0            
3426 4           *ok = 1;
  0            
  0            
  0            
  4            
  0            
  0            
3427 4           return result;
  0            
  0            
  0            
  4            
  0            
  0            
3428             }
3429             }
3430              
3431 11 50         if (insert_pos == UINT32_MAX) {
  1 50          
  1 50          
  1 50          
  6 50          
  1 50          
  1            
3432 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
3433 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
3434 0           *ok = 0;
  0            
  0            
  0            
  0            
  0            
  0            
3435 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
3436             }
3437              
3438             /* LRU eviction only when actually inserting */
3439 11 50         if (hdr->max_size > 0 && hdr->size >= hdr->max_size)
  1 0          
  1 50          
  1 0          
  6 50          
  1 0          
  1 100          
    50          
    50          
    0          
    50          
    0          
3440 0           SHM_FN(lru_evict_one)(h);
  0            
  0            
  0            
  0            
  0            
  0            
3441              
3442 11           int was_tombstone = (h->states[insert_pos] == SHM_TOMBSTONE);
  1            
  1            
  1            
  6            
  1            
  1            
3443             #ifdef SHM_KEY_IS_INT
3444 8           nodes[insert_pos].key = key;
  6            
  1            
  1            
3445             #else
3446 3 50         if (!shm_str_store(hdr, h->arena, &nodes[insert_pos].key_off, &nodes[insert_pos].key_len, key_str, key_len, key_utf8)) {
  1 50          
  1 50          
  1            
3447 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
3448 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
3449 0           *ok = 0;
  0            
  0            
  0            
3450 0           return 0;
  0            
  0            
  0            
3451             }
3452             #endif
3453 11           nodes[insert_pos].value = delta;
  1            
  1            
  1            
  6            
  1            
  1            
3454 11           h->states[insert_pos] = SHM_MAKE_TAG(hash);
  1            
  1            
  1            
  6            
  1            
  1            
3455 11           hdr->size++;
  1            
  1            
  1            
  6            
  1            
  1            
3456 11 50         if (was_tombstone) hdr->tombstones--;
  1 50          
  1 50          
  1 100          
  6 50          
  1 50          
  1            
3457              
3458 11 50         if (h->lru_prev) shm_lru_push_front(h, insert_pos);
  1 50          
  1 50          
  1 100          
  6 50          
  1 50          
  1            
3459 11 50         if (h->expires_at && hdr->default_ttl > 0)
  1 0          
  1 50          
  1 0          
  6 50          
  1 0          
  1 100          
    50          
    50          
    0          
    50          
    0          
3460 4           h->expires_at[insert_pos] = shm_expiry_ts(hdr->default_ttl);
  0            
  0            
  0            
  4            
  0            
  0            
3461              
3462 11           shm_seqlock_write_end(&hdr->seq);
  1            
  1            
  1            
  6            
  1            
  1            
3463 11           shm_rwlock_wrunlock(hdr);
  1            
  1            
  1            
  6            
  1            
  1            
3464 11           *ok = 1;
  1            
  1            
  1            
  6            
  1            
  1            
3465 11           return delta;
  1            
  1            
  1            
  6            
  1            
  1            
3466             }
3467              
3468             /* ---- Compare-and-swap (atomic, integer-value variants) ---- */
3469              
3470 17           static int SHM_FN(cas)(ShmHandle *h,
  3            
  2            
  2            
  6            
  2            
  2            
3471             #ifdef SHM_KEY_IS_INT
3472             SHM_KEY_INT_TYPE key,
3473             #else
3474             const char *key_str, uint32_t key_len, bool key_utf8,
3475             #endif
3476             SHM_VAL_INT_TYPE expected, SHM_VAL_INT_TYPE desired
3477             ) {
3478             #ifdef SHM_KEY_IS_INT
3479 10 100         SHM_SHARD_DISPATCH(h, key);
  6 50          
  2 50          
  2            
3480             #else
3481 7 50         SHM_SHARD_DISPATCH(h, key_str, key_len);
  3 50          
  2 50          
  2            
3482             #endif
3483 17           ShmHeader *hdr = h->hdr;
  3            
  2            
  2            
  6            
  2            
  2            
3484 17           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  3            
  2            
  2            
  6            
  2            
  2            
3485 17           uint8_t *states = h->states;
  3            
  2            
  2            
  6            
  2            
  2            
3486 17 50         uint32_t now = h->expires_at ? shm_now() : 0;
  3 50          
  2 50          
  2 100          
  6 50          
  2 50          
  2            
3487              
3488 17           shm_rwlock_wrlock(hdr);
  3            
  2            
  2            
  6            
  2            
  2            
3489 17           shm_seqlock_write_begin(&hdr->seq);
  3            
  2            
  2            
  6            
  2            
  2            
3490              
3491 17           uint32_t mask = hdr->table_cap - 1;
  3            
  2            
  2            
  6            
  2            
  2            
3492             #ifdef SHM_KEY_IS_INT
3493 10           uint32_t hash = SHM_HASH_KEY(key);
  6            
  2            
  2            
3494             #else
3495 7           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  3            
  2            
  2            
3496             #endif
3497 17           uint32_t pos = hash & mask;
  3            
  2            
  2            
  6            
  2            
  2            
3498 17           uint8_t tag = SHM_MAKE_TAG(hash);
  3            
  2            
  2            
  6            
  2            
  2            
3499              
3500 17 50         for (uint32_t i = 0; i <= mask; i++) {
  3 50          
  2 50          
  2 50          
  6 50          
  2 50          
  2            
3501 17           uint32_t idx = (pos + i) & mask;
  3            
  2            
  2            
  6            
  2            
  2            
3502 17           uint8_t st = states[idx];
  3            
  2            
  2            
  6            
  2            
  2            
3503 17           __builtin_prefetch(&nodes[idx], 0, 1);
  3            
  2            
  2            
  6            
  2            
  2            
3504 17           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  3            
  2            
  2            
  6            
  2            
  2            
3505 17 50         if (st == SHM_EMPTY) break;
  3 50          
  2 50          
  2 100          
  6 50          
  2 50          
  2            
3506 16 50         if (st != tag) continue;
  3 50          
  2 50          
  2 50          
  5 50          
  2 50          
  2            
3507             #ifdef SHM_KEY_IS_INT
3508 9 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  5 50          
  2 50          
  2            
3509             #else
3510 7 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  3 50          
  2 50          
  2            
3511             #endif
3512 16 50         if (SHM_IS_EXPIRED(h, idx, now)) {
  3 0          
  2 0          
  2 50          
  5 0          
  2 0          
  2 50          
    0          
    0          
    100          
    50          
    100          
    50          
    0          
    0          
    50          
    0          
    0          
3513 1           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  1            
  0            
  0            
3514 1           SHM_FN(maybe_shrink)(h);
  0            
  0            
  0            
  1            
  0            
  0            
3515 1           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  1            
  0            
  0            
3516 1           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  1            
  0            
  0            
3517 1           return 0;
  0            
  0            
  0            
  1            
  0            
  0            
3518             }
3519 15 100         if (nodes[idx].value == expected) {
  3 100          
  2 100          
  2 100          
  4 100          
  2 100          
  2            
3520 9           nodes[idx].value = desired;
  2            
  1            
  1            
  3            
  1            
  1            
3521 9 50         if (h->lru_prev) shm_lru_promote(h, idx);
  2 50          
  1 50          
  1 50          
  3 50          
  1 50          
  1            
3522 9 50         if (h->expires_at && hdr->default_ttl > 0 && h->expires_at[idx] != 0)
  2 0          
  1 0          
  1 50          
  3 0          
  1 0          
  1 50          
    0          
    0          
    100          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
    0          
3523 1           h->expires_at[idx] = shm_expiry_ts(hdr->default_ttl);
  0            
  0            
  0            
  1            
  0            
  0            
3524 9           shm_seqlock_write_end(&hdr->seq);
  2            
  1            
  1            
  3            
  1            
  1            
3525 9           shm_rwlock_wrunlock(hdr);
  2            
  1            
  1            
  3            
  1            
  1            
3526 9           return 1;
  2            
  1            
  1            
  3            
  1            
  1            
3527             }
3528 6           shm_seqlock_write_end(&hdr->seq);
  1            
  1            
  1            
  1            
  1            
  1            
3529 6           shm_rwlock_wrunlock(hdr);
  1            
  1            
  1            
  1            
  1            
  1            
3530 6           return 0;
  1            
  1            
  1            
  1            
  1            
  1            
3531             }
3532             }
3533              
3534 1           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  1            
  0            
  0            
3535 1           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  1            
  0            
  0            
3536 1           return 0;
  0            
  0            
  0            
  1            
  0            
  0            
3537             }
3538              
3539             #endif /* SHM_HAS_COUNTERS */
3540              
3541             /* ---- Compare-and-swap (atomic, string-value variants) ---- */
3542              
3543             #ifdef SHM_VAL_IS_STR
3544 28           static int SHM_FN(cas)(ShmHandle *h,
  10            
  6            
  6            
  6            
3545             #ifdef SHM_KEY_IS_INT
3546             SHM_KEY_INT_TYPE key,
3547             #else
3548             const char *key_str, uint32_t key_len, bool key_utf8,
3549             #endif
3550             const char *expected_str, uint32_t expected_len,
3551             const char *desired_str, uint32_t desired_len, bool desired_utf8
3552             ) {
3553             #ifdef SHM_KEY_IS_INT
3554 18 50         SHM_SHARD_DISPATCH(h, key);
  6 50          
  6 50          
  6            
3555             #else
3556 10 50         SHM_SHARD_DISPATCH(h, key_str, key_len);
  10            
3557             #endif
3558 28           ShmHeader *hdr = h->hdr;
  10            
  6            
  6            
  6            
3559 28           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  10            
  6            
  6            
  6            
3560 28           uint8_t *states = h->states;
  10            
  6            
  6            
  6            
3561 28 100         uint32_t now = h->expires_at ? shm_now() : 0;
  10 50          
  6 50          
  6 50          
  6            
3562              
3563 28           shm_rwlock_wrlock(hdr);
  10            
  6            
  6            
  6            
3564 28           shm_seqlock_write_begin(&hdr->seq);
  10            
  6            
  6            
  6            
3565              
3566 28           uint32_t mask = hdr->table_cap - 1;
  10            
  6            
  6            
  6            
3567             #ifdef SHM_KEY_IS_INT
3568 18           uint32_t hash = SHM_HASH_KEY(key);
  6            
  6            
  6            
3569             #else
3570 10           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  10            
3571             #endif
3572 28           uint32_t pos = hash & mask;
  10            
  6            
  6            
  6            
3573 28           uint8_t tag = SHM_MAKE_TAG(hash);
  10            
  6            
  6            
  6            
3574              
3575 37 50         for (uint32_t i = 0; i <= mask; i++) {
  10 50          
  9 50          
  9 50          
  9            
3576 37           uint32_t idx = (pos + i) & mask;
  10            
  9            
  9            
  9            
3577 37           uint8_t st = states[idx];
  10            
  9            
  9            
  9            
3578 37           __builtin_prefetch(&nodes[idx], 0, 1);
  10            
  9            
  9            
  9            
3579 37           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  10            
  9            
  9            
  9            
3580 37 100         if (st == SHM_EMPTY) break;
  10 100          
  9 100          
  9 100          
  9            
3581 33 50         if (st != tag) continue;
  9 100          
  8 100          
  8 100          
  8            
3582             #ifdef SHM_KEY_IS_INT
3583 15 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  5 50          
  5 50          
  5            
3584             #else
3585 9 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  9            
3586             #endif
3587 24 100         if (SHM_IS_EXPIRED(h, idx, now)) {
  9 50          
  5 50          
  5 50          
  5 0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
3588 0           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
3589 0           SHM_FN(maybe_shrink)(h);
  0            
  0            
  0            
  0            
3590 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
3591 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
3592 0           return 0;
  0            
  0            
  0            
  0            
3593             }
3594             char ibuf[SHM_INLINE_MAX];
3595             uint32_t cur_len;
3596 24           const char *cur_str = shm_str_ptr(nodes[idx].val_off, nodes[idx].val_len,
  9            
  5            
  5            
  5            
3597 24           h->arena, ibuf, &cur_len);
  9            
  5            
  5            
  5            
3598 24 100         if (cur_len != expected_len || memcmp(cur_str, expected_str, cur_len) != 0) {
  9 100          
  5 100          
  5 50          
  5 100          
    50          
    100          
    50          
3599 5           shm_seqlock_write_end(&hdr->seq);
  2            
  1            
  1            
  1            
3600 5           shm_rwlock_wrunlock(hdr);
  2            
  1            
  1            
  1            
3601 5           return 0;
  2            
  1            
  1            
  1            
3602             }
3603 19           uint32_t old_off = nodes[idx].val_off;
  7            
  4            
  4            
  4            
3604 19           uint32_t old_lf = nodes[idx].val_len;
  7            
  4            
  4            
  4            
3605 19 50         if (!shm_str_store(hdr, h->arena, &nodes[idx].val_off, &nodes[idx].val_len,
  7 50          
  4 50          
  4 50          
  4            
3606             desired_str, desired_len, desired_utf8)) {
3607 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
3608 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
3609 0           return 0;
  0            
  0            
  0            
  0            
3610             }
3611 19           shm_str_free(hdr, h->arena, old_off, old_lf);
  7            
  4            
  4            
  4            
3612 19 100         if (h->lru_prev) shm_lru_promote(h, idx);
  7 50          
  4 50          
  4 50          
  4            
3613 19 100         if (h->expires_at && hdr->default_ttl > 0 && h->expires_at[idx] != 0)
  7 50          
  4 50          
  4 50          
  4 0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
3614 1           h->expires_at[idx] = shm_expiry_ts(hdr->default_ttl);
  1            
  0            
  0            
  0            
3615 19           shm_seqlock_write_end(&hdr->seq);
  7            
  4            
  4            
  4            
3616 19           shm_rwlock_wrunlock(hdr);
  7            
  4            
  4            
  4            
3617 19           return 1;
  7            
  4            
  4            
  4            
3618             }
3619             }
3620              
3621 4           shm_seqlock_write_end(&hdr->seq);
  1            
  1            
  1            
  1            
3622 4           shm_rwlock_wrunlock(hdr);
  1            
  1            
  1            
  1            
3623 4           return 0;
  1            
  1            
  1            
  1            
3624             }
3625             #endif /* SHM_VAL_IS_STR */
3626              
3627             /* ---- Size ---- */
3628              
3629 190           static inline uint32_t SHM_FN(size)(ShmHandle *h) {
  28            
  3            
  0            
  0            
  2            
  0            
  1            
  153            
  2            
  1            
3630 190 100         if (h->shard_handles) {
  28 50          
  3 0          
  0 0          
  0 50          
  2 0          
  0 50          
  1 100          
  153 50          
  2 50          
  1            
3631 15           uint64_t total = 0;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  13            
  0            
  0            
3632 99 100         for (uint32_t i = 0; i < h->num_shards; i++)
  10 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  89 0          
  0 0          
  0            
3633 84           total += SHM_FN(size)(h->shard_handles[i]);
  8            
  0            
  0            
  0            
  0            
  0            
  0            
  76            
  0            
  0            
3634 15 50         return (uint32_t)(total > UINT32_MAX ? UINT32_MAX : total);
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  13 0          
  0 0          
  0            
3635             }
3636 175           return __atomic_load_n(&h->hdr->size, __ATOMIC_ACQUIRE);
  26            
  3            
  0            
  0            
  2            
  0            
  1            
  140            
  2            
  1            
3637             }
3638              
3639             /* ---- Max entries ---- */
3640              
3641 8           static inline uint32_t SHM_FN(max_entries)(ShmHandle *h) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
3642 8 50         if (h->shard_handles) return SHM_FN(max_entries)(h->shard_handles[0]);
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  7 0          
  0 0          
  0            
3643 6           return h->hdr->max_table_cap * 3 / 4;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
3644             }
3645              
3646             /* ---- Accessors ---- */
3647              
3648 10           static inline uint32_t SHM_FN(max_size)(ShmHandle *h) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  9            
  0            
  0            
3649 10 50         if (h->shard_handles) return SHM_FN(max_size)(h->shard_handles[0]);
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  9 0          
  0 0          
  0            
3650 8           return h->hdr->max_size;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
3651             }
3652              
3653 10           static inline uint32_t SHM_FN(ttl)(ShmHandle *h) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  9            
  0            
  0            
3654 10 50         if (h->shard_handles) return SHM_FN(ttl)(h->shard_handles[0]);
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  9 0          
  0 0          
  0            
3655 8           return h->hdr->default_ttl;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
3656             }
3657              
3658             /* ---- Get value with TTL remaining (atomic snapshot) ----
3659             * Returns 1 if key found and live (output args filled); 0 if missing/expired.
3660             * *out_ttl_remaining encodes the entry's TTL state on a return of 1:
3661             * -1 = map has no TTL enabled
3662             * 0 = entry is permanent
3663             * >0 = seconds remaining until expiry
3664             * Caller may pass NULL out_ttl_remaining if only the value is wanted.
3665             */
3666 7           static int SHM_FN(get_with_ttl)(ShmHandle *h,
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  3            
  0            
  0            
3667             #ifdef SHM_KEY_IS_INT
3668             SHM_KEY_INT_TYPE key,
3669             #else
3670             const char *key_str, uint32_t key_len, bool key_utf8,
3671             #endif
3672             #ifdef SHM_VAL_IS_STR
3673             const char **out_str, uint32_t *out_len, bool *out_utf8,
3674             #else
3675             SHM_VAL_INT_TYPE *out_value,
3676             #endif
3677             int64_t *out_ttl_remaining
3678             ) {
3679             #ifdef SHM_KEY_IS_INT
3680 6 50         SHM_SHARD_DISPATCH(h, key);
  3 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
3681             #else
3682 1 0         SHM_SHARD_DISPATCH(h, key_str, key_len);
  0 50          
  1 0          
  0 0          
  0            
3683             #endif
3684 7           ShmHeader *hdr = h->hdr;
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  3            
  0            
  0            
3685 7           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  3            
  0            
  0            
3686 7           uint8_t *states = h->states;
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  3            
  0            
  0            
3687 7 0         uint32_t now_ts = h->expires_at ? shm_now() : 0;
  0 50          
  1 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0 100          
  3 0          
  0 0          
  0            
3688              
3689 7           shm_rwlock_rdlock(hdr);
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  3            
  0            
  0            
3690              
3691 7           uint32_t mask = hdr->table_cap - 1;
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  3            
  0            
  0            
3692             #ifdef SHM_KEY_IS_INT
3693 6           uint32_t hash = SHM_HASH_KEY(key);
  3            
  0            
  0            
  3            
  0            
  0            
3694             #else
3695 1           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  0            
  1            
  0            
  0            
3696             #endif
3697 7           uint32_t pos = hash & mask;
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  3            
  0            
  0            
3698 7           uint8_t tag = SHM_MAKE_TAG(hash);
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  3            
  0            
  0            
3699              
3700 7 0         for (uint32_t i = 0; i <= mask; i++) {
  0 50          
  1 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
3701 7           uint32_t idx = (pos + i) & mask;
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  3            
  0            
  0            
3702 7           uint8_t st = states[idx];
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  3            
  0            
  0            
3703 7           __builtin_prefetch(&nodes[idx], 0, 1);
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  3            
  0            
  0            
3704 7           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  3            
  0            
  0            
3705 7 0         if (st == SHM_EMPTY) break;
  0 50          
  1 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0 100          
  3 0          
  0 0          
  0            
3706 6 0         if (st != tag) continue;
  0 50          
  1 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
3707             #ifdef SHM_KEY_IS_INT
3708 5 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  3 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
3709             #else
3710 1 0         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  0 50          
  1 0          
  0 0          
  0            
3711             #endif
3712 6 0         if (SHM_IS_EXPIRED(h, idx, now_ts)) {
  0 0          
  1 0          
  0 50          
  0 50          
  3 50          
  0 0          
  0 0          
  2 0          
  0 0          
  0 0          
    0          
    50          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
3713 1           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3714 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3715             }
3716             #ifdef SHM_VAL_IS_STR
3717             {
3718 3 0         uint32_t vl = SHM_STR_LEN(nodes[idx].val_len);
  0 50          
  3 0          
  0 0          
  0            
3719 3 0         if (!shm_ensure_copy_buf(h, vl)) {
  0 50          
  3 0          
  0 0          
  0            
3720 0           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
3721 0           return 0;
  0            
  0            
  0            
  0            
3722             }
3723 3           shm_str_copy(h->copy_buf, nodes[idx].val_off, nodes[idx].val_len, h->arena, vl);
  0            
  3            
  0            
  0            
3724 3           *out_str = h->copy_buf;
  0            
  3            
  0            
  0            
3725 3           *out_len = vl;
  0            
  3            
  0            
  0            
3726 3           *out_utf8 = SHM_UNPACK_UTF8(nodes[idx].val_len);
  0            
  3            
  0            
  0            
3727             }
3728             #else
3729 2           *out_value = nodes[idx].value;
  1            
  0            
  0            
  1            
  0            
  0            
3730             #endif
3731 5 0         if (out_ttl_remaining) {
  0 50          
  1 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
3732 5 0         if (!h->expires_at) *out_ttl_remaining = -1;
  0 50          
  1 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
3733 4 0         else if (h->expires_at[idx] == 0) *out_ttl_remaining = 0;
  0 50          
  1 0          
  0 0          
  0 100          
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
3734 3           else *out_ttl_remaining = (int64_t)(h->expires_at[idx] - now_ts);
  0            
  1            
  0            
  0            
  2            
  0            
  0            
  0            
  0            
  0            
3735             }
3736 5           shm_rwlock_rdunlock(hdr);
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  1            
  0            
  0            
3737 5           return 1;
  0            
  1            
  0            
  0            
  3            
  0            
  0            
  1            
  0            
  0            
3738             }
3739             }
3740              
3741 1           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3742 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3743             }
3744              
3745             /* ---- TTL remaining for a key (-1 = not found/expired, 0 = permanent) ---- */
3746              
3747 54           static int64_t SHM_FN(ttl_remaining)(ShmHandle *h,
  6            
  1            
  2            
  1            
  1            
  1            
  0            
  38            
  0            
  4            
3748             #ifdef SHM_KEY_IS_INT
3749             SHM_KEY_INT_TYPE key
3750             #else
3751             const char *key_str, uint32_t key_len, bool key_utf8
3752             #endif
3753             ) {
3754             #ifdef SHM_KEY_IS_INT
3755 44 50         SHM_SHARD_DISPATCH(h, key);
  1 50          
  1 0          
  0 100          
  38 0          
  0 50          
  4            
3756             #else
3757 10 50         SHM_SHARD_DISPATCH(h, key_str, key_len);
  6 50          
  1 50          
  2 50          
  1            
3758             #endif
3759 54 50         if (!h->expires_at) return -1; /* TTL not enabled */
  6 50          
  1 50          
  2 50          
  1 50          
  1 50          
  1 0          
  0 100          
  38 0          
  0 50          
  4            
3760              
3761 53           ShmHeader *hdr = h->hdr;
  6            
  1            
  2            
  1            
  1            
  1            
  0            
  37            
  0            
  4            
3762 53           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  6            
  1            
  2            
  1            
  1            
  1            
  0            
  37            
  0            
  4            
3763 53           uint8_t *states = h->states;
  6            
  1            
  2            
  1            
  1            
  1            
  0            
  37            
  0            
  4            
3764              
3765 53           shm_rwlock_rdlock(hdr);
  6            
  1            
  2            
  1            
  1            
  1            
  0            
  37            
  0            
  4            
3766              
3767 53           uint32_t mask = hdr->table_cap - 1;
  6            
  1            
  2            
  1            
  1            
  1            
  0            
  37            
  0            
  4            
3768             #ifdef SHM_KEY_IS_INT
3769 43           uint32_t hash = SHM_HASH_KEY(key);
  1            
  1            
  0            
  37            
  0            
  4            
3770             #else
3771 10           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  6            
  1            
  2            
  1            
3772             #endif
3773 53           uint32_t pos = hash & mask;
  6            
  1            
  2            
  1            
  1            
  1            
  0            
  37            
  0            
  4            
3774 53           uint8_t tag = SHM_MAKE_TAG(hash);
  6            
  1            
  2            
  1            
  1            
  1            
  0            
  37            
  0            
  4            
3775              
3776 56 50         for (uint32_t i = 0; i <= mask; i++) {
  7 50          
  1 50          
  2 50          
  1 50          
  1 50          
  1 0          
  0 50          
  39 0          
  0 50          
  4            
3777 56           uint32_t idx = (pos + i) & mask;
  7            
  1            
  2            
  1            
  1            
  1            
  0            
  39            
  0            
  4            
3778 56           uint8_t st = states[idx];
  7            
  1            
  2            
  1            
  1            
  1            
  0            
  39            
  0            
  4            
3779 56           __builtin_prefetch(&nodes[idx], 0, 1);
  7            
  1            
  2            
  1            
  1            
  1            
  0            
  39            
  0            
  4            
3780 56           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  7            
  1            
  2            
  1            
  1            
  1            
  0            
  39            
  0            
  4            
3781 56 100         if (st == SHM_EMPTY) break;
  7 50          
  1 50          
  2 50          
  1 50          
  1 50          
  1 0          
  0 100          
  39 0          
  0 50          
  4            
3782 54 100         if (st != tag) continue; /* tombstone or tag mismatch */
  6 50          
  1 50          
  2 50          
  1 50          
  1 50          
  1 0          
  0 100          
  38 0          
  0 50          
  4            
3783             #ifdef SHM_KEY_IS_INT
3784 42 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  1 50          
  1 0          
  0 50          
  36 0          
  0 50          
  4            
3785             #else
3786 9 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  5 50          
  1 50          
  2 50          
  1            
3787             #endif
3788 51           uint32_t exp = h->expires_at[idx];
  5            
  1            
  2            
  1            
  1            
  1            
  0            
  36            
  0            
  4            
3789 51 50         if (exp == 0) {
  5 50          
  1 100          
  2 50          
  1 50          
  1 50          
  1 0          
  0 100          
  36 0          
  0 100          
  4            
3790 17           shm_rwlock_rdunlock(hdr);
  0            
  0            
  1            
  1            
  1            
  1            
  0            
  12            
  0            
  1            
3791 17           return 0; /* permanent entry */
  0            
  0            
  1            
  1            
  1            
  1            
  0            
  12            
  0            
  1            
3792             }
3793 34           uint32_t now = shm_now();
  5            
  1            
  1            
  0            
  0            
  0            
  0            
  24            
  0            
  3            
3794 34 50         if (now >= exp) {
  5 50          
  1 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  24 0          
  0 50          
  3            
3795 0           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3796 0           return -1; /* expired */
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3797             }
3798 34           int64_t remaining = (int64_t)(exp - now);
  5            
  1            
  1            
  0            
  0            
  0            
  0            
  24            
  0            
  3            
3799 34           shm_rwlock_rdunlock(hdr);
  5            
  1            
  1            
  0            
  0            
  0            
  0            
  24            
  0            
  3            
3800 34           return remaining;
  5            
  1            
  1            
  0            
  0            
  0            
  0            
  24            
  0            
  3            
3801             }
3802             }
3803              
3804 2           shm_rwlock_rdunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3805 2           return -1; /* not found */
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3806             }
3807              
3808             /* ---- Persist (remove TTL from a key, making it permanent) ---- */
3809             /* Seqlock required: get() reads expires_at[] under seqlock. */
3810              
3811 10           static int SHM_FN(persist)(ShmHandle *h,
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  4            
  0            
  1            
3812             #ifdef SHM_KEY_IS_INT
3813             SHM_KEY_INT_TYPE key
3814             #else
3815             const char *key_str, uint32_t key_len, bool key_utf8
3816             #endif
3817             ) {
3818             #ifdef SHM_KEY_IS_INT
3819 8 50         SHM_SHARD_DISPATCH(h, key);
  2 50          
  1 0          
  0 100          
  4 0          
  0 50          
  1            
3820             #else
3821 2 0         SHM_SHARD_DISPATCH(h, key_str, key_len);
  0 0          
  0 50          
  1 50          
  1            
3822             #endif
3823 10 0         if (!h->expires_at) return 0;
  0 0          
  0 50          
  1 50          
  1 50          
  2 50          
  1 0          
  0 50          
  4 0          
  0 50          
  1            
3824              
3825 10           ShmHeader *hdr = h->hdr;
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  4            
  0            
  1            
3826 10           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  4            
  0            
  1            
3827 10           uint8_t *states = h->states;
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  4            
  0            
  1            
3828 10           uint32_t now = shm_now();
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  4            
  0            
  1            
3829              
3830 10           shm_rwlock_wrlock(hdr);
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  4            
  0            
  1            
3831              
3832 10           uint32_t mask = hdr->table_cap - 1;
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  4            
  0            
  1            
3833             #ifdef SHM_KEY_IS_INT
3834 8           uint32_t hash = SHM_HASH_KEY(key);
  2            
  1            
  0            
  4            
  0            
  1            
3835             #else
3836 2           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  0            
  0            
  1            
  1            
3837             #endif
3838 10           uint32_t pos = hash & mask;
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  4            
  0            
  1            
3839 10           uint8_t tag = SHM_MAKE_TAG(hash);
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  4            
  0            
  1            
3840              
3841 10 0         for (uint32_t i = 0; i <= mask; i++) {
  0 0          
  0 50          
  1 50          
  1 50          
  2 50          
  1 0          
  0 50          
  4 0          
  0 50          
  1            
3842 10           uint32_t idx = (pos + i) & mask;
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  4            
  0            
  1            
3843 10           uint8_t st = states[idx];
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  4            
  0            
  1            
3844 10           __builtin_prefetch(&nodes[idx], 0, 1);
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  4            
  0            
  1            
3845 10           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  4            
  0            
  1            
3846 10 0         if (st == SHM_EMPTY) break;
  0 0          
  0 50          
  1 50          
  1 50          
  2 50          
  1 0          
  0 100          
  4 0          
  0 50          
  1            
3847 9 0         if (st != tag) continue;
  0 0          
  0 50          
  1 50          
  1 50          
  2 50          
  1 0          
  0 50          
  3 0          
  0 50          
  1            
3848             #ifdef SHM_KEY_IS_INT
3849 7 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  2 50          
  1 0          
  0 50          
  3 0          
  0 50          
  1            
3850             #else
3851 2 0         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  0 0          
  0 50          
  1 50          
  1            
3852             #endif
3853 9 0         if (SHM_IS_EXPIRED(h, idx, now)) {
  0 0          
  0 0          
  1 0          
  1 0          
  2 0          
  1 50          
  0 50          
  3 50          
  0 50          
  1 50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    0          
    50          
    50          
    50          
3854 0           shm_seqlock_write_begin(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3855 0           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3856 0           SHM_FN(maybe_shrink)(h);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3857 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3858 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3859 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3860             }
3861 9           shm_seqlock_write_begin(&hdr->seq);
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  3            
  0            
  1            
3862 9           h->expires_at[idx] = 0; /* permanent */
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  3            
  0            
  1            
3863 9           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  3            
  0            
  1            
3864 9           shm_rwlock_wrunlock(hdr);
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  3            
  0            
  1            
3865 9           return 1;
  0            
  0            
  1            
  1            
  2            
  1            
  0            
  3            
  0            
  1            
3866             }
3867             }
3868              
3869 1           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3870 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3871             }
3872              
3873             /* ---- Set TTL (change TTL without changing value) ---- */
3874              
3875 10           static int SHM_FN(set_ttl)(ShmHandle *h,
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  5            
  0            
  1            
3876             #ifdef SHM_KEY_IS_INT
3877             SHM_KEY_INT_TYPE key,
3878             #else
3879             const char *key_str, uint32_t key_len, bool key_utf8,
3880             #endif
3881             uint32_t ttl_sec
3882             ) {
3883             #ifdef SHM_KEY_IS_INT
3884 7 50         SHM_SHARD_DISPATCH(h, key);
  1 0          
  0 0          
  0 100          
  5 0          
  0 50          
  1            
3885             #else
3886 3 0         SHM_SHARD_DISPATCH(h, key_str, key_len);
  0 50          
  1 50          
  1 50          
  1            
3887             #endif
3888 10 0         if (!h->expires_at) return 0;
  0 50          
  1 50          
  1 50          
  1 50          
  1 0          
  0 0          
  0 50          
  5 0          
  0 50          
  1            
3889              
3890 10           ShmHeader *hdr = h->hdr;
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  5            
  0            
  1            
3891 10           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  5            
  0            
  1            
3892 10           uint8_t *states = h->states;
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  5            
  0            
  1            
3893 10           uint32_t now = shm_now();
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  5            
  0            
  1            
3894              
3895 10           shm_rwlock_wrlock(hdr);
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  5            
  0            
  1            
3896              
3897 10           uint32_t mask = hdr->table_cap - 1;
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  5            
  0            
  1            
3898             #ifdef SHM_KEY_IS_INT
3899 7           uint32_t hash = SHM_HASH_KEY(key);
  1            
  0            
  0            
  5            
  0            
  1            
3900             #else
3901 3           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  0            
  1            
  1            
  1            
3902             #endif
3903 10           uint32_t pos = hash & mask;
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  5            
  0            
  1            
3904 10           uint8_t tag = SHM_MAKE_TAG(hash);
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  5            
  0            
  1            
3905              
3906 10 0         for (uint32_t i = 0; i <= mask; i++) {
  0 50          
  1 50          
  1 50          
  1 50          
  1 0          
  0 0          
  0 50          
  5 0          
  0 50          
  1            
3907 10           uint32_t idx = (pos + i) & mask;
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  5            
  0            
  1            
3908 10           uint8_t st = states[idx];
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  5            
  0            
  1            
3909 10           __builtin_prefetch(&nodes[idx], 0, 1);
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  5            
  0            
  1            
3910 10           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  5            
  0            
  1            
3911 10 0         if (st == SHM_EMPTY) break;
  0 50          
  1 50          
  1 50          
  1 50          
  1 0          
  0 0          
  0 100          
  5 0          
  0 50          
  1            
3912 9 0         if (st != tag) continue;
  0 50          
  1 50          
  1 50          
  1 50          
  1 0          
  0 0          
  0 50          
  4 0          
  0 50          
  1            
3913             #ifdef SHM_KEY_IS_INT
3914 6 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  1 0          
  0 0          
  0 50          
  4 0          
  0 50          
  1            
3915             #else
3916 3 0         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  0 50          
  1 50          
  1 50          
  1            
3917             #endif
3918 9 0         if (SHM_IS_EXPIRED(h, idx, now)) {
  0 0          
  1 0          
  1 50          
  1 50          
  1 50          
  0 50          
  0 50          
  4 0          
  0 50          
  1 50          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    100          
    50          
    0          
    0          
    0          
    50          
    50          
    50          
3919 0           shm_seqlock_write_begin(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3920 0           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3921 0           SHM_FN(maybe_shrink)(h);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3922 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3923 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3924 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
3925             }
3926 9           shm_seqlock_write_begin(&hdr->seq);
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  4            
  0            
  1            
3927 9 0         if (ttl_sec == 0)
  0 50          
  1 50          
  1 50          
  1 50          
  1 0          
  0 0          
  0 100          
  4 0          
  0 50          
  1            
3928 1           h->expires_at[idx] = 0; /* permanent */
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3929             else
3930 8           h->expires_at[idx] = shm_expiry_ts(ttl_sec);
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  3            
  0            
  1            
3931 9           shm_seqlock_write_end(&hdr->seq);
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  4            
  0            
  1            
3932 9           shm_rwlock_wrunlock(hdr);
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  4            
  0            
  1            
3933 9           return 1;
  0            
  1            
  1            
  1            
  1            
  0            
  0            
  4            
  0            
  1            
3934             }
3935             }
3936              
3937 1           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3938 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3939             }
3940              
3941             /* ---- Stats ---- */
3942              
3943 37           static inline uint32_t SHM_FN(capacity)(ShmHandle *h) {
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  34            
  0            
  0            
3944 37 50         if (h->shard_handles) {
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  34 0          
  0 0          
  0            
3945 3           uint32_t total = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
3946 15 0         for (uint32_t i = 0; i < h->num_shards; i++)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  15 0          
  0 0          
  0            
3947 12           total += SHM_FN(capacity)(h->shard_handles[i]);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
3948 3           return (uint32_t)total;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
3949             }
3950 34           return h->hdr->table_cap;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  31            
  0            
  0            
3951             }
3952              
3953 18           static inline uint32_t SHM_FN(tombstones)(ShmHandle *h) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  17            
  0            
  0            
3954 18 50         if (h->shard_handles) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  17 0          
  0 0          
  0            
3955 2           uint32_t total = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
3956 10 0         for (uint32_t i = 0; i < h->num_shards; i++)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  10 0          
  0 0          
  0            
3957 8           total += SHM_FN(tombstones)(h->shard_handles[i]);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
3958 2           return (uint32_t)total;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
3959             }
3960 16           return h->hdr->tombstones;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  15            
  0            
  0            
3961             }
3962              
3963 10           static inline size_t SHM_FN(mmap_size)(ShmHandle *h) {
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
3964 10 50         if (h->shard_handles) return SHM_FN(mmap_size)(h->shard_handles[0]);
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  8 0          
  0 0          
  0            
3965 8           return h->mmap_size;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
3966             }
3967              
3968             /* ---- Flush expired entries (partial / gradual) ---- */
3969              
3970             /* Scan up to `limit` slots starting from the shared flush_cursor.
3971             * Returns the number of entries actually expired.
3972             * Sets *done_out to 1 if the cursor wrapped around (full cycle complete).
3973             * The wrlock is held only for `limit` slots, not the entire table. */
3974 42           static uint32_t SHM_FN(flush_expired_partial)(ShmHandle *h, uint32_t limit, int *done_out) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  41            
  0            
  0            
3975 42 50         if (h->shard_handles) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  41 0          
  0 0          
  0            
3976 1           uint32_t total = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3977 1           int all_done = 1;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3978 5 0         for (uint32_t i = 0; i < h->num_shards; i++) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  5 0          
  0 0          
  0            
3979 4           int done = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
3980 4           total += SHM_FN(flush_expired_partial)(h->shard_handles[i], limit, &done);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
3981 4 0         if (!done) all_done = 0;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  4 0          
  0 0          
  0            
3982             }
3983 1 0         if (done_out) *done_out = all_done;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
3984 1           return total;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3985             }
3986 41 50         if (!h->expires_at) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  40 0          
  0 0          
  0            
3987 1 0         if (done_out) *done_out = 1; /* trivially complete */
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
3988 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
3989             }
3990 40 50         if (done_out) *done_out = 0;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  39 0          
  0 0          
  0            
3991              
3992 40           ShmHeader *hdr = h->hdr;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  39            
  0            
  0            
3993 40           uint8_t *states = h->states;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  39            
  0            
  0            
3994 40           uint32_t now = shm_now();
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  39            
  0            
  0            
3995 40           uint32_t flushed = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  39            
  0            
  0            
3996              
3997 40           shm_rwlock_wrlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  39            
  0            
  0            
3998 40           shm_seqlock_write_begin(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  39            
  0            
  0            
3999              
4000 40           uint32_t cap = hdr->table_cap;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  39            
  0            
  0            
4001 40           uint32_t start = hdr->flush_cursor;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  39            
  0            
  0            
4002 40 50         if (start >= cap) start = 0; /* clamp after shrink */
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  39 0          
  0 0          
  0            
4003 40 50         if (limit == 0) limit = 1; /* scan at least one slot */
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  39 0          
  0 0          
  0            
4004 40 50         if (limit > cap) limit = cap; /* can't scan more than exists */
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  39 0          
  0 0          
  0            
4005              
4006 956 100         for (uint32_t n = 0; n < limit; n++) {
  17 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  939 0          
  0 0          
  0            
4007 916           uint32_t i = (start + n) % cap;
  16            
  0            
  0            
  0            
  0            
  0            
  0            
  900            
  0            
  0            
4008 916 100         if (SHM_IS_LIVE(states[i]) && h->expires_at[i] != 0 && now >= h->expires_at[i]) {
  16 50          
  0 50          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  900 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
4009 247           SHM_FN(expire_at)(h, i);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  245            
  0            
  0            
4010 247           flushed++;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  245            
  0            
  0            
4011             }
4012             }
4013              
4014 40           uint32_t next = (start + limit) % cap;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  39            
  0            
  0            
4015             /* Full cycle complete when this chunk crossed the end of the table */
4016 40 50         int done = (limit >= cap || start + limit >= cap);
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  39 0          
  0 0          
  0 0          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
    0          
    0          
4017 40 50         hdr->flush_cursor = done ? 0 : next;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  39 0          
  0 0          
  0            
4018              
4019 40 50         if (done_out) *done_out = done;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  39 0          
  0 0          
  0            
4020              
4021             /* Only shrink/compact when a full cycle is done, otherwise the rehash
4022             * can move entries to slots the cursor already passed. */
4023 40 50         if (done && flushed > 0)
  1 50          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  39 0          
  0 0          
  0 0          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
    0          
    0          
4024 10           SHM_FN(maybe_shrink)(h);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  9            
  0            
  0            
4025              
4026 40           shm_seqlock_write_end(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  39            
  0            
  0            
4027 40           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  39            
  0            
  0            
4028 40           return flushed;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  39            
  0            
  0            
4029             }
4030              
4031             /* Convenience: full scan in one call (original behavior). */
4032 13           static uint32_t SHM_FN(flush_expired)(ShmHandle *h) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
4033 13 50         if (h->shard_handles) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  12 0          
  0 0          
  0            
4034 1           uint32_t total = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4035 5 0         for (uint32_t i = 0; i < h->num_shards; i++)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  5 0          
  0 0          
  0            
4036 4           total += SHM_FN(flush_expired)(h->shard_handles[i]);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
4037 1           return total;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4038             }
4039 12 50         if (!h->expires_at) return 0;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  11 0          
  0 0          
  0            
4040             int done;
4041 11           return SHM_FN(flush_expired_partial)(h, UINT32_MAX, &done);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
4042             }
4043              
4044             /* ---- Touch (refresh TTL / promote LRU without changing value) ---- */
4045              
4046 12           static int SHM_FN(touch)(ShmHandle *h,
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  9            
  0            
  0            
4047             #ifdef SHM_KEY_IS_INT
4048             SHM_KEY_INT_TYPE key
4049             #else
4050             const char *key_str, uint32_t key_len, bool key_utf8
4051             #endif
4052             ) {
4053             #ifdef SHM_KEY_IS_INT
4054 9 0         SHM_SHARD_DISPATCH(h, key);
  0 0          
  0 0          
  0 100          
  9 0          
  0 0          
  0            
4055             #else
4056 3 50         SHM_SHARD_DISPATCH(h, key_str, key_len);
  2 50          
  1 0          
  0 0          
  0            
4057             #endif
4058 12 50         if (!h->lru_prev && !h->expires_at) return 0; /* nothing to do */
  2 50          
  1 50          
  0 50          
  0 0          
  0 0          
  0 0          
  0 0          
  9 0          
  0 0          
  0 0          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
    0          
    0          
4059              
4060 11           ShmHeader *hdr = h->hdr;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4061 11           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4062 11           uint8_t *states = h->states;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4063 11 50         uint32_t now = h->expires_at ? shm_now() : 0;
  2 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  8 0          
  0 0          
  0            
4064              
4065 11           shm_rwlock_wrlock(hdr);
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4066              
4067 11           uint32_t mask = hdr->table_cap - 1;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4068             #ifdef SHM_KEY_IS_INT
4069 8           uint32_t hash = SHM_HASH_KEY(key);
  0            
  0            
  0            
  8            
  0            
  0            
4070             #else
4071 3           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  2            
  1            
  0            
  0            
4072             #endif
4073 11           uint32_t pos = hash & mask;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4074 11           uint8_t tag = SHM_MAKE_TAG(hash);
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4075              
4076 11 50         for (uint32_t i = 0; i <= mask; i++) {
  2 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  8 0          
  0 0          
  0            
4077 11           uint32_t idx = (pos + i) & mask;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4078 11           uint8_t st = states[idx];
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4079 11           __builtin_prefetch(&nodes[idx], 0, 1);
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4080 11           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4081 11 100         if (st == SHM_EMPTY) break;
  2 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  8 0          
  0 0          
  0            
4082 9 50         if (st != tag) continue; /* tombstone or tag mismatch */
  1 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  7 0          
  0 0          
  0            
4083             #ifdef SHM_KEY_IS_INT
4084 7 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 0          
  0 50          
  7 0          
  0 0          
  0            
4085             #else
4086 2 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  1 50          
  1 0          
  0 0          
  0            
4087             #endif
4088 9 50         if (SHM_IS_EXPIRED(h, idx, now)) {
  1 50          
  1 50          
  0 50          
  0 50          
  0 50          
  0 0          
  0 0          
  7 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
4089 1           shm_seqlock_write_begin(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4090 1           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4091 1           SHM_FN(maybe_shrink)(h);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4092 1           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4093 1           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4094 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4095             }
4096 8           shm_seqlock_write_begin(&hdr->seq);
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
4097 8 50         if (h->lru_prev) shm_lru_promote(h, idx);
  1 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  6 0          
  0 0          
  0            
4098 8 50         if (h->expires_at && hdr->default_ttl > 0 && h->expires_at[idx] != 0) {
  1 50          
  1 50          
  0 50          
  0 50          
  0 50          
  0 0          
  0 0          
  6 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
4099 6           h->expires_at[idx] = shm_expiry_ts(hdr->default_ttl);
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
4100             }
4101 8           shm_seqlock_write_end(&hdr->seq);
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
4102 8           shm_rwlock_wrunlock(hdr);
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
4103 8           return 1;
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
4104             }
4105             }
4106              
4107 2           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4108 2           return 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4109             }
4110              
4111             /* ---- Reserve (pre-grow table to target capacity) ---- */
4112              
4113 14           static int SHM_FN(reserve)(ShmHandle *h, uint32_t target) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  13            
  0            
  0            
4114 14 50         if (h->shard_handles) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  13 0          
  0 0          
  0            
4115 1           int ok = 1;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4116 5 0         for (uint32_t i = 0; i < h->num_shards; i++)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  5 0          
  0 0          
  0            
4117 4           ok &= SHM_FN(reserve)(h->shard_handles[i], target);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
4118 1           return ok;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4119             }
4120 13           ShmHeader *hdr = h->hdr;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
4121 13 50         if (target > hdr->max_table_cap) return 0;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  12 0          
  0 0          
  0            
4122             /* compute min capacity for target entries at <75% load */
4123 11           uint32_t needed = shm_next_pow2(target + target / 3 + 1);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
4124 11 50         if (needed < SHM_INITIAL_CAP) needed = SHM_INITIAL_CAP;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  10 0          
  0 0          
  0            
4125 11 50         if (needed <= hdr->table_cap) return 1; /* already big enough */
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  10 0          
  0 0          
  0            
4126 10 50         if (needed > hdr->max_table_cap) return 0; /* exceeds max */
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  9 0          
  0 0          
  0            
4127              
4128 9           shm_rwlock_wrlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4129 9           shm_seqlock_write_begin(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4130 9           int ok = 1;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4131 9 50         if (needed > hdr->table_cap)
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  8 0          
  0 0          
  0            
4132 9           ok = SHM_FN(resize)(h, needed);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4133 9           shm_seqlock_write_end(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4134 9           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4135 9           return ok;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4136             }
4137              
4138             /* ---- Cache stats accessors ---- */
4139              
4140 28           static inline uint64_t SHM_FN(stat_evictions)(ShmHandle *h) {
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  25            
  0            
  0            
4141 28 50         if (h->shard_handles) {
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  25 0          
  0 0          
  0            
4142 3           uint64_t total = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
4143 15 0         for (uint32_t i = 0; i < h->num_shards; i++)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  15 0          
  0 0          
  0            
4144 12           total += SHM_FN(stat_evictions)(h->shard_handles[i]);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
4145 3           return (uint64_t)total;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
4146             }
4147 25           return __atomic_load_n(&h->hdr->stat_evictions, __ATOMIC_RELAXED);
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  22            
  0            
  0            
4148             }
4149              
4150 17           static inline uint64_t SHM_FN(stat_expired)(ShmHandle *h) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  16            
  0            
  0            
4151 17 50         if (h->shard_handles) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  16 0          
  0 0          
  0            
4152 2           uint64_t total = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4153 10 0         for (uint32_t i = 0; i < h->num_shards; i++)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  10 0          
  0 0          
  0            
4154 8           total += SHM_FN(stat_expired)(h->shard_handles[i]);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4155 2           return (uint64_t)total;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4156             }
4157 15           return __atomic_load_n(&h->hdr->stat_expired, __ATOMIC_RELAXED);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  14            
  0            
  0            
4158             }
4159              
4160 13           static inline uint32_t SHM_FN(stat_recoveries)(ShmHandle *h) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
4161 13 50         if (h->shard_handles) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  12 0          
  0 0          
  0            
4162 2           uint32_t total = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4163 10 0         for (uint32_t i = 0; i < h->num_shards; i++)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  10 0          
  0 0          
  0            
4164 8           total += SHM_FN(stat_recoveries)(h->shard_handles[i]);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4165 2           return (uint32_t)total;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4166             }
4167 11           return __atomic_load_n(&h->hdr->stat_recoveries, __ATOMIC_RELAXED);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
4168             }
4169              
4170 17           static inline uint64_t SHM_FN(arena_used)(ShmHandle *h) {
  5            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
4171 17 50         if (h->shard_handles) {
  5 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  12 0          
  0 0          
  0            
4172 2           uint64_t total = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4173 10 0         for (uint32_t i = 0; i < h->num_shards; i++)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  10 0          
  0 0          
  0            
4174 8           total += SHM_FN(arena_used)(h->shard_handles[i]);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4175 2           return (uint64_t)total;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4176             }
4177 15 50         return h->arena ? __atomic_load_n(&h->hdr->arena_bump, __ATOMIC_RELAXED) : 0;
  5 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  10 0          
  0 0          
  0            
4178             }
4179              
4180 14           static inline uint64_t SHM_FN(arena_cap)(ShmHandle *h) {
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
4181 14 50         if (h->shard_handles) {
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  12 0          
  0 0          
  0            
4182 2           uint64_t total = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4183 10 0         for (uint32_t i = 0; i < h->num_shards; i++)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  10 0          
  0 0          
  0            
4184 8           total += SHM_FN(arena_cap)(h->shard_handles[i]);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4185 2           return (uint64_t)total;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4186             }
4187 12 50         return h->arena ? h->hdr->arena_cap : 0;
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  10 0          
  0 0          
  0            
4188             }
4189              
4190             /* ---- Clear ---- */
4191              
4192 30           static void SHM_FN(clear)(ShmHandle *h) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  27            
  1            
  1            
4193 30 50         if (h->shard_handles) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  27 50          
  1 50          
  1            
4194 22 0         for (uint32_t i = 0; i < h->num_shards; i++)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  22 0          
  0 0          
  0            
4195 20           SHM_FN(clear)(h->shard_handles[i]);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  20            
  0            
  0            
4196 2           return;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4197             }
4198 28           ShmHeader *hdr = h->hdr;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  25            
  1            
  1            
4199              
4200 28           shm_rwlock_wrlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  25            
  1            
  1            
4201 28           shm_seqlock_write_begin(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  25            
  1            
  1            
4202              
4203 28           memset(h->states, SHM_EMPTY, hdr->table_cap);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  25            
  1            
  1            
4204 28           hdr->size = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  25            
  1            
  1            
4205 28           hdr->tombstones = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  25            
  1            
  1            
4206              
4207             /* shrink back to initial capacity */
4208 28 50         if (hdr->table_cap > SHM_INITIAL_CAP) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  25 50          
  1 50          
  1            
4209 20           uint32_t old_cap = hdr->table_cap;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  20            
  0            
  0            
4210 20           hdr->table_cap = SHM_INITIAL_CAP;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  20            
  0            
  0            
4211 20           size_t node_shrink = (size_t)(old_cap - SHM_INITIAL_CAP) * sizeof(SHM_NODE_TYPE);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  20            
  0            
  0            
4212 20           madvise((char *)h->nodes + (size_t)SHM_INITIAL_CAP * sizeof(SHM_NODE_TYPE),
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  20            
  0            
  0            
4213             node_shrink, MADV_DONTNEED);
4214 20           madvise(h->states + SHM_INITIAL_CAP, old_cap - SHM_INITIAL_CAP, MADV_DONTNEED);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  20            
  0            
  0            
4215             }
4216              
4217             /* reset arena */
4218 28 50         if (h->arena) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  25 50          
  1 50          
  1            
4219 1           hdr->arena_bump = SHM_ARENA_MIN_ALLOC;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4220 1           memset(hdr->arena_free, 0, sizeof(hdr->arena_free));
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4221 1           madvise(h->arena, (size_t)hdr->arena_cap, MADV_DONTNEED);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4222             }
4223              
4224             /* reset LRU (full max_table_cap range) */
4225 28 50         if (h->lru_prev) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  25 50          
  1 50          
  1            
4226 2           memset(h->lru_prev, 0xFF, hdr->max_table_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4227 2           memset(h->lru_next, 0xFF, hdr->max_table_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4228 2 0         if (h->lru_accessed) memset(h->lru_accessed, 0, hdr->max_table_cap);
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
4229 2           hdr->lru_head = SHM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4230 2           hdr->lru_tail = SHM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4231             }
4232              
4233             /* reset TTL (full max_table_cap range) */
4234 28 50         if (h->expires_at) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  25 50          
  1 50          
  1            
4235 0           memset(h->expires_at, 0, hdr->max_table_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4236 0           hdr->flush_cursor = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4237             }
4238              
4239 28           hdr->table_gen++;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  25            
  1            
  1            
4240              
4241 28           shm_seqlock_write_end(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  25            
  1            
  1            
4242 28           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  25            
  1            
  1            
4243              
4244 28           h->iter_pos = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  25            
  1            
  1            
4245 28 50         if (h->iter_active) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  25 50          
  1 50          
  1            
4246 1           h->iter_active = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4247 1 0         if (h->iterating > 0) h->iterating--;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
4248             }
4249 28           h->deferred = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  25            
  1            
  1            
4250             }
4251              
4252             /* ---- Iterator reset ---- */
4253              
4254 6           static inline void SHM_FN(iter_reset)(ShmHandle *h) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
4255 6 0         if (h->shard_handles) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  6 0          
  0 0          
  0            
4256 5 0         for (uint32_t i = 0; i < h->num_shards; i++)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  5 0          
  0 0          
  0            
4257 4           SHM_FN(iter_reset)(h->shard_handles[i]);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
4258 1           h->shard_iter = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4259 1           return;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4260             }
4261 5 0         if (h->iter_active) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  5 0          
  0 0          
  0            
4262 1           h->iter_active = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4263 1 0         if (h->iterating > 0) h->iterating--;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
4264             }
4265 5           h->iter_pos = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
4266             }
4267              
4268             /* ---- Get or set (atomic: lookup + insert under single wrlock) ---- */
4269              
4270 23           static int SHM_FN(get_or_set)(ShmHandle *h,
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4271             #ifdef SHM_KEY_IS_INT
4272             SHM_KEY_INT_TYPE key,
4273             #else
4274             const char *key_str, uint32_t key_len, bool key_utf8,
4275             #endif
4276             #ifdef SHM_VAL_IS_STR
4277             const char *def_str, uint32_t def_len, bool def_utf8,
4278             const char **out_str, uint32_t *out_len, bool *out_utf8
4279             #else
4280             SHM_VAL_INT_TYPE def_value,
4281             SHM_VAL_INT_TYPE *out_value
4282             #endif
4283             ) {
4284             #ifdef SHM_KEY_IS_INT
4285 13 0         SHM_SHARD_DISPATCH(h, key);
  0 0          
  0 50          
  2 100          
  7 50          
  2 50          
  2            
4286             #else
4287 10 50         SHM_SHARD_DISPATCH(h, key_str, key_len);
  8 0          
  0 0          
  0 50          
  2            
4288             #endif
4289 23           ShmHeader *hdr = h->hdr;
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4290 23           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4291 23           uint8_t *states = h->states;
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4292 23 50         uint32_t now = h->expires_at ? shm_now() : 0;
  8 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0 50          
  2 100          
  7 50          
  2 50          
  2            
4293              
4294 23           shm_rwlock_wrlock(hdr);
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4295 23           shm_seqlock_write_begin(&hdr->seq);
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4296              
4297 23           SHM_FN(maybe_grow)(h);
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4298 23           uint32_t mask = hdr->table_cap - 1;
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4299             #ifdef SHM_KEY_IS_INT
4300 13           uint32_t hash = SHM_HASH_KEY(key);
  0            
  0            
  2            
  7            
  2            
  2            
4301             #else
4302 10           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  8            
  0            
  0            
  2            
4303             #endif
4304 23           uint32_t pos = hash & mask;
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4305 23           uint32_t insert_pos = UINT32_MAX;
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4306              
4307 23           uint8_t tag = SHM_MAKE_TAG(hash);
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4308 23 50         for (uint32_t i = 0; i <= mask; i++) {
  8 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0 50          
  2 50          
  7 50          
  2 50          
  2            
4309 23           uint32_t idx = (pos + i) & mask;
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4310 23           uint8_t st = states[idx];
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4311 23           __builtin_prefetch(&nodes[idx], 0, 1);
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4312 23           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
  8            
  0            
  0            
  2            
  0            
  0            
  2            
  7            
  2            
  2            
4313              
4314 23 100         if (st == SHM_EMPTY) {
  8 0          
  0 0          
  0 100          
  2 0          
  0 0          
  0 100          
  2 100          
  7 100          
  2 100          
  2            
4315 11 50         if (insert_pos == UINT32_MAX) insert_pos = idx;
  5 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  2 50          
  1 50          
  1            
4316 11           break;
  5            
  0            
  0            
  1            
  0            
  0            
  1            
  2            
  1            
  1            
4317             }
4318 12 50         if (st == SHM_TOMBSTONE) {
  3 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  5 50          
  1 50          
  1            
4319 0 0         if (insert_pos == UINT32_MAX) insert_pos = idx;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
4320 0           continue;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4321             }
4322 12 50         if (st != tag) continue;
  3 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  5 50          
  1 50          
  1            
4323             #ifdef SHM_KEY_IS_INT
4324 8 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 50          
  1 50          
  5 50          
  1 50          
  1            
4325             #else
4326 4 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  3 0          
  0 0          
  0 50          
  1            
4327             #endif
4328             /* TTL check */
4329 12 50         if (SHM_IS_EXPIRED(h, idx, now)) {
  3 0          
  0 0          
  0 0          
  1 0          
  0 0          
  0 0          
  1 0          
  5 0          
  1 50          
  1 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    100          
    100          
    100          
    50          
    0          
    0          
    50          
    0          
    0          
4330 1           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4331 1 0         if (insert_pos == UINT32_MAX) insert_pos = idx;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
4332 1           break;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4333             }
4334              
4335             #ifdef SHM_VAL_IS_STR
4336             {
4337 4 50         uint32_t vl = SHM_STR_LEN(nodes[idx].val_len);
  3 0          
  0 0          
  0 50          
  1            
4338 4 50         if (!shm_ensure_copy_buf(h, vl)) {
  3 0          
  0 0          
  0 50          
  1            
4339 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
4340 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
4341 0           return 0;
  0            
  0            
  0            
  0            
4342             }
4343 4           shm_str_copy(h->copy_buf, nodes[idx].val_off, nodes[idx].val_len, h->arena, vl);
  3            
  0            
  0            
  1            
4344 4           *out_str = h->copy_buf;
  3            
  0            
  0            
  1            
4345 4           *out_len = vl;
  3            
  0            
  0            
  1            
4346 4           *out_utf8 = SHM_UNPACK_UTF8(nodes[idx].val_len);
  3            
  0            
  0            
  1            
4347             }
4348             #else
4349 7           *out_value = nodes[idx].value;
  0            
  0            
  1            
  4            
  1            
  1            
4350             #endif
4351 11 50         if (h->lru_prev) shm_lru_promote(h, idx);
  3 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  4 50          
  1 50          
  1            
4352 11 50         if (h->expires_at && hdr->default_ttl > 0 && h->expires_at[idx] != 0)
  3 0          
  0 0          
  0 0          
  1 0          
  0 0          
  0 0          
  1 0          
  4 0          
  1 50          
  1 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    100          
    50          
    100          
    50          
    0          
    0          
    50          
    0          
    0          
4353 1           h->expires_at[idx] = shm_expiry_ts(hdr->default_ttl);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4354 11           shm_seqlock_write_end(&hdr->seq);
  3            
  0            
  0            
  1            
  0            
  0            
  1            
  4            
  1            
  1            
4355 11           shm_rwlock_wrunlock(hdr);
  3            
  0            
  0            
  1            
  0            
  0            
  1            
  4            
  1            
  1            
4356 11           return 1;
  3            
  0            
  0            
  1            
  0            
  0            
  1            
  4            
  1            
  1            
4357             }
4358             }
4359              
4360             /* not found — insert default value */
4361 12 50         if (insert_pos == UINT32_MAX) {
  5 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  3 50          
  1 50          
  1            
4362 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4363 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4364 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4365             }
4366              
4367             /* LRU eviction only when actually inserting a new entry */
4368 12 50         if (hdr->max_size > 0 && hdr->size >= hdr->max_size)
  5 0          
  0 0          
  0 0          
  1 0          
  0 0          
  0 50          
  1 0          
  3 0          
  1 0          
  1 0          
    0          
    50          
    0          
    50          
    0          
    50          
    0          
    50          
    0          
4369 0           SHM_FN(lru_evict_one)(h);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4370              
4371 12           int was_tombstone = (states[insert_pos] == SHM_TOMBSTONE);
  5            
  0            
  0            
  1            
  0            
  0            
  1            
  3            
  1            
  1            
4372              
4373             #ifdef SHM_KEY_IS_INT
4374 6           nodes[insert_pos].key = key;
  0            
  0            
  1            
  3            
  1            
  1            
4375             #else
4376 6 50         if (!shm_str_store(hdr, h->arena, &nodes[insert_pos].key_off, &nodes[insert_pos].key_len, key_str, key_len, key_utf8)) {
  5 0          
  0 0          
  0 50          
  1            
4377 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
4378 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
4379 0           return 0;
  0            
  0            
  0            
  0            
4380             }
4381             #endif
4382              
4383             #ifdef SHM_VAL_IS_STR
4384 6 50         if (!shm_ensure_copy_buf(h, def_len > 0 ? def_len : 1)) {
  5 50          
  0 0          
  0 0          
  1 0          
    0          
    50          
    50          
4385             #ifndef SHM_KEY_IS_INT
4386 0           shm_str_free(hdr, h->arena, nodes[insert_pos].key_off, nodes[insert_pos].key_len);
  0            
4387             #endif
4388 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
4389 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
4390 0           return 0;
  0            
  0            
  0            
  0            
4391             }
4392 6 50         if (!shm_str_store(hdr, h->arena, &nodes[insert_pos].val_off, &nodes[insert_pos].val_len, def_str, def_len, def_utf8)) {
  5 0          
  0 0          
  0 50          
  1            
4393             #ifndef SHM_KEY_IS_INT
4394 0           shm_str_free(hdr, h->arena, nodes[insert_pos].key_off, nodes[insert_pos].key_len);
  0            
4395             #endif
4396 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
4397 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
4398 0           return 0;
  0            
  0            
  0            
  0            
4399             }
4400             #else
4401 6           nodes[insert_pos].value = def_value;
  0            
  0            
  1            
  3            
  1            
  1            
4402             #endif
4403              
4404 12           states[insert_pos] = SHM_MAKE_TAG(hash);
  5            
  0            
  0            
  1            
  0            
  0            
  1            
  3            
  1            
  1            
4405 12           hdr->size++;
  5            
  0            
  0            
  1            
  0            
  0            
  1            
  3            
  1            
  1            
4406 12 50         if (was_tombstone) hdr->tombstones--;
  5 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 100          
  3 50          
  1 50          
  1            
4407              
4408 12 50         if (h->lru_prev) shm_lru_push_front(h, insert_pos);
  5 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  3 50          
  1 50          
  1            
4409 12 50         if (h->expires_at && hdr->default_ttl > 0)
  5 0          
  0 0          
  0 0          
  1 0          
  0 0          
  0 50          
  1 0          
  3 0          
  1 0          
  1 0          
    0          
    50          
    0          
    100          
    50          
    50          
    0          
    50          
    0          
4410 1           h->expires_at[insert_pos] = shm_expiry_ts(hdr->default_ttl);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4411              
4412             #ifdef SHM_VAL_IS_STR
4413 6           memcpy(h->copy_buf, def_str, def_len);
  5            
  0            
  0            
  1            
4414 6           *out_str = h->copy_buf;
  5            
  0            
  0            
  1            
4415 6           *out_len = def_len;
  5            
  0            
  0            
  1            
4416 6           *out_utf8 = def_utf8;
  5            
  0            
  0            
  1            
4417             #else
4418 6           *out_value = def_value;
  0            
  0            
  1            
  3            
  1            
  1            
4419             #endif
4420 12           shm_seqlock_write_end(&hdr->seq);
  5            
  0            
  0            
  1            
  0            
  0            
  1            
  3            
  1            
  1            
4421 12           shm_rwlock_wrunlock(hdr);
  5            
  0            
  0            
  1            
  0            
  0            
  1            
  3            
  1            
  1            
4422 12           return 2; /* 2 = inserted new */
  5            
  0            
  0            
  1            
  0            
  0            
  1            
  3            
  1            
  1            
4423             }
4424              
4425             /* ---- Each (iterator) ---- */
4426              
4427 733           static int SHM_FN(each)(ShmHandle *h,
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  730            
  0            
  0            
4428             #ifdef SHM_KEY_IS_INT
4429             SHM_KEY_INT_TYPE *out_key,
4430             #else
4431             const char **out_key_str, uint32_t *out_key_len, bool *out_key_utf8,
4432             #endif
4433             #ifdef SHM_VAL_IS_STR
4434             const char **out_val_str, uint32_t *out_val_len, bool *out_val_utf8
4435             #else
4436             SHM_VAL_INT_TYPE *out_value
4437             #endif
4438             ) {
4439             /* Sharded: chain each() across shards */
4440 733 50         if (h->shard_handles) {
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  730 0          
  0 0          
  0            
4441 70 0         while (h->shard_iter < h->num_shards) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  70 0          
  0 0          
  0            
4442 68           int rc = SHM_FN(each)(h->shard_handles[h->shard_iter],
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  68            
  0            
  0            
4443             #ifdef SHM_KEY_IS_INT
4444             out_key,
4445             #else
4446             out_key_str, out_key_len, out_key_utf8,
4447             #endif
4448             #ifdef SHM_VAL_IS_STR
4449             out_val_str, out_val_len, out_val_utf8
4450             #else
4451             out_value
4452             #endif
4453             );
4454 68 0         if (rc) return 1;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  68 0          
  0 0          
  0            
4455 8           SHM_FN(flush_deferred)(h->shard_handles[h->shard_iter]);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4456 8           h->shard_iter++;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
4457             }
4458 2           h->shard_iter = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4459 2           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4460             }
4461 671           ShmHeader *hdr = h->hdr;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  668            
  0            
  0            
4462 671           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  668            
  0            
  0            
4463 671           uint8_t *states = h->states;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  668            
  0            
  0            
4464 671 50         uint32_t now = h->expires_at ? shm_now() : 0;
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  668 0          
  0 0          
  0            
4465              
4466 671           shm_rwlock_rdlock(hdr);
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  668            
  0            
  0            
4467              
4468 671 100         if (!h->iter_active) {
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  668 0          
  0 0          
  0            
4469 23           h->iter_active = 1;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  22            
  0            
  0            
4470 23           h->iter_gen = hdr->table_gen;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  22            
  0            
  0            
4471 23           h->iterating++;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  22            
  0            
  0            
4472             }
4473              
4474             /* Auto-reset on cross-process resize */
4475 671 50         if (h->iter_gen != hdr->table_gen) {
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  668 0          
  0 0          
  0            
4476 0           h->iter_pos = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4477 0           h->iter_gen = hdr->table_gen;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4478             }
4479              
4480 671 100         while (shm_find_next_live(states, hdr->table_cap, &h->iter_pos)) {
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  668 0          
  0 0          
  0            
4481 651           uint32_t pos = h->iter_pos++;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  649            
  0            
  0            
4482             {
4483             /* skip expired entries */
4484 651 50         if (SHM_IS_EXPIRED(h, pos, now))
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  649 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
4485 0           continue;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4486              
4487             #ifdef SHM_KEY_IS_INT
4488 649           *out_key = nodes[pos].key;
  0            
  0            
  0            
  649            
  0            
  0            
4489             #else
4490             {
4491 2 50         uint32_t kl = SHM_STR_LEN(nodes[pos].key_len);
  2 0          
  0 0          
  0 0          
  0            
4492             #ifdef SHM_VAL_IS_STR
4493 2 50         uint32_t vl = SHM_STR_LEN(nodes[pos].val_len);
  2            
4494 2           uint64_t total64 = (uint64_t)kl + vl;
  2            
4495 2 50         if (total64 > UINT32_MAX) {
  2            
4496 0           h->iter_pos = 0;
  0            
4497 0           h->iter_active = 0;
  0            
4498 0 0         if (h->iterating > 0) h->iterating--;
  0            
4499 0           shm_rwlock_rdunlock(hdr);
  0            
4500 0           return 0;
  0            
4501             }
4502 2           uint32_t total = (uint32_t)total64;
  2            
4503             #else
4504 0           uint32_t total = kl;
  0            
  0            
  0            
4505             #endif
4506 2 50         if (!shm_ensure_copy_buf(h, total)) {
  2 0          
  0 0          
  0 0          
  0            
4507 0           h->iter_pos = 0;
  0            
  0            
  0            
  0            
4508 0           h->iter_active = 0;
  0            
  0            
  0            
  0            
4509 0 0         if (h->iterating > 0) h->iterating--;
  0 0          
  0 0          
  0 0          
  0            
4510 0           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
4511 0           return 0;
  0            
  0            
  0            
  0            
4512             }
4513 2           shm_str_copy(h->copy_buf, nodes[pos].key_off, nodes[pos].key_len, h->arena, kl);
  2            
  0            
  0            
  0            
4514 2           *out_key_str = h->copy_buf;
  2            
  0            
  0            
  0            
4515 2           *out_key_len = kl;
  2            
  0            
  0            
  0            
4516 2           *out_key_utf8 = SHM_UNPACK_UTF8(nodes[pos].key_len);
  2            
  0            
  0            
  0            
4517             }
4518             #endif
4519             #ifdef SHM_VAL_IS_STR
4520             {
4521 2 50         uint32_t vl = SHM_STR_LEN(nodes[pos].val_len);
  2 0          
  0 0          
  0 0          
  0            
4522             #ifndef SHM_KEY_IS_INT
4523 2 50         uint32_t kl = SHM_STR_LEN(nodes[pos].key_len);
  2            
4524 2           shm_str_copy(h->copy_buf + kl, nodes[pos].val_off, nodes[pos].val_len, h->arena, vl);
  2            
4525 2           *out_val_str = h->copy_buf + kl;
  2            
4526             #else
4527 0 0         if (!shm_ensure_copy_buf(h, vl)) {
  0 0          
  0 0          
  0            
4528 0           h->iter_pos = 0;
  0            
  0            
  0            
4529 0           h->iter_active = 0;
  0            
  0            
  0            
4530 0 0         if (h->iterating > 0) h->iterating--;
  0 0          
  0 0          
  0            
4531 0           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
4532 0           return 0;
  0            
  0            
  0            
4533             }
4534 0           shm_str_copy(h->copy_buf, nodes[pos].val_off, nodes[pos].val_len, h->arena, vl);
  0            
  0            
  0            
4535 0           *out_val_str = h->copy_buf;
  0            
  0            
  0            
4536             #endif
4537 2           *out_val_len = vl;
  2            
  0            
  0            
  0            
4538 2           *out_val_utf8 = SHM_UNPACK_UTF8(nodes[pos].val_len);
  2            
  0            
  0            
  0            
4539             }
4540             #else
4541 649           *out_value = nodes[pos].value;
  0            
  0            
  0            
  649            
  0            
  0            
4542             #endif
4543 651           shm_rwlock_rdunlock(hdr);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  649            
  0            
  0            
4544 651           return 1;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  649            
  0            
  0            
4545             }
4546             }
4547              
4548 20           h->iter_pos = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  19            
  0            
  0            
4549 20           h->iter_active = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  19            
  0            
  0            
4550 20 50         if (h->iterating > 0) h->iterating--;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  19 0          
  0 0          
  0            
4551 20           shm_rwlock_rdunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  19            
  0            
  0            
4552 20           return 0; /* caller should call flush_deferred */
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  19            
  0            
  0            
4553             }
4554              
4555             /* ---- Cursor iteration ---- */
4556              
4557 783           static int SHM_FN(cursor_next)(ShmCursor *c,
  29            
  6            
  0            
  0            
  0            
  0            
  3            
  745            
  0            
  0            
4558             #ifdef SHM_KEY_IS_INT
4559             SHM_KEY_INT_TYPE *out_key,
4560             #else
4561             const char **out_key_str, uint32_t *out_key_len, bool *out_key_utf8,
4562             #endif
4563             #ifdef SHM_VAL_IS_STR
4564             const char **out_val_str, uint32_t *out_val_len, bool *out_val_utf8
4565             #else
4566             SHM_VAL_INT_TYPE *out_value
4567             #endif
4568             ) {
4569             /* Chain across shards: when current shard exhausted, move to next */
4570 1608 100         while (c->shard_idx < c->shard_count) {
  63 100          
  13 0          
  0 0          
  0 0          
  0 0          
  0 100          
  7 100          
  1525 0          
  0 0          
  0            
4571 807           ShmHandle *h = c->current;
  32            
  6            
  0            
  0            
  0            
  0            
  3            
  766            
  0            
  0            
4572 807           ShmHeader *hdr = h->hdr;
  32            
  6            
  0            
  0            
  0            
  0            
  3            
  766            
  0            
  0            
4573 807           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  32            
  6            
  0            
  0            
  0            
  0            
  3            
  766            
  0            
  0            
4574 807           uint8_t *states = h->states;
  32            
  6            
  0            
  0            
  0            
  0            
  3            
  766            
  0            
  0            
4575 807 50         uint32_t now = h->expires_at ? shm_now() : 0;
  32 50          
  6 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3 50          
  766 0          
  0 0          
  0            
4576              
4577 807           shm_rwlock_rdlock(hdr);
  32            
  6            
  0            
  0            
  0            
  0            
  3            
  766            
  0            
  0            
4578              
4579             /* Auto-reset on cross-process resize */
4580 807 50         if (c->gen != hdr->table_gen) {
  32 50          
  6 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3 50          
  766 0          
  0 0          
  0            
4581 0           c->iter_pos = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4582 0           c->gen = hdr->table_gen;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4583             }
4584              
4585 807 100         while (shm_find_next_live(states, hdr->table_cap, &c->iter_pos)) {
  32 100          
  6 0          
  0 0          
  0 0          
  0 0          
  0 100          
  3 100          
  766 0          
  0 0          
  0            
4586 765           uint32_t pos = c->iter_pos++;
  27            
  5            
  0            
  0            
  0            
  0            
  2            
  731            
  0            
  0            
4587             {
4588 765 50         if (SHM_IS_EXPIRED(h, pos, now))
  27 0          
  5 0          
  0 50          
  0 0          
  0 0          
  0 0          
  2 0          
  731 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
4589 0           continue;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4590              
4591             #ifdef SHM_KEY_IS_INT
4592 733           *out_key = nodes[pos].key;
  0            
  0            
  2            
  731            
  0            
  0            
4593             #else
4594             {
4595 32 50         uint32_t kl = SHM_STR_LEN(nodes[pos].key_len);
  27 50          
  5 0          
  0 0          
  0            
4596             #ifdef SHM_VAL_IS_STR
4597 27 50         uint32_t vl = SHM_STR_LEN(nodes[pos].val_len);
  27            
4598 27           uint64_t total64 = (uint64_t)kl + vl;
  27            
4599 27 50         if (total64 > UINT32_MAX) {
  27            
4600 0           shm_rwlock_rdunlock(hdr);
  0            
4601 0           return 0;
  0            
4602             }
4603 27           uint32_t total = (uint32_t)total64;
  27            
4604             #else
4605 5           uint32_t total = kl;
  5            
  0            
  0            
4606             #endif
4607 32 50         if (!shm_cursor_ensure_copy_buf(c, total)) {
  27 50          
  5 0          
  0 0          
  0            
4608 0           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
4609 0           return 0;
  0            
  0            
  0            
  0            
4610             }
4611 32           shm_str_copy(c->copy_buf, nodes[pos].key_off, nodes[pos].key_len, h->arena, kl);
  27            
  5            
  0            
  0            
4612 32           *out_key_str = c->copy_buf;
  27            
  5            
  0            
  0            
4613 32           *out_key_len = kl;
  27            
  5            
  0            
  0            
4614 32           *out_key_utf8 = SHM_UNPACK_UTF8(nodes[pos].key_len);
  27            
  5            
  0            
  0            
4615             }
4616             #endif
4617             #ifdef SHM_VAL_IS_STR
4618             {
4619 29 50         uint32_t vl = SHM_STR_LEN(nodes[pos].val_len);
  27 0          
  0 0          
  0 50          
  2            
4620             #ifndef SHM_KEY_IS_INT
4621 27 50         uint32_t kl = SHM_STR_LEN(nodes[pos].key_len);
  27            
4622 27           shm_str_copy(c->copy_buf + kl, nodes[pos].val_off, nodes[pos].val_len, h->arena, vl);
  27            
4623 27           *out_val_str = c->copy_buf + kl;
  27            
4624             #else
4625 2 0         if (!shm_cursor_ensure_copy_buf(c, vl)) {
  0 0          
  0 50          
  2            
4626 0           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
4627 0           return 0;
  0            
  0            
  0            
4628             }
4629 2           shm_str_copy(c->copy_buf, nodes[pos].val_off, nodes[pos].val_len, h->arena, vl);
  0            
  0            
  2            
4630 2           *out_val_str = c->copy_buf;
  0            
  0            
  2            
4631             #endif
4632 29           *out_val_len = vl;
  27            
  0            
  0            
  2            
4633 29           *out_val_utf8 = SHM_UNPACK_UTF8(nodes[pos].val_len);
  27            
  0            
  0            
  2            
4634             }
4635             #else
4636 736           *out_value = nodes[pos].value;
  5            
  0            
  0            
  731            
  0            
  0            
4637             #endif
4638 765           shm_rwlock_rdunlock(hdr);
  27            
  5            
  0            
  0            
  0            
  0            
  2            
  731            
  0            
  0            
4639 765           return 1;
  27            
  5            
  0            
  0            
  0            
  0            
  2            
  731            
  0            
  0            
4640             }
4641             }
4642              
4643 42           shm_rwlock_rdunlock(hdr);
  5            
  1            
  0            
  0            
  0            
  0            
  1            
  35            
  0            
  0            
4644              
4645             /* Current shard exhausted — flush deferred work and advance */
4646 42 50         if (h->iterating > 0) h->iterating--;
  5 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 50          
  35 0          
  0 0          
  0            
4647 42           SHM_FN(flush_deferred)(h);
  5            
  1            
  0            
  0            
  0            
  0            
  1            
  35            
  0            
  0            
4648 42           c->shard_idx++;
  5            
  1            
  0            
  0            
  0            
  0            
  1            
  35            
  0            
  0            
4649 42 100         if (c->shard_idx < c->shard_count) {
  5 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 100          
  35 0          
  0 0          
  0            
4650 24           ShmHandle *parent = c->handle;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  21            
  0            
  0            
4651 24           c->current = parent->shard_handles[c->shard_idx];
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  21            
  0            
  0            
4652 24           c->current->iterating++;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  21            
  0            
  0            
4653 24           c->iter_pos = 0;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  21            
  0            
  0            
4654 24           c->gen = c->current->hdr->table_gen;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  21            
  0            
  0            
4655             }
4656             }
4657              
4658 18           return 0;
  2            
  1            
  0            
  0            
  0            
  0            
  1            
  14            
  0            
  0            
4659             }
4660              
4661 2           static inline void SHM_FN(cursor_reset)(ShmCursor *c) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4662             /* Decrement current shard's iterating counter and flush deferred */
4663 2 0         if (c->current && c->current->iterating > 0)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  2 0          
  0 0          
  0 0          
    0          
    0          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
4664 0           c->current->iterating--;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
4665 2           SHM_FN(flush_deferred)(c->current);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4666             /* Reset to first shard */
4667 2           c->shard_idx = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4668 2 0         if (c->handle->shard_handles) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  2 0          
  0 0          
  0            
4669 1           c->current = c->handle->shard_handles[0];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4670             } else {
4671 1           c->current = c->handle;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
4672             }
4673 2           c->current->iterating++;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4674 2           c->iter_pos = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4675 2           c->gen = c->current->hdr->table_gen;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4676 2           }
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
4677              
4678             /* Position cursor at the slot of a specific key. Returns 1 if found, 0 if not.
4679             Next cursor_next call will return this key's entry, then continue forward. */
4680 8           static int SHM_FN(cursor_seek)(ShmCursor *c,
4681             #ifdef SHM_KEY_IS_INT
4682             SHM_KEY_INT_TYPE key
4683             #else
4684             const char *key_str, uint32_t key_len, bool key_utf8
4685             #endif
4686             ) {
4687             /* For sharded maps, route to the correct shard based on key hash */
4688             #ifdef SHM_KEY_IS_INT
4689 5           uint32_t hash = SHM_HASH_KEY(key);
4690             #else
4691 3           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
4692             #endif
4693 8           ShmHandle *parent = c->handle;
4694             ShmHandle *target;
4695 8           uint32_t target_shard = 0;
4696 8           if (parent->shard_handles) {
4697 1           target_shard = hash & parent->shard_mask;
4698 1           target = parent->shard_handles[target_shard];
4699             } else {
4700 7           target = parent;
4701             }
4702              
4703             /* Switch cursor to the target shard */
4704 8           if (target != c->current) {
4705 1           if (c->current && c->current->iterating > 0)
4706 1           c->current->iterating--;
4707 1           SHM_FN(flush_deferred)(c->current);
4708 1           c->current = target;
4709 1           c->current->iterating++;
4710 1           c->shard_idx = target_shard;
4711             }
4712              
4713 8           ShmHandle *h = c->current;
4714 8           ShmHeader *hdr = h->hdr;
4715 8           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
4716 8           uint8_t *states = h->states;
4717 8           uint32_t now = h->expires_at ? shm_now() : 0;
4718              
4719 8           shm_rwlock_rdlock(hdr);
4720              
4721 8           uint32_t mask = hdr->table_cap - 1;
4722 8           uint32_t pos = hash & mask;
4723 8           uint8_t tag = SHM_MAKE_TAG(hash);
4724              
4725 9           for (uint32_t i = 0; i <= mask; i++) {
4726 9           uint32_t idx = (pos + i) & mask;
4727 9           uint8_t st = states[idx];
4728 9           __builtin_prefetch(&nodes[idx], 0, 1);
4729 9           __builtin_prefetch(&nodes[(idx + 1) & mask], 0, 1);
4730 9           if (st == SHM_EMPTY) break;
4731 7           if (st != tag) continue;
4732             #ifdef SHM_KEY_IS_INT
4733 4           if (SHM_KEY_EQ(&nodes[idx], key)) {
4734             #else
4735 2           if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
4736             #endif
4737 6           if (SHM_IS_EXPIRED(h, idx, now)) {
4738 1           shm_rwlock_rdunlock(hdr);
4739 1           return 0;
4740             }
4741 5           c->iter_pos = idx;
4742 5           c->gen = hdr->table_gen;
4743 5           shm_rwlock_rdunlock(hdr);
4744 5           return 1;
4745             }
4746             }
4747              
4748 2           shm_rwlock_rdunlock(hdr);
4749 2           return 0;
4750             }
4751              
4752             /* ---- Undefine template macros ---- */
4753              
4754             #undef SHM_PASTE2
4755             #undef SHM_PASTE
4756             #undef SHM_FN
4757             #undef SHM_NODE_TYPE
4758             #undef SHM_PREFIX
4759             #undef SHM_VARIANT_ID
4760              
4761             #ifdef SHM_KEY_IS_INT
4762             #undef SHM_KEY_IS_INT
4763             #undef SHM_KEY_INT_TYPE
4764             #undef SHM_HASH_KEY
4765             #undef SHM_KEY_EQ
4766             #else
4767             #undef SHM_HASH_KEY_STR
4768             #undef SHM_KEY_EQ_STR
4769             #endif
4770              
4771             #ifdef SHM_VAL_IS_STR
4772             #undef SHM_VAL_IS_STR
4773             #else
4774             #undef SHM_VAL_INT_TYPE
4775             #endif
4776              
4777             #ifdef SHM_HAS_COUNTERS
4778             #undef SHM_HAS_COUNTERS
4779             #endif