File Coverage

shm_generic.h
Criterion Covered Total %
statement 7575 17313 43.7
branch 2583 10958 23.5
condition n/a
subroutine n/a
pod n/a
total 10158 28271 35.9


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