File Coverage

shm_generic.h
Criterion Covered Total %
statement 7227 15200 47.5
branch 2467 9622 25.6
condition n/a
subroutine n/a
pod n/a
total 9694 24822 39.0


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