File Coverage

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