File Coverage

shm_generic.h
Criterion Covered Total %
statement 7708 17318 44.5
branch 2668 10962 24.3
condition n/a
subroutine n/a
pod n/a
total 10376 28280 36.6


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