File Coverage

shm_generic.h
Criterion Covered Total %
statement 7499 15451 48.5
branch 2579 9764 26.4
condition n/a
subroutine n/a
pod n/a
total 10078 25215 39.9


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