File Coverage

shm_generic.h
Criterion Covered Total %
statement 4060 8893 45.6
branch 1361 5488 24.8
condition n/a
subroutine n/a
pod n/a
total 5421 14381 37.7


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             #include
38             #include
39             #include
40             #include
41             #include
42             #include
43              
44             #define XXH_INLINE_ALL
45             #include "xxhash.h"
46              
47             /* ---- Constants ---- */
48              
49             #define SHM_MAGIC 0x53484D31U /* "SHM1" */
50             #define SHM_VERSION 5
51             #define SHM_INITIAL_CAP 16
52             #define SHM_MAX_STR_LEN 0x7FFFFFFFU
53             #define SHM_LRU_NONE UINT32_MAX
54              
55             /* UINT32_MAX = use default TTL; 0 = no TTL; other = per-key TTL */
56             #define SHM_TTL_USE_DEFAULT UINT32_MAX
57              
58             #define SHM_IS_EXPIRED(h, i, now) \
59             ((h)->expires_at && (h)->expires_at[(i)] && \
60             (now) >= (h)->expires_at[(i)])
61              
62             /* Compute expiry timestamp with overflow protection */
63 229           static inline uint32_t shm_expiry_ts(uint32_t ttl) {
64 229           uint64_t sum = (uint64_t)(uint32_t)time(NULL) + ttl;
65 229 50         return (sum > UINT32_MAX) ? UINT32_MAX : (uint32_t)sum;
66             }
67              
68             #define SHM_EMPTY 0
69             #define SHM_LIVE 1
70             #define SHM_TOMBSTONE 2
71              
72             #define SHM_ARENA_NUM_CLASSES 16 /* 2^4..2^19 = 16..524288 */
73             #define SHM_ARENA_MIN_ALLOC 16
74              
75             /* ---- UTF-8 flag packing ---- */
76              
77             #define SHM_UTF8_FLAG ((uint32_t)0x80000000U)
78             #define SHM_LEN_MASK ((uint32_t)SHM_MAX_STR_LEN)
79             #define SHM_PACK_LEN(len, utf8) ((uint32_t)(len) | ((utf8) ? SHM_UTF8_FLAG : 0))
80             #define SHM_UNPACK_LEN(packed) ((uint32_t)((packed) & SHM_LEN_MASK))
81             #define SHM_UNPACK_UTF8(packed) (((packed) & SHM_UTF8_FLAG) != 0)
82              
83             /* ---- Shared memory header (256 bytes, 4 cache lines, in mmap) ---- */
84              
85             #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
86             #define SHM_STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
87             #else
88             #define SHM_STATIC_ASSERT(cond, msg)
89             #endif
90              
91             typedef struct {
92             /* ---- Cache line 0 (0-63): immutable after create ---- */
93             uint32_t magic; /* 0 */
94             uint32_t version; /* 4 */
95             uint32_t variant_id; /* 8 */
96             uint32_t node_size; /* 12 */
97             uint32_t max_table_cap; /* 16 */
98             uint32_t table_cap; /* 20: changes on resize only */
99             uint32_t max_size; /* 24: LRU capacity, 0 = disabled */
100             uint32_t default_ttl; /* 28: TTL seconds, 0 = disabled */
101             uint64_t total_size; /* 32 */
102             uint64_t nodes_off; /* 40 */
103             uint64_t states_off; /* 48 */
104             uint64_t arena_off; /* 56 */
105              
106             /* ---- Cache line 1 (64-127): seqlock + read-path data ---- */
107             uint32_t seq; /* 64: seqlock counter, odd = writer active */
108             uint32_t _pad1; /* 68 */
109             uint64_t arena_cap; /* 72: immutable, read by seqlock string path */
110             uint8_t _reserved1[48]; /* 80-127 */
111              
112             /* ---- Cache line 2 (128-191): rwlock + write-hot fields ---- */
113             uint32_t rwlock; /* 128: 0=unlocked, 1..0x7FFFFFFF=readers, 0x80000000|pid=writer */
114             uint32_t rwlock_waiters; /* 132 */
115             uint32_t size; /* 136 */
116             uint32_t tombstones; /* 140 */
117             uint32_t lru_head; /* 144: MRU slot index */
118             uint32_t lru_tail; /* 148: LRU slot index */
119             uint32_t flush_cursor; /* 152: partial flush_expired scan cursor */
120             uint32_t table_gen; /* 156: incremented on every resize */
121             uint64_t arena_bump; /* 160 */
122             uint64_t stat_evictions; /* 168: cumulative LRU eviction count */
123             uint64_t stat_expired; /* 176: cumulative TTL expiration count */
124             uint32_t stat_recoveries; /* 184: cumulative stale lock recovery count */
125             uint32_t _reserved2; /* 188 */
126              
127             /* ---- Cache line 3 (192-255): arena free lists ---- */
128             uint32_t arena_free[SHM_ARENA_NUM_CLASSES]; /* 192-255 */
129             } ShmHeader;
130              
131             SHM_STATIC_ASSERT(sizeof(ShmHeader) == 256, "ShmHeader must be exactly 256 bytes (4 cache lines)");
132              
133             /* ---- Process-local handle ---- */
134              
135             typedef struct {
136             ShmHeader *hdr;
137             void *nodes;
138             uint8_t *states;
139             char *arena;
140             uint32_t *lru_prev; /* NULL if LRU disabled */
141             uint32_t *lru_next; /* NULL if LRU disabled */
142             uint32_t *expires_at; /* NULL if TTL disabled */
143             size_t mmap_size;
144             uint32_t max_mask; /* max_table_cap - 1, for seqlock bounds clamping */
145             uint32_t iter_pos;
146             char *copy_buf;
147             uint32_t copy_buf_size;
148             uint32_t iterating; /* active iterator count (each + cursors) */
149             uint32_t iter_gen; /* table_gen snapshot for each() */
150             uint8_t iter_active; /* 1 = built-in each is in progress */
151             uint8_t deferred; /* shrink/compact deferred while iterating */
152             char *path; /* backing file path (strdup'd) */
153             } ShmHandle;
154              
155             /* ---- Cursor (independent iterator) ---- */
156              
157             typedef struct {
158             ShmHandle *handle;
159             uint32_t iter_pos;
160             uint32_t gen; /* table_gen snapshot — reset on mismatch */
161             char *copy_buf;
162             uint32_t copy_buf_size;
163             } ShmCursor;
164              
165             /* Grow a copy buffer to hold `needed` bytes; returns 0 on OOM */
166 33           static inline int shm_grow_buf(char **buf, uint32_t *cap, uint32_t needed) {
167 33 100         if (needed == 0) needed = 1;
168 33 100         if (needed <= *cap) return 1;
169 19 50         uint32_t ns = *cap ? *cap : 64;
170 19 50         while (ns < needed) {
171 0           uint32_t next = ns * 2;
172 0 0         if (next <= ns) { ns = needed; break; } /* overflow guard */
173 0           ns = next;
174             }
175 19           char *nb = (char *)realloc(*buf, ns);
176 19 50         if (!nb) return 0;
177 19           *buf = nb;
178 19           *cap = ns;
179 19           return 1;
180             }
181              
182 20           static inline int shm_ensure_copy_buf(ShmHandle *h, uint32_t needed) {
183 20           return shm_grow_buf(&h->copy_buf, &h->copy_buf_size, needed);
184             }
185              
186 13           static inline int shm_cursor_ensure_copy_buf(ShmCursor *c, uint32_t needed) {
187 13           return shm_grow_buf(&c->copy_buf, &c->copy_buf_size, needed);
188             }
189              
190             /* ---- Hash functions (xxHash, XXH3) ---- */
191              
192 9883           static inline uint64_t shm_hash_int64(int64_t key) {
193 9883           return XXH3_64bits(&key, sizeof(key));
194             }
195              
196 143           static inline uint64_t shm_hash_string(const char *data, uint32_t len) {
197 143           return XXH3_64bits(data, (size_t)len);
198             }
199              
200             /* ---- Futex-based read-write lock ---- */
201              
202             #define SHM_RWLOCK_SPIN_LIMIT 32
203             #define SHM_LOCK_TIMEOUT_SEC 2 /* FUTEX_WAIT timeout for stale lock detection */
204              
205 100000           static inline void shm_rwlock_spin_pause(void) {
206             #if defined(__x86_64__) || defined(__i386__)
207 100000           __asm__ volatile("pause" ::: "memory");
208             #elif defined(__aarch64__)
209             __asm__ volatile("yield" ::: "memory");
210             #else
211             __asm__ volatile("" ::: "memory");
212             #endif
213 100000           }
214              
215             /* Extract writer PID from rwlock value (lower 31 bits when write-locked). */
216             #define SHM_RWLOCK_WRITER_BIT 0x80000000U
217             #define SHM_RWLOCK_PID_MASK 0x7FFFFFFFU
218             #define SHM_RWLOCK_WR(pid) (SHM_RWLOCK_WRITER_BIT | ((uint32_t)(pid) & SHM_RWLOCK_PID_MASK))
219              
220             /* Check if a PID is alive. Returns 1 if alive or unknown, 0 if definitely dead. */
221 1           static inline int shm_pid_alive(uint32_t pid) {
222 1 50         if (pid == 0) return 1; /* no owner recorded, assume alive */
223 1 50         return !(kill((pid_t)pid, 0) == -1 && errno == ESRCH);
    50          
224             }
225              
226             /* Force-recover a stale write lock left by a dead process.
227             * Uses CAS on rwlock to atomically verify the dead writer is still the holder. */
228 1           static inline void shm_recover_stale_lock(ShmHeader *hdr, uint32_t observed_rwlock) {
229             /* CAS: only recover if rwlock still holds the same dead writer's value */
230 1 50         if (!__atomic_compare_exchange_n(&hdr->rwlock, &observed_rwlock, 0,
231             0, __ATOMIC_RELEASE, __ATOMIC_RELAXED))
232 0           return; /* lock state changed — another process recovered or writer released */
233             /* Force seqlock to even (readers see consistent state) */
234 1           uint32_t seq = __atomic_load_n(&hdr->seq, __ATOMIC_RELAXED);
235 1 50         if (seq & 1)
236 1           __atomic_store_n(&hdr->seq, seq + 1, __ATOMIC_RELEASE);
237 1           __atomic_add_fetch(&hdr->stat_recoveries, 1, __ATOMIC_RELAXED);
238 1 50         if (__atomic_load_n(&hdr->rwlock_waiters, __ATOMIC_RELAXED) > 0)
239 0           syscall(SYS_futex, &hdr->rwlock, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
240             }
241              
242             static const struct timespec shm_lock_timeout = { SHM_LOCK_TIMEOUT_SEC, 0 };
243              
244 513           static inline void shm_rwlock_rdlock(ShmHeader *hdr) {
245 513           uint32_t *lock = &hdr->rwlock;
246 513           uint32_t *waiters = &hdr->rwlock_waiters;
247 513           for (int spin = 0; ; spin++) {
248 513           uint32_t cur = __atomic_load_n(lock, __ATOMIC_RELAXED);
249             /* Write-preferring: when lock is free (cur==0) and writers are
250             * waiting, yield to let the writer acquire. When readers are
251             * already active (cur>=1), new readers may join freely. */
252 513 50         if (cur > 0 && cur < SHM_RWLOCK_WRITER_BIT) {
    0          
253 0 0         if (__atomic_compare_exchange_n(lock, &cur, cur + 1,
254             1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
255 513           return;
256 513 50         } else if (cur == 0 && !__atomic_load_n(waiters, __ATOMIC_RELAXED)) {
    50          
257 513 50         if (__atomic_compare_exchange_n(lock, &cur, 1,
258             1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
259 513           return;
260             }
261 0 0         if (__builtin_expect(spin < SHM_RWLOCK_SPIN_LIMIT, 1)) {
262 0           shm_rwlock_spin_pause();
263 0           continue;
264             }
265 0           __atomic_add_fetch(waiters, 1, __ATOMIC_RELAXED);
266 0           cur = __atomic_load_n(lock, __ATOMIC_RELAXED);
267             /* Sleep when write-locked OR when yielding to waiting writers */
268 0 0         if (cur >= SHM_RWLOCK_WRITER_BIT || cur == 0) {
    0          
269 0           long rc = syscall(SYS_futex, lock, FUTEX_WAIT, cur,
270             &shm_lock_timeout, NULL, 0);
271 0 0         if (rc == -1 && errno == ETIMEDOUT && cur >= SHM_RWLOCK_WRITER_BIT) {
    0          
    0          
272 0           __atomic_sub_fetch(waiters, 1, __ATOMIC_RELAXED);
273 0           uint32_t val = __atomic_load_n(lock, __ATOMIC_RELAXED);
274 0 0         if (val >= SHM_RWLOCK_WRITER_BIT) {
275 0           uint32_t pid = val & SHM_RWLOCK_PID_MASK;
276 0 0         if (!shm_pid_alive(pid))
277 0           shm_recover_stale_lock(hdr, val);
278             }
279 0           spin = 0;
280 0           continue;
281             }
282             }
283 0           __atomic_sub_fetch(waiters, 1, __ATOMIC_RELAXED);
284 0           spin = 0;
285             }
286             }
287              
288 513           static inline void shm_rwlock_rdunlock(ShmHeader *hdr) {
289 513           uint32_t prev = __atomic_sub_fetch(&hdr->rwlock, 1, __ATOMIC_RELEASE);
290 513 50         if (prev == 0 && __atomic_load_n(&hdr->rwlock_waiters, __ATOMIC_RELAXED) > 0)
    50          
291 0           syscall(SYS_futex, &hdr->rwlock, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
292 513           }
293              
294 4518           static inline void shm_rwlock_wrlock(ShmHeader *hdr) {
295 4518           uint32_t *lock = &hdr->rwlock;
296 4518           uint32_t *waiters = &hdr->rwlock_waiters;
297             /* Encode PID in the rwlock word itself (0x80000000 | pid) to eliminate
298             * any crash window between acquiring the lock and storing the owner. */
299 4518           uint32_t mypid = SHM_RWLOCK_WR((uint32_t)getpid());
300 4518           for (int spin = 0; ; spin++) {
301 4518           uint32_t expected = 0;
302 4518 50         if (__atomic_compare_exchange_n(lock, &expected, mypid,
303             1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
304 4518           return;
305 0 0         if (__builtin_expect(spin < SHM_RWLOCK_SPIN_LIMIT, 1)) {
306 0           shm_rwlock_spin_pause();
307 0           continue;
308             }
309 0           __atomic_add_fetch(waiters, 1, __ATOMIC_RELAXED);
310 0           uint32_t cur = __atomic_load_n(lock, __ATOMIC_RELAXED);
311 0 0         if (cur != 0) {
312 0           long rc = syscall(SYS_futex, lock, FUTEX_WAIT, cur,
313             &shm_lock_timeout, NULL, 0);
314 0 0         if (rc == -1 && errno == ETIMEDOUT) {
    0          
315 0           __atomic_sub_fetch(waiters, 1, __ATOMIC_RELAXED);
316 0           uint32_t val = __atomic_load_n(lock, __ATOMIC_RELAXED);
317 0 0         if (val >= SHM_RWLOCK_WRITER_BIT) {
318 0           uint32_t pid = val & SHM_RWLOCK_PID_MASK;
319 0 0         if (!shm_pid_alive(pid))
320 0           shm_recover_stale_lock(hdr, val);
321             }
322 0           spin = 0;
323 0           continue;
324             }
325             }
326 0           __atomic_sub_fetch(waiters, 1, __ATOMIC_RELAXED);
327 0           spin = 0;
328             }
329             }
330              
331 4518           static inline void shm_rwlock_wrunlock(ShmHeader *hdr) {
332 4518           __atomic_store_n(&hdr->rwlock, 0, __ATOMIC_RELEASE);
333 4518 50         if (__atomic_load_n(&hdr->rwlock_waiters, __ATOMIC_RELAXED) > 0)
334 0           syscall(SYS_futex, &hdr->rwlock, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
335 4518           }
336              
337             /* ---- Seqlock (lock-free readers) ---- */
338              
339 203           static inline uint32_t shm_seqlock_read_begin(ShmHeader *hdr) {
340 203           int spin = 0;
341 100001           for (;;) {
342 100204           uint32_t s = __atomic_load_n(&hdr->seq, __ATOMIC_ACQUIRE);
343 100204 100         if (__builtin_expect((s & 1) == 0, 1)) return s;
344 100001 100         if (__builtin_expect(spin < 100000, 1)) {
345 100000           shm_rwlock_spin_pause();
346 100000           spin++;
347 100001           continue;
348             }
349             /* Prolonged odd seq — check for dead writer */
350 1           uint32_t val = __atomic_load_n(&hdr->rwlock, __ATOMIC_RELAXED);
351 1 50         if (val >= SHM_RWLOCK_WRITER_BIT) {
352 1           uint32_t pid = val & SHM_RWLOCK_PID_MASK;
353 1 50         if (!shm_pid_alive(pid)) {
354 1           shm_recover_stale_lock(hdr, val);
355 1           spin = 0;
356 1           continue;
357             }
358             }
359             /* Writer is alive, yield CPU */
360 0           struct timespec ts = {0, 1000000}; /* 1ms */
361 0           nanosleep(&ts, NULL);
362 0           spin = 0;
363             }
364             }
365              
366 196           static inline int shm_seqlock_read_retry(uint32_t *seq, uint32_t start) {
367 196           __atomic_thread_fence(__ATOMIC_ACQUIRE); /* ensure data loads complete before retry check */
368 196           return __atomic_load_n(seq, __ATOMIC_RELAXED) != start;
369             }
370              
371 4508           static inline void shm_seqlock_write_begin(uint32_t *seq) {
372 4508           __atomic_add_fetch(seq, 1, __ATOMIC_RELEASE); /* seq becomes odd */
373 4508           }
374              
375 4508           static inline void shm_seqlock_write_end(uint32_t *seq) {
376 4508           __atomic_add_fetch(seq, 1, __ATOMIC_RELEASE); /* seq becomes even */
377 4508           }
378              
379             /* ---- Arena allocator ---- */
380              
381             static inline uint32_t shm_next_pow2(uint32_t v);
382              
383 159           static inline uint32_t shm_arena_round_up(uint32_t len) {
384 159 50         if (len < SHM_ARENA_MIN_ALLOC) return SHM_ARENA_MIN_ALLOC;
385 0           return shm_next_pow2(len);
386             }
387              
388 159           static inline int shm_arena_class_index(uint32_t alloc_size) {
389 159 50         if (alloc_size <= SHM_ARENA_MIN_ALLOC) return 0;
390 0 0         if (alloc_size > (SHM_ARENA_MIN_ALLOC << (SHM_ARENA_NUM_CLASSES - 1))) return -1;
391 0           return 32 - __builtin_clz(alloc_size - 1) - 4; /* log2(alloc_size) - 4 */
392             }
393              
394 134           static inline uint32_t shm_arena_alloc(ShmHeader *hdr, char *arena, uint32_t len) {
395 134           uint32_t asize = shm_arena_round_up(len);
396 134           int cls = shm_arena_class_index(asize);
397              
398 134 50         if (cls >= 0 && hdr->arena_free[cls] != 0) {
    100          
399 9           uint32_t head = hdr->arena_free[cls];
400             uint32_t next;
401 9           memcpy(&next, arena + head, sizeof(uint32_t));
402 9           hdr->arena_free[cls] = next;
403 9           return head;
404             }
405              
406 125           uint64_t off = hdr->arena_bump;
407 125 50         if (off + asize > hdr->arena_cap || off + asize > (uint64_t)UINT32_MAX)
    50          
408 0           return 0;
409 125           hdr->arena_bump = off + asize;
410 125           return (uint32_t)off;
411             }
412              
413 25           static inline void shm_arena_free_block(ShmHeader *hdr, char *arena,
414             uint32_t off, uint32_t len) {
415 25           uint32_t asize = shm_arena_round_up(len);
416 25           int cls = shm_arena_class_index(asize);
417 25 50         if (cls < 0 || off == 0) return;
    50          
418              
419 25           uint32_t old_head = hdr->arena_free[cls];
420 25           memcpy(arena + off, &old_head, sizeof(uint32_t));
421 25           hdr->arena_free[cls] = off;
422             }
423              
424             /* ---- Utility ---- */
425              
426 133           static inline uint32_t shm_next_pow2(uint32_t v) {
427 133 50         if (v < 2) return 2;
428 133 50         if (v > 0x80000000U) return 0; /* overflow: no valid power of 2 */
429 133           v--;
430 133           v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16;
431 133           return v + 1;
432             }
433              
434             /* ---- LRU helpers ---- */
435              
436 21           static inline void shm_lru_unlink(ShmHandle *h, uint32_t idx) {
437 21           uint32_t *prev = h->lru_prev;
438 21           uint32_t *next = h->lru_next;
439 21           ShmHeader *hdr = h->hdr;
440 21           uint32_t p = prev[idx], n = next[idx];
441 21 50         if (p != SHM_LRU_NONE) next[p] = n;
442 0           else hdr->lru_head = n;
443 21 100         if (n != SHM_LRU_NONE) prev[n] = p;
444 18           else hdr->lru_tail = p;
445 21           prev[idx] = next[idx] = SHM_LRU_NONE;
446 21           }
447              
448 74           static inline void shm_lru_push_front(ShmHandle *h, uint32_t idx) {
449 74           uint32_t *prev = h->lru_prev;
450 74           uint32_t *next = h->lru_next;
451 74           ShmHeader *hdr = h->hdr;
452 74           prev[idx] = SHM_LRU_NONE;
453 74           next[idx] = hdr->lru_head;
454 74 100         if (hdr->lru_head != SHM_LRU_NONE) prev[hdr->lru_head] = idx;
455 16           else hdr->lru_tail = idx;
456 74           hdr->lru_head = idx;
457 74           }
458              
459 16           static inline void shm_lru_promote(ShmHandle *h, uint32_t idx) {
460 16 100         if (h->hdr->lru_head == idx) return;
461 6           shm_lru_unlink(h, idx);
462 6           shm_lru_push_front(h, idx);
463             }
464              
465             /* ---- Create / Open / Close ---- */
466              
467             /* Error buffer for shm_create_map diagnostics */
468             #define SHM_ERR_BUFLEN 256
469              
470 129           static ShmHandle *shm_create_map(const char *path, uint32_t max_entries,
471             uint32_t node_size, uint32_t variant_id,
472             int has_arena, uint32_t max_size,
473             uint32_t default_ttl,
474             char *errbuf) {
475 129 50         if (errbuf) errbuf[0] = '\0';
476 129           uint32_t max_tcap = shm_next_pow2((uint32_t)((uint64_t)max_entries * 4 / 3 + 1));
477 129 100         if (max_tcap < SHM_INITIAL_CAP) max_tcap = SHM_INITIAL_CAP;
478              
479 129           int has_lru = (max_size > 0);
480 129           int has_ttl = (default_ttl > 0);
481              
482 129           uint64_t nodes_off = sizeof(ShmHeader);
483 129           uint64_t states_off = nodes_off + (uint64_t)max_tcap * node_size;
484 129           uint64_t next_off = states_off + max_tcap;
485              
486             /* LRU arrays (between states and arena) */
487 129           uint64_t lru_prev_off = 0, lru_next_off = 0, expires_off = 0;
488 129 100         if (has_lru) {
489 16           next_off = (next_off + 3) & ~(uint64_t)3;
490 16           lru_prev_off = next_off;
491 16           next_off += (uint64_t)max_tcap * sizeof(uint32_t);
492 16           lru_next_off = next_off;
493 16           next_off += (uint64_t)max_tcap * sizeof(uint32_t);
494             }
495 129 100         if (has_ttl) {
496 34           next_off = (next_off + 3) & ~(uint64_t)3;
497 34           expires_off = next_off;
498 34           next_off += (uint64_t)max_tcap * sizeof(uint32_t);
499             }
500              
501 129           uint64_t arena_off = 0, arena_cap = 0;
502 129 100         if (has_arena) {
503 42           arena_off = (next_off + 7) & ~(uint64_t)7;
504 42           arena_cap = (uint64_t)max_entries * 128;
505 42 50         if (arena_cap < 4096) arena_cap = 4096;
506             }
507              
508 129 100         uint64_t total_size = has_arena ? arena_off + arena_cap : next_off;
509              
510             #define SHM_ERR(fmt, ...) do { if (errbuf) snprintf(errbuf, SHM_ERR_BUFLEN, fmt, ##__VA_ARGS__); } while(0)
511              
512 129           int fd = open(path, O_RDWR | O_CREAT, 0666);
513 129 100         if (fd < 0) { SHM_ERR("open(%s): %s", path, strerror(errno)); return NULL; }
    50          
514              
515 128 50         if (flock(fd, LOCK_EX) < 0) { SHM_ERR("flock(%s): %s", path, strerror(errno)); close(fd); return NULL; }
    0          
516              
517             struct stat st;
518 128 50         if (fstat(fd, &st) < 0) { SHM_ERR("fstat(%s): %s", path, strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; }
    0          
519              
520 128           int is_new = (st.st_size == 0);
521              
522 128 100         if (!is_new && (uint64_t)st.st_size < sizeof(ShmHeader)) {
    50          
523 0 0         SHM_ERR("%s: file too small (%lld bytes, need %zu)", path,
524             (long long)st.st_size, sizeof(ShmHeader));
525 0           flock(fd, LOCK_UN); close(fd); return NULL;
526             }
527              
528 128 100         if (is_new) {
529 124 50         if (ftruncate(fd, (off_t)total_size) < 0) {
530 0 0         SHM_ERR("ftruncate(%s, %llu): %s", path, (unsigned long long)total_size, strerror(errno));
531 0           flock(fd, LOCK_UN); close(fd); return NULL;
532             }
533             }
534              
535 128 100         void *base = mmap(NULL, is_new ? total_size : (size_t)st.st_size,
536             PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
537 128 50         if (base == MAP_FAILED) { SHM_ERR("mmap(%s): %s", path, strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; }
    0          
538              
539 128           ShmHeader *hdr = (ShmHeader *)base;
540              
541 128 100         if (is_new) {
542 124           memset(hdr, 0, sizeof(ShmHeader));
543 124           hdr->magic = SHM_MAGIC;
544 124           hdr->version = SHM_VERSION;
545 124           hdr->variant_id = variant_id;
546 124           hdr->node_size = node_size;
547 124           hdr->max_table_cap = max_tcap;
548 124           hdr->table_cap = SHM_INITIAL_CAP;
549 124           hdr->total_size = total_size;
550 124           hdr->nodes_off = nodes_off;
551 124           hdr->states_off = states_off;
552 124 100         hdr->arena_off = has_arena ? arena_off : 0;
553 124           hdr->arena_cap = arena_cap;
554 124           hdr->arena_bump = SHM_ARENA_MIN_ALLOC; /* reserve offset 0 */
555 124           hdr->max_size = max_size;
556 124           hdr->default_ttl = default_ttl;
557 124           hdr->lru_head = SHM_LRU_NONE;
558 124           hdr->lru_tail = SHM_LRU_NONE;
559              
560             /* init LRU arrays (full max_tcap, not just initial cap) */
561 124 100         if (has_lru) {
562 15           memset((char *)base + lru_prev_off, 0xFF,
563             max_tcap * sizeof(uint32_t));
564 15           memset((char *)base + lru_next_off, 0xFF,
565             max_tcap * sizeof(uint32_t));
566             }
567 124 100         if (has_ttl) {
568 33           memset((char *)base + expires_off, 0,
569             max_tcap * sizeof(uint32_t));
570             }
571              
572 124           __atomic_thread_fence(__ATOMIC_SEQ_CST);
573             } else {
574 12           int valid = (hdr->magic == SHM_MAGIC &&
575 4 50         hdr->version == SHM_VERSION &&
576 4 100         hdr->variant_id == variant_id &&
577 2 50         hdr->node_size == node_size &&
578 2 50         hdr->total_size == (uint64_t)st.st_size &&
579 2 50         hdr->nodes_off >= sizeof(ShmHeader) &&
580 2 50         hdr->states_off > hdr->nodes_off &&
581 2 50         hdr->states_off < hdr->total_size &&
582 2 50         (!hdr->arena_off || (hdr->arena_off < hdr->total_size &&
    0          
583 0 0         hdr->arena_off + hdr->arena_cap <= hdr->total_size &&
584 0 0         hdr->arena_bump <= hdr->arena_cap &&
585 0 0         hdr->arena_bump >= SHM_ARENA_MIN_ALLOC)) &&
586 2 50         hdr->max_table_cap > 0 &&
587 2 50         (hdr->max_table_cap & (hdr->max_table_cap - 1)) == 0 &&
588 2 50         hdr->table_cap > 0 &&
589 2 50         (hdr->table_cap & (hdr->table_cap - 1)) == 0 &&
590 2 50         hdr->table_cap <= hdr->max_table_cap &&
591 2 50         hdr->states_off + hdr->max_table_cap <= hdr->total_size &&
592 10 50         hdr->nodes_off + (uint64_t)hdr->max_table_cap * hdr->node_size <= hdr->states_off &&
    50          
593 2 100         (!hdr->max_size ||
594 1 50         ((hdr->lru_head == SHM_LRU_NONE || hdr->lru_head < hdr->max_table_cap) &&
    50          
595 1 50         (hdr->lru_tail == SHM_LRU_NONE || hdr->lru_tail < hdr->max_table_cap))));
    50          
596 4 100         if (!valid) {
597 2 50         if (hdr->magic != SHM_MAGIC)
598 0 0         SHM_ERR("%s: bad magic (not a HashMap::Shared file)", path);
599 2 50         else if (hdr->version != SHM_VERSION)
600 0 0         SHM_ERR("%s: version mismatch (file=%u, expected=%u)", path, hdr->version, SHM_VERSION);
601 2 50         else if (hdr->variant_id != variant_id)
602 2 50         SHM_ERR("%s: variant mismatch (file=%u, expected=%u)", path, hdr->variant_id, variant_id);
603             else
604 0 0         SHM_ERR("%s: corrupt header", path);
605 2           munmap(base, (size_t)st.st_size);
606 2           flock(fd, LOCK_UN); close(fd);
607 2           return NULL;
608             }
609 2           total_size = (uint64_t)st.st_size;
610              
611             /* Recompute LRU/TTL offsets from header fields */
612 2           has_lru = (hdr->max_size > 0);
613 2           has_ttl = (hdr->default_ttl > 0);
614 2           next_off = hdr->states_off + hdr->max_table_cap;
615 2 100         if (has_lru) {
616 1           next_off = (next_off + 3) & ~(uint64_t)3;
617 1           lru_prev_off = next_off;
618 1           next_off += (uint64_t)hdr->max_table_cap * sizeof(uint32_t);
619 1           lru_next_off = next_off;
620 1           next_off += (uint64_t)hdr->max_table_cap * sizeof(uint32_t);
621             }
622 2 100         if (has_ttl) {
623 1           next_off = (next_off + 3) & ~(uint64_t)3;
624 1           expires_off = next_off;
625 1           next_off += (uint64_t)hdr->max_table_cap * sizeof(uint32_t);
626             }
627 2 50         if (next_off > total_size) {
628 0 0         SHM_ERR("%s: file too small for LRU/TTL arrays", path);
629 0           munmap(base, (size_t)st.st_size);
630 0           flock(fd, LOCK_UN); close(fd);
631 0           return NULL;
632             }
633             }
634              
635 126           flock(fd, LOCK_UN);
636 126           close(fd);
637              
638 126           ShmHandle *h = (ShmHandle *)calloc(1, sizeof(ShmHandle));
639 126 50         if (!h) { SHM_ERR("calloc: out of memory"); munmap(base, (size_t)total_size); return NULL; }
    0          
640              
641 126           h->hdr = hdr;
642 126           h->nodes = (char *)hdr + hdr->nodes_off;
643 126           h->states = (uint8_t *)((char *)hdr + hdr->states_off);
644 126 100         h->arena = hdr->arena_off ? (char *)hdr + hdr->arena_off : NULL;
645 126 100         h->lru_prev = has_lru ? (uint32_t *)((char *)hdr + lru_prev_off) : NULL;
646 126 100         h->lru_next = has_lru ? (uint32_t *)((char *)hdr + lru_next_off) : NULL;
647 126 100         h->expires_at = has_ttl ? (uint32_t *)((char *)hdr + expires_off) : NULL;
648 126           h->mmap_size = (size_t)total_size;
649 126           h->max_mask = hdr->max_table_cap - 1;
650 126           h->iter_pos = 0;
651 126           h->path = strdup(path);
652 126 50         if (!h->path) { SHM_ERR("strdup: out of memory"); munmap(base, (size_t)total_size); free(h); return NULL; }
    0          
653              
654             #undef SHM_ERR
655 126           return h;
656             }
657              
658 126           static void shm_close_map(ShmHandle *h) {
659 126 50         if (!h) return;
660 126 50         if (h->hdr) munmap(h->hdr, h->mmap_size);
661 126           free(h->copy_buf);
662 126           free(h->path);
663 126           free(h);
664             }
665              
666             /* Unlink the backing file. Returns 1 on success, 0 on failure. */
667 3           static int shm_unlink_path(const char *path) {
668 3           return (unlink(path) == 0) ? 1 : 0;
669             }
670              
671 16           static inline ShmCursor *shm_cursor_create(ShmHandle *h) {
672 16           ShmCursor *c = (ShmCursor *)calloc(1, sizeof(ShmCursor));
673 16 50         if (!c) return NULL;
674 16           c->handle = h;
675 16           c->gen = h->hdr->table_gen;
676 16           h->iterating++;
677 16           return c;
678             }
679              
680 16           static inline void shm_cursor_destroy(ShmCursor *c) {
681 16 50         if (!c) return;
682 16           ShmHandle *h = c->handle;
683 16 50         if (h && h->iterating > 0)
    50          
684 16           h->iterating--;
685 16           free(c->copy_buf);
686 16           free(c);
687             }
688              
689             #endif /* SHM_DEFS_H */
690              
691              
692             /* ================================================================
693             * Part 2: Template (included per variant)
694             * ================================================================ */
695              
696             #define SHM_PASTE2(a, b) a##_##b
697             #define SHM_PASTE(a, b) SHM_PASTE2(a, b)
698             #define SHM_FN(name) SHM_PASTE(SHM_PREFIX, name)
699              
700             /* ---- Node struct ---- */
701              
702             typedef struct {
703             #ifdef SHM_KEY_IS_INT
704             SHM_KEY_INT_TYPE key;
705             #else
706             uint32_t key_off;
707             uint32_t key_len; /* high bit = UTF-8 */
708             #endif
709             #ifdef SHM_VAL_IS_STR
710             uint32_t val_off;
711             uint32_t val_len; /* high bit = UTF-8 */
712             #else
713             SHM_VAL_INT_TYPE value;
714             #endif
715             } SHM_NODE_TYPE;
716              
717             /* ---- Key hashing ---- */
718              
719             #ifdef SHM_KEY_IS_INT
720             #define SHM_HASH_KEY(k) ((uint32_t)(shm_hash_int64((int64_t)(k))))
721             #define SHM_KEY_EQ(node_ptr, k) ((node_ptr)->key == (k))
722             #else
723             #define SHM_HASH_KEY_STR(str, len) ((uint32_t)shm_hash_string((str), (len)))
724             #define SHM_KEY_EQ_STR(node_ptr, arena, str, len, utf8) \
725             (SHM_UNPACK_LEN((node_ptr)->key_len) == (len) && \
726             SHM_UNPACK_UTF8((node_ptr)->key_len) == (utf8) && \
727             memcmp((arena) + (node_ptr)->key_off, (str), (len)) == 0)
728             #endif
729              
730             /* ---- Create / Open ---- */
731              
732 129           static ShmHandle *SHM_FN(create)(const char *path, uint32_t max_entries,
  24            
  10            
  1            
  2            
  2            
  1            
  2            
  82            
  3            
  2            
733             uint32_t max_size, uint32_t default_ttl,
734             char *errbuf) {
735 129           int has_arena = 0;
  24            
  10            
  1            
  2            
  2            
  1            
  2            
  82            
  3            
  2            
736             #ifndef SHM_KEY_IS_INT
737 37           has_arena = 1;
  24            
  10            
  1            
  2            
738             #endif
739             #ifdef SHM_VAL_IS_STR
740 29           has_arena = 1;
  24            
  2            
  1            
  2            
741             #endif
742 129           return shm_create_map(path, max_entries,
  24            
  10            
  1            
  2            
  2            
  1            
  2            
  82            
  3            
  2            
743             (uint32_t)sizeof(SHM_NODE_TYPE),
744             SHM_VARIANT_ID, has_arena,
745             max_size, default_ttl, errbuf);
746             }
747              
748             /* ---- Rehash helper (used during resize) — returns new index ---- */
749              
750 5293           static uint32_t SHM_FN(rehash_insert_raw)(ShmHandle *h, SHM_NODE_TYPE *node) {
  0            
  0            
  13            
  0            
  0            
  0            
  0            
  3238            
  2042            
  0            
751 5293           ShmHeader *hdr = h->hdr;
  0            
  0            
  13            
  0            
  0            
  0            
  0            
  3238            
  2042            
  0            
752 5293           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  0            
  0            
  13            
  0            
  0            
  0            
  0            
  3238            
  2042            
  0            
753 5293           uint8_t *states = h->states;
  0            
  0            
  13            
  0            
  0            
  0            
  0            
  3238            
  2042            
  0            
754 5293           uint32_t mask = hdr->table_cap - 1;
  0            
  0            
  13            
  0            
  0            
  0            
  0            
  3238            
  2042            
  0            
755              
756             #ifdef SHM_KEY_IS_INT
757 5280           uint32_t hash = SHM_HASH_KEY(node->key);
  0            
  0            
  0            
  3238            
  2042            
  0            
758             #else
759 13           uint32_t klen = SHM_UNPACK_LEN(node->key_len);
  0            
  0            
  13            
  0            
760 13           uint32_t hash = shm_hash_string(h->arena + node->key_off, klen);
  0            
  0            
  13            
  0            
761             #endif
762              
763 5293           uint32_t pos = hash & mask;
  0            
  0            
  13            
  0            
  0            
  0            
  0            
  3238            
  2042            
  0            
764 6890 0         while (states[pos] == SHM_LIVE)
  0 0          
  0 100          
  15 0          
  0 0          
  0 0          
  0 0          
  0 100          
  4069 100          
  2806 0          
  0            
765 1597           pos = (pos + 1) & mask;
  0            
  0            
  2            
  0            
  0            
  0            
  0            
  831            
  764            
  0            
766              
767 5293           nodes[pos] = *node;
  0            
  0            
  13            
  0            
  0            
  0            
  0            
  3238            
  2042            
  0            
768 5293           states[pos] = SHM_LIVE;
  0            
  0            
  13            
  0            
  0            
  0            
  0            
  3238            
  2042            
  0            
769 5293           return pos;
  0            
  0            
  13            
  0            
  0            
  0            
  0            
  3238            
  2042            
  0            
770             }
771              
772             /* ---- Tombstone at index (helper for eviction/expiry) ---- */
773              
774 983           static void SHM_FN(tombstone_at)(ShmHandle *h, uint32_t idx) {
  9            
  3            
  0            
  1            
  1            
  0            
  1            
  467            
  500            
  1            
775 983           ShmHeader *hdr = h->hdr;
  9            
  3            
  0            
  1            
  1            
  0            
  1            
  467            
  500            
  1            
776 983           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  9            
  3            
  0            
  1            
  1            
  0            
  1            
  467            
  500            
  1            
777             #ifndef SHM_KEY_IS_INT
778 13           shm_arena_free_block(hdr, h->arena,
  9            
  3            
  0            
  1            
779 13           nodes[idx].key_off, SHM_UNPACK_LEN(nodes[idx].key_len));
  9            
  3            
  0            
  1            
780             #endif
781             #ifdef SHM_VAL_IS_STR
782 11           shm_arena_free_block(hdr, h->arena,
  9            
  1            
  0            
  1            
783 11           nodes[idx].val_off, SHM_UNPACK_LEN(nodes[idx].val_len));
  9            
  1            
  0            
  1            
784             #endif
785 983           h->states[idx] = SHM_TOMBSTONE;
  9            
  3            
  0            
  1            
  1            
  0            
  1            
  467            
  500            
  1            
786 983           hdr->size--;
  9            
  3            
  0            
  1            
  1            
  0            
  1            
  467            
  500            
  1            
787 983           hdr->tombstones++;
  9            
  3            
  0            
  1            
  1            
  0            
  1            
  467            
  500            
  1            
788 983           }
  9            
  3            
  0            
  1            
  1            
  0            
  1            
  467            
  500            
  1            
789              
790             /* ---- LRU eviction ---- */
791              
792 13           static void SHM_FN(lru_evict_one)(ShmHandle *h) {
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
793 13           uint32_t victim = h->hdr->lru_tail;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
794 13 50         if (victim == SHM_LRU_NONE) return;
  2 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  10 0          
  0 0          
  0            
795 13           shm_lru_unlink(h, victim);
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
796 13 50         if (h->expires_at) h->expires_at[victim] = 0;
  2 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  10 0          
  0 0          
  0            
797 13           SHM_FN(tombstone_at)(h, victim);
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
798 13           h->hdr->stat_evictions++;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
799             }
800              
801             /* ---- TTL expiration ---- */
802              
803 179           static void SHM_FN(expire_at)(ShmHandle *h, uint32_t idx) {
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  176            
  0            
  0            
804 179 50         if (h->lru_prev) shm_lru_unlink(h, idx);
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  176 0          
  0 0          
  0            
805 179           h->expires_at[idx] = 0;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  176            
  0            
  0            
806 179           SHM_FN(tombstone_at)(h, idx);
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  176            
  0            
  0            
807 179           h->hdr->stat_expired++;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  176            
  0            
  0            
808 179           }
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  176            
  0            
  0            
809              
810             /* ---- Resize (elastic grow/shrink) ---- */
811              
812 59           static int SHM_FN(resize)(ShmHandle *h, uint32_t new_cap) {
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
813 59           ShmHeader *hdr = h->hdr;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
814 59           uint32_t old_cap = hdr->table_cap;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
815 59           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
816 59           uint8_t *states = h->states;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
817              
818 59           uint32_t live = hdr->size;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
819 59           SHM_NODE_TYPE *saved = NULL;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
820 59           uint32_t *saved_indices = NULL;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
821 59           uint32_t *old_to_new = NULL;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
822 59           uint32_t *saved_exp = NULL;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
823              
824             /* Save LRU order (tail-to-head) */
825 59           uint32_t *lru_order = NULL;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
826 59           uint32_t lru_count = 0;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
827 59 50         int need_mapping = (h->lru_prev || h->expires_at);
  2 50          
  0 0          
  1 0          
  0 50          
  0 50          
  0 0          
  0 0          
  48 0          
  8 0          
  0 0          
    0          
    0          
    0          
    50          
    100          
    50          
    50          
    0          
    0          
828              
829 59 50         if (live > 0) {
  2 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  48 50          
  8 0          
  0            
830 51           saved = (SHM_NODE_TYPE *)malloc((size_t)live * sizeof(SHM_NODE_TYPE));
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  42            
  8            
  0            
831 51 0         if (!saved) return 0;
  0 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  42 50          
  8 0          
  0            
832              
833 51 0         if (need_mapping) {
  0 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  42 50          
  8 0          
  0            
834 8           saved_indices = (uint32_t *)malloc((size_t)live * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
835 8           old_to_new = (uint32_t *)malloc((size_t)old_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
836 8 0         if (!saved_indices || !old_to_new) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  8 0          
  0 0          
  0 0          
    0          
    0          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
837 0           free(saved); free(saved_indices); free(old_to_new);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
838 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
839             }
840 8           memset(old_to_new, 0xFF, old_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
841             }
842              
843 51 0         if (h->lru_prev) {
  0 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  42 50          
  8 0          
  0            
844 0           lru_order = (uint32_t *)malloc((size_t)live * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
845 0 0         if (!lru_order) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
846 0           free(saved); free(saved_indices); free(old_to_new);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
847 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
848             }
849 0           uint32_t idx = hdr->lru_tail;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
850 0 0         while (idx != SHM_LRU_NONE && lru_count < live) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
851 0           lru_order[lru_count++] = idx;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
852 0           idx = h->lru_prev[idx];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
853             }
854             }
855              
856 51 0         if (h->expires_at) {
  0 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  42 50          
  8 0          
  0            
857 8           saved_exp = (uint32_t *)malloc(old_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
858 8 0         if (!saved_exp) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  8 0          
  0 0          
  0            
859 0           free(saved); free(saved_indices); free(old_to_new); free(lru_order);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
860 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
861             }
862 8           memcpy(saved_exp, h->expires_at, old_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
863             }
864              
865 51           uint32_t j = 0;
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  42            
  8            
  0            
866 9315 0         for (uint32_t i = 0; i < old_cap && j < live; i++) {
  0 0          
  0 0          
  17 0          
  0 100          
  0 50          
  0 0          
  0 0          
  5210 0          
  4088 0          
  0 0          
    0          
    0          
    0          
    100          
    50          
    100          
    50          
    0          
    0          
867 9264 0         if (states[i] == SHM_LIVE) {
  0 0          
  0 100          
  16 0          
  0 0          
  0 0          
  0 0          
  0 100          
  5168 100          
  4080 0          
  0            
868 5293           saved[j] = nodes[i];
  0            
  0            
  13            
  0            
  0            
  0            
  0            
  3238            
  2042            
  0            
869 5293 0         if (saved_indices) saved_indices[j] = i;
  0 0          
  0 50          
  13 0          
  0 0          
  0 0          
  0 0          
  0 100          
  3238 50          
  2042 0          
  0            
870 5293           j++;
  0            
  0            
  13            
  0            
  0            
  0            
  0            
  3238            
  2042            
  0            
871             }
872             }
873 51           live = j;
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  42            
  8            
  0            
874             }
875              
876 59           memset(states, SHM_EMPTY, new_cap);
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
877 59           hdr->table_cap = new_cap;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
878 59           hdr->tombstones = 0;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
879              
880             /* Reset LRU arrays */
881 59 50         if (h->lru_prev) {
  2 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  48 50          
  8 0          
  0            
882 0           memset(h->lru_prev, 0xFF, new_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
883 0           memset(h->lru_next, 0xFF, new_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
884 0           hdr->lru_head = SHM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
885 0           hdr->lru_tail = SHM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
886             }
887 59 50         if (h->expires_at) {
  2 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  48 50          
  8 0          
  0            
888 11           memset(h->expires_at, 0, new_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  11            
  0            
  0            
889             }
890              
891 5352 50         for (uint32_t k = 0; k < live; k++) {
  2 0          
  0 100          
  14 0          
  0 0          
  0 0          
  0 0          
  0 100          
  3286 100          
  2050 0          
  0            
892 5293           uint32_t new_idx = SHM_FN(rehash_insert_raw)(h, &saved[k]);
  0            
  0            
  13            
  0            
  0            
  0            
  0            
  3238            
  2042            
  0            
893 5293 0         if (old_to_new) old_to_new[saved_indices[k]] = new_idx;
  0 0          
  0 50          
  13 0          
  0 0          
  0 0          
  0 0          
  0 100          
  3238 50          
  2042 0          
  0            
894             }
895              
896             /* Rebuild LRU chain in original order (lru_order[0]=tail/LRU, last=head/MRU).
897             * Push front from LRU to MRU so the last push (MRU) ends up at head. */
898 59 50         if (h->lru_prev && lru_order) {
  2 0          
  0 0          
  1 0          
  0 50          
  0 0          
  0 0          
  0 0          
  48 0          
  8 0          
  0 0          
    0          
    0          
    0          
    50          
    0          
    50          
    0          
    0          
    0          
899 0 0         for (uint32_t i = 0; i < lru_count; i++) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
900 0           uint32_t new_idx = old_to_new[lru_order[i]];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
901 0 0         if (new_idx != SHM_LRU_NONE)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
902 0           shm_lru_push_front(h, new_idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
903             }
904             }
905              
906             /* Restore expires_at */
907 59 50         if (h->expires_at && saved_exp) {
  2 0          
  0 0          
  1 0          
  0 50          
  0 0          
  0 0          
  0 0          
  48 0          
  8 0          
  0 0          
    0          
    0          
    0          
    100          
    100          
    50          
    0          
    0          
    0          
908 292 0         for (uint32_t k = 0; k < live; k++) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  292 0          
  0 0          
  0            
909 284           uint32_t new_idx = old_to_new[saved_indices[k]];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  284            
  0            
  0            
910 284 0         if (new_idx != SHM_LRU_NONE)
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  284 0          
  0 0          
  0            
911 284           h->expires_at[new_idx] = saved_exp[saved_indices[k]];
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  284            
  0            
  0            
912             }
913             }
914              
915 59 50         if (new_cap < old_cap) {
  2 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  48 100          
  8 0          
  0            
916 5           size_t node_shrink = (size_t)(old_cap - new_cap) * sizeof(SHM_NODE_TYPE);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  1            
  0            
917 5           madvise((char *)nodes + (size_t)new_cap * sizeof(SHM_NODE_TYPE),
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  1            
  0            
918             node_shrink, MADV_DONTNEED);
919 5           madvise(states + new_cap, old_cap - new_cap, MADV_DONTNEED);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  1            
  0            
920             }
921              
922 59           hdr->table_gen++;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
923              
924 59           free(saved);
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
925 59           free(saved_indices);
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
926 59           free(old_to_new);
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
927 59           free(lru_order);
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
928 59           free(saved_exp);
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
929 59           return 1;
  2            
  0            
  1            
  0            
  0            
  0            
  0            
  48            
  8            
  0            
930             }
931              
932 3649           static inline void SHM_FN(maybe_grow)(ShmHandle *h) {
  39            
  17            
  14            
  8            
  5            
  13            
  6            
  2517            
  1017            
  13            
933 3649           ShmHeader *hdr = h->hdr;
  39            
  17            
  14            
  8            
  5            
  13            
  6            
  2517            
  1017            
  13            
934 3649           uint32_t size = hdr->size, tomb = hdr->tombstones, cap = hdr->table_cap;
  39            
  17            
  14            
  8            
  5            
  13            
  6            
  2517            
  1017            
  13            
935 3649 50         if (__builtin_expect((uint64_t)(size + tomb) * 4 > (uint64_t)cap * 3, 0)) {
  39 50          
  17 100          
  14 50          
  8 50          
  5 50          
  13 50          
  6 100          
  2517 100          
  1017 50          
  13            
936 52 0         if (h->iterating > 0) { h->deferred = 1; return; }
  0 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  44 50          
  7 0          
  0            
937 52           uint32_t new_cap = cap * 2;
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  44            
  7            
  0            
938 52 0         if (new_cap <= hdr->max_table_cap)
  0 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 100          
  44 50          
  7 0          
  0            
939 48           SHM_FN(resize)(h, new_cap);
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  40            
  7            
  0            
940 3597 100         } else if (__builtin_expect(tomb > size || tomb > cap / 4, 0)) {
  39 50          
  17 50          
  13 50          
  8 50          
  5 50          
  13 50          
  6 50          
  2473 50          
  1010 50          
  13 50          
    50          
    50          
    50          
    100          
    50          
    50          
    50          
    50          
    50          
941 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            
942 3           SHM_FN(resize)(h, cap);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
943             }
944             }
945              
946 801           static inline void SHM_FN(maybe_shrink)(ShmHandle *h) {
  5            
  2            
  0            
  1            
  1            
  0            
  1            
  290            
  500            
  1            
947 801           ShmHeader *hdr = h->hdr;
  5            
  2            
  0            
  1            
  1            
  0            
  1            
  290            
  500            
  1            
948 801 50         if (hdr->table_cap <= SHM_INITIAL_CAP) return;
  5 50          
  2 0          
  0 50          
  1 50          
  1 0          
  0 50          
  1 100          
  290 50          
  500 50          
  1            
949 778 0         if (__builtin_expect((uint64_t)hdr->size * 4 < hdr->table_cap, 0)) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  278 100          
  500 0          
  0            
950 145 0         if (h->iterating > 0) { h->deferred = 1; return; }
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  144 50          
  1 0          
  0            
951 3           uint32_t new_cap = hdr->table_cap / 2;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  1            
  0            
952 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          
  2 50          
  1 0          
  0            
953 3           SHM_FN(resize)(h, new_cap);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  1            
  0            
954             }
955             }
956              
957 24           static inline void SHM_FN(flush_deferred)(ShmHandle *h) {
  3            
  1            
  0            
  0            
  0            
  0            
  1            
  19            
  0            
  0            
958 24 50         if (!h->deferred || h->iterating > 0) return;
  3 0          
  1 50          
  0 0          
  0 0          
  0 0          
  0 0          
  1 0          
  19 0          
  0 0          
  0 0          
    0          
    50          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
959 2           h->deferred = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
960 2           ShmHeader *hdr = h->hdr;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
961 2           shm_rwlock_wrlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
962 2           shm_seqlock_write_begin(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
963 2           uint32_t size = hdr->size, tomb = hdr->tombstones, cap = hdr->table_cap;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
964 2 0         if ((uint64_t)(size + tomb) * 4 > (uint64_t)cap * 3) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
965 0           uint32_t new_cap = cap * 2;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
966 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            
967 0           SHM_FN(resize)(h, new_cap);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
968 4 0         } else if (cap > SHM_INITIAL_CAP && (uint64_t)size * 4 < cap) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  4 0          
  0 0          
  0 0          
    0          
    0          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
969 2           uint32_t new_cap = cap / 2;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
970 2 0         if (new_cap < SHM_INITIAL_CAP) new_cap = SHM_INITIAL_CAP;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
971 2           SHM_FN(resize)(h, new_cap);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
972 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          
973 0           SHM_FN(resize)(h, cap);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
974             }
975 2           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
976 2           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
977             }
978              
979             /* ---- Put ---- */
980              
981 3619           static int SHM_FN(put_impl)(ShmHandle *h,
  35            
  16            
  13            
  5            
  5            
  13            
  4            
  2504            
  1014            
  10            
982             #ifdef SHM_KEY_IS_INT
983             SHM_KEY_INT_TYPE key,
984             #else
985             const char *key_str, uint32_t key_len, bool key_utf8,
986             #endif
987             #ifdef SHM_VAL_IS_STR
988             const char *val_str, uint32_t val_len, bool val_utf8,
989             #else
990             SHM_VAL_INT_TYPE value,
991             #endif
992             uint32_t ttl_sec
993             ) {
994 3619           ShmHeader *hdr = h->hdr;
  35            
  16            
  13            
  5            
  5            
  13            
  4            
  2504            
  1014            
  10            
995 3619           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  35            
  16            
  13            
  5            
  5            
  13            
  4            
  2504            
  1014            
  10            
996 3619           uint8_t *states = h->states;
  35            
  16            
  13            
  5            
  5            
  13            
  4            
  2504            
  1014            
  10            
997              
998 3619           shm_rwlock_wrlock(hdr);
  35            
  16            
  13            
  5            
  5            
  13            
  4            
  2504            
  1014            
  10            
999 3619           shm_seqlock_write_begin(&hdr->seq);
  35            
  16            
  13            
  5            
  5            
  13            
  4            
  2504            
  1014            
  10            
1000              
1001 3619           SHM_FN(maybe_grow)(h);
  35            
  16            
  13            
  5            
  5            
  13            
  4            
  2504            
  1014            
  10            
1002 3619           uint32_t mask = hdr->table_cap - 1;
  35            
  16            
  13            
  5            
  5            
  13            
  4            
  2504            
  1014            
  10            
1003             #ifdef SHM_KEY_IS_INT
1004 3550           uint32_t hash = SHM_HASH_KEY(key);
  5            
  13            
  4            
  2504            
  1014            
  10            
1005             #else
1006 69           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  35            
  16            
  13            
  5            
1007             #endif
1008 3619           uint32_t pos = hash & mask;
  35            
  16            
  13            
  5            
  5            
  13            
  4            
  2504            
  1014            
  10            
1009 3619           uint32_t insert_pos = UINT32_MAX;
  35            
  16            
  13            
  5            
  5            
  13            
  4            
  2504            
  1014            
  10            
1010              
1011             /* Resolve effective expiry timestamp */
1012 3619           uint32_t exp_ts = 0;
  35            
  16            
  13            
  5            
  5            
  13            
  4            
  2504            
  1014            
  10            
1013 3619 100         if (h->expires_at) {
  35 100          
  16 50          
  13 50          
  5 50          
  5 50          
  13 50          
  4 100          
  2504 50          
  1014 50          
  10            
1014 227 100         uint32_t ttl = (ttl_sec == SHM_TTL_USE_DEFAULT) ? hdr->default_ttl : ttl_sec;
  5 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  221 0          
  0 0          
  0            
1015 227 50         if (ttl > 0)
  5 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  221 0          
  0 0          
  0            
1016 220           exp_ts = shm_expiry_ts(ttl);
  5            
  1            
  0            
  0            
  0            
  0            
  0            
  214            
  0            
  0            
1017             }
1018              
1019 9062 50         for (uint32_t i = 0; i <= mask; i++) {
  41 50          
  19 50          
  21 50          
  9 50          
  5 50          
  21 50          
  4 100          
  5564 50          
  3366 50          
  12            
1020 9061           uint32_t idx = (pos + i) & mask;
  41            
  19            
  21            
  9            
  5            
  21            
  4            
  5563            
  3366            
  12            
1021              
1022 9061 100         if (states[idx] == SHM_EMPTY) {
  41 100          
  19 100          
  21 100          
  9 50          
  5 100          
  21 50          
  4 100          
  5563 100          
  3366 100          
  12            
1023 3616 50         if (insert_pos == UINT32_MAX) insert_pos = idx;
  34 50          
  16 50          
  13 100          
  5 50          
  5 50          
  13 50          
  4 100          
  2502 50          
  1014 50          
  10            
1024 3616           break;
  34            
  16            
  13            
  5            
  5            
  13            
  4            
  2502            
  1014            
  10            
1025             }
1026 5445 50         if (states[idx] == SHM_TOMBSTONE) {
  7 50          
  3 50          
  8 100          
  4 0          
  0 50          
  8 0          
  0 100          
  3061 50          
  2352 50          
  2            
1027 2 0         if (insert_pos == UINT32_MAX) insert_pos = idx;
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
1028 2           continue;
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  1            
  0            
  0            
1029             }
1030             /* SHM_LIVE — check key match */
1031             #ifdef SHM_KEY_IS_INT
1032 5422 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 50          
  8 0          
  0 100          
  3060 50          
  2352 50          
  2            
1033             #else
1034 21 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  7 50          
  3 100          
  8 50          
  3 50          
    50          
    100          
    50          
    50          
    50          
    0          
    0          
1035             #endif
1036             /* update existing value */
1037             #ifdef SHM_VAL_IS_STR
1038 1           uint32_t new_voff = shm_arena_alloc(hdr, h->arena, val_len);
  1            
  0            
  0            
  0            
1039 1 50         if (new_voff == 0 && val_len > 0) {
  1 0          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
1040 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
1041 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
1042 0           return 0;
  0            
  0            
  0            
  0            
1043             }
1044 1           memcpy(h->arena + new_voff, val_str, val_len);
  1            
  0            
  0            
  0            
1045 1           uint32_t old_voff = nodes[idx].val_off;
  1            
  0            
  0            
  0            
1046 1           uint32_t old_vlen = SHM_UNPACK_LEN(nodes[idx].val_len);
  1            
  0            
  0            
  0            
1047 1           nodes[idx].val_off = new_voff;
  1            
  0            
  0            
  0            
1048 1 50         nodes[idx].val_len = SHM_PACK_LEN(val_len, val_utf8);
  1 0          
  0 0          
  0 0          
  0            
1049 1           shm_arena_free_block(hdr, h->arena, old_voff, old_vlen);
  1            
  0            
  0            
  0            
1050             #else
1051 1           nodes[idx].value = value;
  0            
  0            
  0            
  1            
  0            
  0            
1052             #endif
1053 2 50         if (h->lru_prev) shm_lru_promote(h, idx);
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
1054 2 50         if (h->expires_at) h->expires_at[idx] = exp_ts;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
1055 2           shm_seqlock_write_end(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1056 2           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1057 2           return 1;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1058             }
1059             }
1060              
1061 3617 50         if (insert_pos == UINT32_MAX) {
  34 50          
  16 50          
  13 50          
  5 50          
  5 50          
  13 50          
  4 100          
  2503 50          
  1014 50          
  10            
1062 1           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1063 1           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1064 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1065             }
1066              
1067             /* LRU eviction only when actually inserting */
1068 3616 100         if (hdr->max_size > 0 && hdr->size >= hdr->max_size)
  34 100          
  16 100          
  13 100          
  5 50          
  5 0          
  13 50          
  4 0          
  2502 50          
  1014 0          
  10 50          
    0          
    50          
    0          
    100          
    100          
    50          
    0          
    50          
    0          
1069 13           SHM_FN(lru_evict_one)(h);
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
1070              
1071             /* insert new entry */
1072 3616           int was_tombstone = (states[insert_pos] == SHM_TOMBSTONE);
  34            
  16            
  13            
  5            
  5            
  13            
  4            
  2502            
  1014            
  10            
1073              
1074             #ifdef SHM_KEY_IS_INT
1075 3548           nodes[insert_pos].key = key;
  5            
  13            
  4            
  2502            
  1014            
  10            
1076             #else
1077 68           uint32_t koff = shm_arena_alloc(hdr, h->arena, key_len);
  34            
  16            
  13            
  5            
1078 68 50         if (koff == 0 && key_len > 0) {
  34 0          
  16 50          
  13 0          
  5 50          
    0          
    50          
    0          
1079 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
1080 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
1081 0           return 0;
  0            
  0            
  0            
  0            
1082             }
1083 68           memcpy(h->arena + koff, key_str, key_len);
  34            
  16            
  13            
  5            
1084 68           nodes[insert_pos].key_off = koff;
  34            
  16            
  13            
  5            
1085 68 100         nodes[insert_pos].key_len = SHM_PACK_LEN(key_len, key_utf8);
  34 50          
  16 50          
  13 100          
  5            
1086             #endif
1087              
1088             #ifdef SHM_VAL_IS_STR
1089 56           uint32_t voff = shm_arena_alloc(hdr, h->arena, val_len);
  34            
  5            
  13            
  4            
1090 56 50         if (voff == 0 && val_len > 0) {
  34 0          
  5 50          
  13 0          
  4 50          
    0          
    50          
    0          
1091             #ifndef SHM_KEY_IS_INT
1092 0           shm_arena_free_block(hdr, h->arena, koff, key_len);
  0            
1093             #endif
1094 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
1095 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
1096 0           return 0;
  0            
  0            
  0            
  0            
1097             }
1098 56           memcpy(h->arena + voff, val_str, val_len);
  34            
  5            
  13            
  4            
1099 56           nodes[insert_pos].val_off = voff;
  34            
  5            
  13            
  4            
1100 56 100         nodes[insert_pos].val_len = SHM_PACK_LEN(val_len, val_utf8);
  34 100          
  5 50          
  13 100          
  4            
1101             #else
1102 3560           nodes[insert_pos].value = value;
  16            
  13            
  5            
  2502            
  1014            
  10            
1103             #endif
1104              
1105 3616           states[insert_pos] = SHM_LIVE;
  34            
  16            
  13            
  5            
  5            
  13            
  4            
  2502            
  1014            
  10            
1106 3616           hdr->size++;
  34            
  16            
  13            
  5            
  5            
  13            
  4            
  2502            
  1014            
  10            
1107 3616 50         if (was_tombstone) hdr->tombstones--;
  34 50          
  16 50          
  13 100          
  5 50          
  5 50          
  13 50          
  4 100          
  2502 50          
  1014 50          
  10            
1108              
1109 3616 100         if (h->lru_prev) shm_lru_push_front(h, insert_pos);
  34 100          
  16 50          
  13 50          
  5 50          
  5 50          
  13 50          
  4 100          
  2502 50          
  1014 50          
  10            
1110 3616 100         if (h->expires_at) h->expires_at[insert_pos] = exp_ts;
  34 100          
  16 50          
  13 50          
  5 50          
  5 50          
  13 50          
  4 100          
  2502 50          
  1014 50          
  10            
1111              
1112 3616           shm_seqlock_write_end(&hdr->seq);
  34            
  16            
  13            
  5            
  5            
  13            
  4            
  2502            
  1014            
  10            
1113 3616           shm_rwlock_wrunlock(hdr);
  34            
  16            
  13            
  5            
  5            
  13            
  4            
  2502            
  1014            
  10            
1114 3616           return 1;
  34            
  16            
  13            
  5            
  5            
  13            
  4            
  2502            
  1014            
  10            
1115             }
1116              
1117 3606           static inline int SHM_FN(put)(ShmHandle *h,
  34            
  16            
  13            
  5            
  5            
  13            
  4            
  2492            
  1014            
  10            
1118             #ifdef SHM_KEY_IS_INT
1119             SHM_KEY_INT_TYPE key,
1120             #else
1121             const char *key_str, uint32_t key_len, bool key_utf8,
1122             #endif
1123             #ifdef SHM_VAL_IS_STR
1124             const char *val_str, uint32_t val_len, bool val_utf8
1125             #else
1126             SHM_VAL_INT_TYPE value
1127             #endif
1128             ) {
1129 3606           return SHM_FN(put_impl)(h,
  34            
  16            
  13            
  5            
  5            
  13            
  4            
  2492            
  1014            
  10            
1130             #ifdef SHM_KEY_IS_INT
1131             key,
1132             #else
1133             key_str, key_len, key_utf8,
1134             #endif
1135             #ifdef SHM_VAL_IS_STR
1136             val_str, val_len, val_utf8,
1137             #else
1138             value,
1139             #endif
1140             SHM_TTL_USE_DEFAULT);
1141             }
1142              
1143 13           static inline int SHM_FN(put_ttl)(ShmHandle *h,
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
1144             #ifdef SHM_KEY_IS_INT
1145             SHM_KEY_INT_TYPE key,
1146             #else
1147             const char *key_str, uint32_t key_len, bool key_utf8,
1148             #endif
1149             #ifdef SHM_VAL_IS_STR
1150             const char *val_str, uint32_t val_len, bool val_utf8,
1151             #else
1152             SHM_VAL_INT_TYPE value,
1153             #endif
1154             uint32_t ttl_sec
1155             ) {
1156 13 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          
  12 0          
  0 0          
  0            
1157 13           return SHM_FN(put_impl)(h,
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
1158             #ifdef SHM_KEY_IS_INT
1159             key,
1160             #else
1161             key_str, key_len, key_utf8,
1162             #endif
1163             #ifdef SHM_VAL_IS_STR
1164             val_str, val_len, val_utf8,
1165             #else
1166             value,
1167             #endif
1168             ttl_sec);
1169             }
1170              
1171             /* ---- Get (locked path for LRU/TTL) ---- */
1172              
1173 44           static int SHM_FN(get_lru)(ShmHandle *h,
  4            
  2            
  0            
  0            
  0            
  0            
  0            
  38            
  0            
  0            
1174             #ifdef SHM_KEY_IS_INT
1175             SHM_KEY_INT_TYPE key,
1176             #else
1177             const char *key_str, uint32_t key_len, bool key_utf8,
1178             #endif
1179             #ifdef SHM_VAL_IS_STR
1180             const char **out_str, uint32_t *out_len, bool *out_utf8
1181             #else
1182             SHM_VAL_INT_TYPE *out_value
1183             #endif
1184             ) {
1185 44           ShmHeader *hdr = h->hdr;
  4            
  2            
  0            
  0            
  0            
  0            
  0            
  38            
  0            
  0            
1186 44           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  4            
  2            
  0            
  0            
  0            
  0            
  0            
  38            
  0            
  0            
1187 44           uint8_t *states = h->states;
  4            
  2            
  0            
  0            
  0            
  0            
  0            
  38            
  0            
  0            
1188              
1189             /* TTL-only (no LRU): use rdlock, upgrade to wrlock only on expiry */
1190 44 100         int ttl_only = h->expires_at && !h->lru_prev;
  4 50          
  2 50          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  38 0          
  0 0          
  0 0          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
    0          
    0          
1191 44 100         uint32_t now = h->expires_at ? (uint32_t)time(NULL) : 0;
  4 50          
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  38 0          
  0 0          
  0            
1192              
1193 44 100         if (ttl_only)
  4 50          
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  38 0          
  0 0          
  0            
1194 23           shm_rwlock_rdlock(hdr);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  21            
  0            
  0            
1195             else
1196 21           shm_rwlock_wrlock(hdr);
  2            
  2            
  0            
  0            
  0            
  0            
  0            
  17            
  0            
  0            
1197              
1198 44           uint32_t mask = hdr->table_cap - 1;
  4            
  2            
  0            
  0            
  0            
  0            
  0            
  38            
  0            
  0            
1199             #ifdef SHM_KEY_IS_INT
1200 38           uint32_t hash = SHM_HASH_KEY(key);
  0            
  0            
  0            
  38            
  0            
  0            
1201             #else
1202 6           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  4            
  2            
  0            
  0            
1203             #endif
1204 44           uint32_t pos = hash & mask;
  4            
  2            
  0            
  0            
  0            
  0            
  0            
  38            
  0            
  0            
1205              
1206 56 50         for (uint32_t i = 0; i <= mask; i++) {
  6 50          
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  47 0          
  0 0          
  0            
1207 56           uint32_t idx = (pos + i) & mask;
  6            
  3            
  0            
  0            
  0            
  0            
  0            
  47            
  0            
  0            
1208 56 100         if (states[idx] == SHM_EMPTY) break;
  6 100          
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  47 0          
  0 0          
  0            
1209 47 100         if (states[idx] == SHM_TOMBSTONE) continue;
  5 100          
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  40 0          
  0 0          
  0            
1210             #ifdef SHM_KEY_IS_INT
1211 32 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 0          
  0 100          
  32 0          
  0 0          
  0            
1212             #else
1213 5 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  4 50          
  1 100          
  0 50          
  0 50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
1214             #endif
1215             /* TTL check */
1216 35 100         if (SHM_IS_EXPIRED(h, idx, now)) {
  3 50          
  1 100          
  0 50          
  0 0          
  0 0          
  0 0          
  0 0          
  31 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
1217 8 50         if (ttl_only) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  7 0          
  0 0          
  0            
1218             /* upgrade: rdunlock, wrlock, re-probe, expire */
1219 7           shm_rwlock_rdunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
1220 7           shm_rwlock_wrlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
1221             /* re-probe — table may have changed */
1222 7           mask = hdr->table_cap - 1;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
1223 7           pos = hash & mask;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
1224 7 50         for (uint32_t j = 0; j < hdr->table_cap; j++) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  6 0          
  0 0          
  0            
1225 7           uint32_t idx2 = (pos + j) & mask;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
1226 7 50         if (states[idx2] == SHM_EMPTY) break;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  6 0          
  0 0          
  0            
1227 7 50         if (states[idx2] == SHM_TOMBSTONE) continue;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  6 0          
  0 0          
  0            
1228             #ifdef SHM_KEY_IS_INT
1229 6 0         if (SHM_KEY_EQ(&nodes[idx2], key)) {
  0 0          
  0 0          
  0 50          
  6 0          
  0 0          
  0            
1230             #else
1231 1 50         if (SHM_KEY_EQ_STR(&nodes[idx2], h->arena, key_str, key_len, key_utf8)) {
  1 50          
  0 50          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1232             #endif
1233 7 50         if (SHM_IS_EXPIRED(h, idx2, now)) {
  1 50          
  0 50          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  6 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
1234 7           shm_seqlock_write_begin(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
1235 7           SHM_FN(expire_at)(h, idx2);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
1236 7           shm_seqlock_write_end(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
1237             }
1238 7           break;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
1239             }
1240             }
1241 7           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
1242             } else {
1243 1           shm_seqlock_write_begin(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1244 1           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1245 1           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1246 1           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1247             }
1248 8           return 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1249             }
1250              
1251             /* LRU promote (only when LRU active, already under wrlock) */
1252 27 100         if (h->lru_prev) {
  2 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  24 0          
  0 0          
  0            
1253 12           shm_seqlock_write_begin(&hdr->seq);
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
1254 12           shm_lru_promote(h, idx);
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
1255 12           shm_seqlock_write_end(&hdr->seq);
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
1256             }
1257              
1258             #ifdef SHM_VAL_IS_STR
1259             {
1260 2           uint32_t vl = SHM_UNPACK_LEN(nodes[idx].val_len);
  2            
  0            
  0            
  0            
1261 2 50         if (!shm_ensure_copy_buf(h, vl)) {
  2 0          
  0 0          
  0 0          
  0            
1262 0 0         if (ttl_only)
  0 0          
  0 0          
  0 0          
  0            
1263 0           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
1264             else
1265 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
1266 0           return 0;
  0            
  0            
  0            
  0            
1267             }
1268 2           memcpy(h->copy_buf, h->arena + nodes[idx].val_off, vl);
  2            
  0            
  0            
  0            
1269 2           *out_str = h->copy_buf;
  2            
  0            
  0            
  0            
1270 2           *out_len = vl;
  2            
  0            
  0            
  0            
1271 2           *out_utf8 = SHM_UNPACK_UTF8(nodes[idx].val_len);
  2            
  0            
  0            
  0            
1272             }
1273             #else
1274 25           *out_value = nodes[idx].value;
  1            
  0            
  0            
  24            
  0            
  0            
1275             #endif
1276 27 100         if (ttl_only)
  2 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  24 0          
  0 0          
  0            
1277 15           shm_rwlock_rdunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  14            
  0            
  0            
1278             else
1279 12           shm_rwlock_wrunlock(hdr);
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
1280 27           return 1;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  24            
  0            
  0            
1281             }
1282             }
1283              
1284 9 50         if (ttl_only)
  1 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  7 0          
  0 0          
  0            
1285 1           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1286             else
1287 8           shm_rwlock_wrunlock(hdr);
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
1288 9           return 0;
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1289             }
1290              
1291             /* ---- Get (seqlock — lock-free read path) ---- */
1292              
1293 232           static int SHM_FN(get)(ShmHandle *h,
  13            
  5            
  3            
  5            
  3            
  3            
  5            
  184            
  5            
  6            
1294             #ifdef SHM_KEY_IS_INT
1295             SHM_KEY_INT_TYPE key,
1296             #else
1297             const char *key_str, uint32_t key_len, bool key_utf8,
1298             #endif
1299             #ifdef SHM_VAL_IS_STR
1300             const char **out_str, uint32_t *out_len, bool *out_utf8
1301             #else
1302             SHM_VAL_INT_TYPE *out_value
1303             #endif
1304             ) {
1305             /* LRU/TTL active: locked path */
1306 232 100         if (h->lru_prev || h->expires_at) {
  13 100          
  5 100          
  3 50          
  5 50          
  3 50          
  3 50          
  5 50          
  184 50          
  5 50          
  6 50          
    50          
    50          
    50          
    100          
    100          
    50          
    50          
    50          
    50          
1307             #ifdef SHM_KEY_IS_INT
1308 38           return SHM_FN(get_lru)(h, key,
  0            
  0            
  0            
  38            
  0            
  0            
1309             #else
1310 6           return SHM_FN(get_lru)(h, key_str, key_len, key_utf8,
  4            
  2            
  0            
  0            
1311             #endif
1312             #ifdef SHM_VAL_IS_STR
1313             out_str, out_len, out_utf8);
1314             #else
1315             out_value);
1316             #endif
1317             }
1318              
1319             /* Fast seqlock path (no LRU/TTL) */
1320 188           ShmHeader *hdr = h->hdr;
  9            
  3            
  3            
  5            
  3            
  3            
  5            
  146            
  5            
  6            
1321 188           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  9            
  3            
  3            
  5            
  3            
  3            
  5            
  146            
  5            
  6            
1322 188           uint8_t *states = h->states;
  9            
  3            
  3            
  5            
  3            
  3            
  5            
  146            
  5            
  6            
1323 188           uint32_t max_mask = h->max_mask;
  9            
  3            
  3            
  5            
  3            
  3            
  5            
  146            
  5            
  6            
1324             #if !defined(SHM_KEY_IS_INT) || defined(SHM_VAL_IS_STR)
1325 31           uint64_t arena_cap = hdr->arena_cap; /* immutable after create */
  9            
  3            
  3            
  5            
  3            
  3            
  5            
1326             #endif
1327             #ifndef SHM_KEY_IS_INT
1328 20           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  9            
  3            
  3            
  5            
1329             #else
1330 168           uint32_t hash = SHM_HASH_KEY(key);
  3            
  3            
  5            
  146            
  5            
  6            
1331             #endif
1332              
1333 7           for (;;) {
  4            
  0            
  0            
  0            
  1            
  1            
  1            
  0            
  0            
  0            
1334 195           uint32_t seq = shm_seqlock_read_begin(hdr);
  13            
  3            
  3            
  5            
  4            
  4            
  6            
  146            
  5            
  6            
1335              
1336 195           uint32_t mask = (hdr->table_cap - 1) & max_mask;
  13            
  3            
  3            
  5            
  4            
  4            
  6            
  146            
  5            
  6            
1337 195           uint32_t pos = hash & mask;
  13            
  3            
  3            
  5            
  4            
  4            
  6            
  146            
  5            
  6            
1338 195           int found = 0;
  13            
  3            
  3            
  5            
  4            
  4            
  6            
  146            
  5            
  6            
1339              
1340             /* local copies of result data */
1341             #ifdef SHM_VAL_IS_STR
1342 27           uint32_t local_vl = 0, local_voff = 0, local_vlen_packed = 0;
  13            
  4            
  4            
  6            
1343             #else
1344 168           SHM_VAL_INT_TYPE local_value = 0;
  3            
  3            
  5            
  146            
  5            
  6            
1345             #endif
1346              
1347 337 50         for (uint32_t i = 0; i <= mask; i++) {
  16 50          
  4 50          
  4 50          
  6 50          
  4 50          
  5 50          
  7 50          
  275 50          
  6 50          
  10            
1348 337           uint32_t idx = (pos + i) & mask;
  16            
  4            
  4            
  6            
  4            
  5            
  7            
  275            
  6            
  10            
1349 337           uint8_t st = states[idx];
  16            
  4            
  4            
  6            
  4            
  5            
  7            
  275            
  6            
  10            
1350              
1351 337 100         if (st == SHM_EMPTY) break;
  16 100          
  4 50          
  4 50          
  6 50          
  4 50          
  5 100          
  7 100          
  275 50          
  6 100          
  10            
1352 262 100         if (st == SHM_TOMBSTONE) continue;
  14 100          
  3 50          
  4 50          
  6 50          
  4 50          
  5 100          
  6 100          
  205 50          
  6 100          
  9            
1353              
1354             #ifdef SHM_KEY_IS_INT
1355 206 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  4 100          
  5 50          
  5 100          
  178 100          
  6 100          
  8            
1356             #else
1357             {
1358             /* bounds-checked key comparison (torn reads can't segfault) */
1359 24           uint32_t kl = SHM_UNPACK_LEN(nodes[idx].key_len);
  12            
  2            
  4            
  6            
1360 24           uint32_t koff = nodes[idx].key_off;
  12            
  2            
  4            
  6            
1361 24 100         if (kl != key_len) continue;
  12 50          
  2 100          
  4 100          
  6            
1362 21 50         if (SHM_UNPACK_UTF8(nodes[idx].key_len) != key_utf8) continue;
  11 50          
  2 50          
  3 50          
  5            
1363 21 50         if ((uint64_t)koff + kl > arena_cap) break; /* torn → will retry */
  11 50          
  2 50          
  3 50          
  5            
1364 21 50         if (memcmp(h->arena + koff, key_str, kl) != 0) continue;
  11 50          
  2 50          
  3 50          
  5            
1365             }
1366             {
1367             #endif
1368             #ifdef SHM_VAL_IS_STR
1369 24           local_vlen_packed = nodes[idx].val_len;
  11            
  4            
  4            
  5            
1370 24           local_vl = SHM_UNPACK_LEN(local_vlen_packed);
  11            
  4            
  4            
  5            
1371 24           local_voff = nodes[idx].val_off;
  11            
  4            
  4            
  5            
1372             #else
1373 96           local_value = __atomic_load_n(&nodes[idx].value, __ATOMIC_RELAXED);
  2            
  3            
  5            
  76            
  5            
  5            
1374             #endif
1375 120           found = 1;
  11            
  2            
  3            
  5            
  4            
  4            
  5            
  76            
  5            
  5            
1376 120           break;
  11            
  2            
  3            
  5            
  4            
  4            
  5            
  76            
  5            
  5            
1377             }
1378             }
1379              
1380 195 100         if (found) {
  13 100          
  3 50          
  3 50          
  5 50          
  4 50          
  4 100          
  6 100          
  146 50          
  5 100          
  6            
1381             #ifdef SHM_VAL_IS_STR
1382             /* bounds check arena access before copy */
1383 24 50         if ((uint64_t)local_voff + local_vl > arena_cap) {
  11 50          
  4 50          
  4 50          
  5            
1384             /* torn read — retry */
1385 0           continue;
  0            
  0            
  0            
  0            
1386             }
1387 24 100         if (local_vl > h->copy_buf_size) {
  11 100          
  4 100          
  4 100          
  5            
1388             /* need bigger buffer — exit seqlock, grow, retry */
1389 7 50         if (!shm_ensure_copy_buf(h, local_vl)) return 0;
  4 50          
  1 50          
  1 50          
  1            
1390 7           continue;
  4            
  1            
  1            
  1            
1391             }
1392 17           memcpy(h->copy_buf, h->arena + local_voff, local_vl);
  7            
  3            
  3            
  4            
1393             #endif
1394 113 50         if (shm_seqlock_read_retry(&hdr->seq, seq)) continue;
  7 50          
  2 50          
  3 50          
  5 50          
  3 50          
  3 50          
  4 50          
  76 50          
  5 50          
  5            
1395              
1396             /* validated — commit results */
1397             #ifdef SHM_VAL_IS_STR
1398 17           *out_str = h->copy_buf;
  7            
  3            
  3            
  4            
1399 17           *out_len = local_vl;
  7            
  3            
  3            
  4            
1400 17           *out_utf8 = SHM_UNPACK_UTF8(local_vlen_packed);
  7            
  3            
  3            
  4            
1401             #else
1402 96           *out_value = local_value;
  2            
  3            
  5            
  76            
  5            
  5            
1403             #endif
1404 113           return 1;
  7            
  2            
  3            
  5            
  3            
  3            
  4            
  76            
  5            
  5            
1405             }
1406              
1407 75 50         if (shm_seqlock_read_retry(&hdr->seq, seq)) continue;
  2 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 50          
  70 0          
  0 50          
  1            
1408 75           return 0;
  2            
  1            
  0            
  0            
  0            
  0            
  1            
  70            
  0            
  1            
1409             }
1410             }
1411              
1412             /* ---- Exists (with TTL check under rdlock) ---- */
1413              
1414 2           static int SHM_FN(exists_ttl)(ShmHandle *h,
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
1415             #ifdef SHM_KEY_IS_INT
1416             SHM_KEY_INT_TYPE key
1417             #else
1418             const char *key_str, uint32_t key_len, bool key_utf8
1419             #endif
1420             ) {
1421 2           ShmHeader *hdr = h->hdr;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
1422 2           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
1423 2           uint8_t *states = h->states;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
1424 2           uint32_t now = (uint32_t)time(NULL);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
1425              
1426 2           shm_rwlock_rdlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
1427              
1428 2           uint32_t mask = hdr->table_cap - 1;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
1429             #ifdef SHM_KEY_IS_INT
1430 2           uint32_t hash = SHM_HASH_KEY(key);
  0            
  0            
  0            
  2            
  0            
  0            
1431             #else
1432 0           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  0            
  0            
  0            
  0            
1433             #endif
1434 2           uint32_t pos = hash & mask;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
1435              
1436 3 0         for (uint32_t i = 0; i <= mask; i++) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
1437 3           uint32_t idx = (pos + i) & mask;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1438 3 0         if (states[idx] == SHM_EMPTY) break;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  3 0          
  0 0          
  0            
1439 2 0         if (states[idx] == SHM_TOMBSTONE) continue;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  2 0          
  0 0          
  0            
1440             #ifdef SHM_KEY_IS_INT
1441 1 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
1442             #else
1443 0 0         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  0 0          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1444             #endif
1445 1 0         int found = !SHM_IS_EXPIRED(h, idx, now);
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  1 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
1446 1           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1447 1           return found;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1448             }
1449             }
1450              
1451 1           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1452 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1453             }
1454              
1455             /* ---- Exists (seqlock — lock-free read path) ---- */
1456              
1457 10           static int SHM_FN(exists)(ShmHandle *h,
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  4            
  0            
  2            
1458             #ifdef SHM_KEY_IS_INT
1459             SHM_KEY_INT_TYPE key
1460             #else
1461             const char *key_str, uint32_t key_len, bool key_utf8
1462             #endif
1463             ) {
1464             /* TTL active: use rdlock path for expiry check */
1465 10 50         if (h->expires_at) {
  1 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 100          
  4 0          
  0 50          
  2            
1466             #ifdef SHM_KEY_IS_INT
1467 2           return SHM_FN(exists_ttl)(h, key);
  0            
  0            
  0            
  2            
  0            
  0            
1468             #else
1469 0           return SHM_FN(exists_ttl)(h, key_str, key_len, key_utf8);
  0            
  0            
  0            
  0            
1470             #endif
1471             }
1472              
1473 8           ShmHeader *hdr = h->hdr;
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  2            
  0            
  2            
1474 8           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  2            
  0            
  2            
1475 8           uint8_t *states = h->states;
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  2            
  0            
  2            
1476 8           uint32_t max_mask = h->max_mask;
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  2            
  0            
  2            
1477             #ifndef SHM_KEY_IS_INT
1478 3           uint64_t arena_cap = hdr->arena_cap;
  1            
  1            
  0            
  1            
1479 3           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  1            
  1            
  0            
  1            
1480             #else
1481 5           uint32_t hash = SHM_HASH_KEY(key);
  0            
  0            
  1            
  2            
  0            
  2            
1482             #endif
1483              
1484 0           for (;;) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1485 8           uint32_t seq = shm_seqlock_read_begin(hdr);
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  2            
  0            
  2            
1486              
1487 8           uint32_t mask = (hdr->table_cap - 1) & max_mask;
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  2            
  0            
  2            
1488 8           uint32_t pos = hash & mask;
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  2            
  0            
  2            
1489 8           int found = 0;
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  2            
  0            
  2            
1490              
1491 8 50         for (uint32_t i = 0; i <= mask; i++) {
  1 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  2 0          
  0 50          
  2            
1492 8           uint32_t idx = (pos + i) & mask;
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  2            
  0            
  2            
1493 8           uint8_t st = states[idx];
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  2            
  0            
  2            
1494 8 50         if (st == SHM_EMPTY) break;
  1 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  2 0          
  0 100          
  2            
1495 7 50         if (st == SHM_TOMBSTONE) continue;
  1 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  2 0          
  0 50          
  1            
1496             #ifdef SHM_KEY_IS_INT
1497 4 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 50          
  1 50          
  2 0          
  0 50          
  1            
1498             #else
1499             {
1500 3           uint32_t kl = SHM_UNPACK_LEN(nodes[idx].key_len);
  1            
  1            
  0            
  1            
1501 3           uint32_t koff = nodes[idx].key_off;
  1            
  1            
  0            
  1            
1502 3 50         if (kl != key_len) continue;
  1 50          
  1 0          
  0 50          
  1            
1503 3 50         if (SHM_UNPACK_UTF8(nodes[idx].key_len) != key_utf8) continue;
  1 50          
  1 0          
  0 50          
  1            
1504 3 50         if ((uint64_t)koff + kl > arena_cap) break;
  1 50          
  1 0          
  0 50          
  1            
1505 3 50         if (memcmp(h->arena + koff, key_str, kl) != 0) continue;
  1 50          
  1 0          
  0 50          
  1            
1506             }
1507             {
1508             #endif
1509 7           found = 1;
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  2            
  0            
  1            
1510 7           break;
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  2            
  0            
  1            
1511             }
1512             }
1513              
1514 8 50         if (shm_seqlock_read_retry(&hdr->seq, seq)) continue;
  1 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  2 0          
  0 50          
  2            
1515 8           return found;
  1            
  1            
  0            
  1            
  0            
  0            
  1            
  2            
  0            
  2            
1516             }
1517             }
1518              
1519             /* ---- Remove ---- */
1520              
1521 785           static int SHM_FN(remove)(ShmHandle *h,
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  279            
  500            
  1            
1522             #ifdef SHM_KEY_IS_INT
1523             SHM_KEY_INT_TYPE key
1524             #else
1525             const char *key_str, uint32_t key_len, bool key_utf8
1526             #endif
1527             ) {
1528 785           ShmHeader *hdr = h->hdr;
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  279            
  500            
  1            
1529 785           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  279            
  500            
  1            
1530 785           uint8_t *states = h->states;
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  279            
  500            
  1            
1531              
1532 785           shm_rwlock_wrlock(hdr);
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  279            
  500            
  1            
1533 785           shm_seqlock_write_begin(&hdr->seq);
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  279            
  500            
  1            
1534              
1535 785           uint32_t mask = hdr->table_cap - 1;
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  279            
  500            
  1            
1536             #ifdef SHM_KEY_IS_INT
1537 781           uint32_t hash = SHM_HASH_KEY(key);
  0            
  0            
  1            
  279            
  500            
  1            
1538             #else
1539 4           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  2            
  1            
  0            
  1            
1540             #endif
1541 785           uint32_t pos = hash & mask;
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  279            
  500            
  1            
1542              
1543 941 50         for (uint32_t i = 0; i <= mask; i++) {
  2 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  345 50          
  590 50          
  1            
1544 941           uint32_t idx = (pos + i) & mask;
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  345            
  590            
  1            
1545 941 50         if (states[idx] == SHM_EMPTY) break;
  2 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  345 50          
  590 50          
  1            
1546 941 50         if (states[idx] == SHM_TOMBSTONE) continue;
  2 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 100          
  345 100          
  590 50          
  1            
1547              
1548             #ifdef SHM_KEY_IS_INT
1549 798 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 50          
  1 100          
  291 100          
  505 50          
  1            
1550             #else
1551 4 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  2 50          
  1 50          
  0 50          
  1 50          
    50          
    0          
    0          
    0          
    50          
    50          
    50          
1552             #endif
1553 785 50         if (h->lru_prev) shm_lru_unlink(h, idx);
  2 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 100          
  279 50          
  500 50          
  1            
1554 785 50         if (h->expires_at) h->expires_at[idx] = 0;
  2 50          
  1 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  279 50          
  500 50          
  1            
1555 785           SHM_FN(tombstone_at)(h, idx);
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  279            
  500            
  1            
1556              
1557 785           SHM_FN(maybe_shrink)(h);
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  279            
  500            
  1            
1558 785           shm_seqlock_write_end(&hdr->seq);
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  279            
  500            
  1            
1559 785           shm_rwlock_wrunlock(hdr);
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  279            
  500            
  1            
1560 785           return 1;
  2            
  1            
  0            
  1            
  0            
  0            
  1            
  279            
  500            
  1            
1561             }
1562             }
1563              
1564 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1565 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1566 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1567             }
1568              
1569             /* ---- Take (remove and return value) ---- */
1570              
1571 9           static int SHM_FN(take)(ShmHandle *h,
  3            
  1            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
1572             #ifdef SHM_KEY_IS_INT
1573             SHM_KEY_INT_TYPE key,
1574             #else
1575             const char *key_str, uint32_t key_len, bool key_utf8,
1576             #endif
1577             #ifdef SHM_VAL_IS_STR
1578             const char **out_str, uint32_t *out_len, bool *out_utf8
1579             #else
1580             SHM_VAL_INT_TYPE *out_value
1581             #endif
1582             ) {
1583 9           ShmHeader *hdr = h->hdr;
  3            
  1            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
1584 9           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  3            
  1            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
1585 9           uint8_t *states = h->states;
  3            
  1            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
1586 9 50         uint32_t now = h->expires_at ? (uint32_t)time(NULL) : 0;
  3 50          
  1 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 100          
  4 0          
  0 0          
  0            
1587              
1588 9           shm_rwlock_wrlock(hdr);
  3            
  1            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
1589 9           shm_seqlock_write_begin(&hdr->seq);
  3            
  1            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
1590              
1591 9           uint32_t mask = hdr->table_cap - 1;
  3            
  1            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
1592             #ifdef SHM_KEY_IS_INT
1593 5           uint32_t hash = SHM_HASH_KEY(key);
  1            
  0            
  0            
  4            
  0            
  0            
1594             #else
1595 4           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  3            
  1            
  0            
  0            
1596             #endif
1597 9           uint32_t pos = hash & mask;
  3            
  1            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
1598              
1599 10 50         for (uint32_t i = 0; i <= mask; i++) {
  4 50          
  1 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  4 0          
  0 0          
  0            
1600 10           uint32_t idx = (pos + i) & mask;
  4            
  1            
  0            
  0            
  1            
  0            
  0            
  4            
  0            
  0            
1601 10 100         if (states[idx] == SHM_EMPTY) break;
  4 50          
  1 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 100          
  4 0          
  0 0          
  0            
1602 8 100         if (states[idx] == SHM_TOMBSTONE) continue;
  3 50          
  1 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
1603              
1604             #ifdef SHM_KEY_IS_INT
1605 4 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  1 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
1606             #else
1607 3 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  2 50          
  1 50          
  0 50          
  0 50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
1608             #endif
1609             /* check TTL */
1610 7 50         if (SHM_IS_EXPIRED(h, idx, now)) {
  2 0          
  1 0          
  0 50          
  0 0          
  1 0          
  0 0          
  0 0          
  3 0          
  0 0          
  0 0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
1611 1           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1612 1           SHM_FN(maybe_shrink)(h);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1613 1           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1614 1           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1615 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1616             }
1617              
1618             /* copy value out before tombstoning */
1619             #ifdef SHM_VAL_IS_STR
1620             {
1621 3           uint32_t vl = SHM_UNPACK_LEN(nodes[idx].val_len);
  2            
  1            
  0            
  0            
1622 3 50         if (!shm_ensure_copy_buf(h, vl)) {
  2 50          
  1 0          
  0 0          
  0            
1623 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
1624 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
1625 0           return 0;
  0            
  0            
  0            
  0            
1626             }
1627 3           memcpy(h->copy_buf, h->arena + nodes[idx].val_off, vl);
  2            
  1            
  0            
  0            
1628 3           *out_str = h->copy_buf;
  2            
  1            
  0            
  0            
1629 3           *out_len = vl;
  2            
  1            
  0            
  0            
1630 3           *out_utf8 = SHM_UNPACK_UTF8(nodes[idx].val_len);
  2            
  1            
  0            
  0            
1631             }
1632             #else
1633 3           *out_value = nodes[idx].value;
  1            
  0            
  0            
  2            
  0            
  0            
1634             #endif
1635 6 50         if (h->lru_prev) shm_lru_unlink(h, idx);
  2 50          
  1 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
1636 6 50         if (h->expires_at) h->expires_at[idx] = 0;
  2 50          
  1 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
1637 6           SHM_FN(tombstone_at)(h, idx);
  2            
  1            
  0            
  0            
  1            
  0            
  0            
  2            
  0            
  0            
1638              
1639 6           SHM_FN(maybe_shrink)(h);
  2            
  1            
  0            
  0            
  1            
  0            
  0            
  2            
  0            
  0            
1640 6           shm_seqlock_write_end(&hdr->seq);
  2            
  1            
  0            
  0            
  1            
  0            
  0            
  2            
  0            
  0            
1641 6           shm_rwlock_wrunlock(hdr);
  2            
  1            
  0            
  0            
  1            
  0            
  0            
  2            
  0            
  0            
1642 6           return 1;
  2            
  1            
  0            
  0            
  1            
  0            
  0            
  2            
  0            
  0            
1643             }
1644             }
1645              
1646 2           shm_seqlock_write_end(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1647 2           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1648 2           return 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1649             }
1650              
1651             /* ---- Counter operations (integer values only) ---- */
1652              
1653             #ifdef SHM_HAS_COUNTERS
1654              
1655 19           static inline int SHM_FN(find_slot)(ShmHandle *h,
  4            
  2            
  3            
  4            
  2            
  4            
1656             #ifdef SHM_KEY_IS_INT
1657             SHM_KEY_INT_TYPE key,
1658             #else
1659             const char *key_str, uint32_t key_len, bool key_utf8,
1660             #endif
1661             uint32_t *out_idx) {
1662 19           ShmHeader *hdr = h->hdr;
  4            
  2            
  3            
  4            
  2            
  4            
1663 19           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  4            
  2            
  3            
  4            
  2            
  4            
1664 19           uint8_t *states = h->states;
  4            
  2            
  3            
  4            
  2            
  4            
1665 19           uint32_t mask = hdr->table_cap - 1;
  4            
  2            
  3            
  4            
  2            
  4            
1666             #ifdef SHM_KEY_IS_INT
1667 10           uint32_t hash = SHM_HASH_KEY(key);
  4            
  2            
  4            
1668             #else
1669 9           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  4            
  2            
  3            
1670             #endif
1671 19           uint32_t pos = hash & mask;
  4            
  2            
  3            
  4            
  2            
  4            
1672              
1673 19 50         for (uint32_t i = 0; i <= mask; i++) {
  4 50          
  2 50          
  3 50          
  4 50          
  2 50          
  4            
1674 19           uint32_t idx = (pos + i) & mask;
  4            
  2            
  3            
  4            
  2            
  4            
1675 19 100         if (states[idx] == SHM_EMPTY) return 0;
  4 100          
  2 100          
  3 100          
  4 100          
  2 100          
  4            
1676 13 50         if (states[idx] == SHM_TOMBSTONE) continue;
  3 50          
  1 50          
  2 50          
  3 50          
  1 50          
  3            
1677             #ifdef SHM_KEY_IS_INT
1678 7 50         if (SHM_KEY_EQ(&nodes[idx], key)) {
  3 50          
  1 50          
  3            
1679             #else
1680 6 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  3 50          
  1 50          
  2 50          
    50          
    50          
    50          
    50          
    50          
1681             #endif
1682 13           *out_idx = idx;
  3            
  1            
  2            
  3            
  1            
  3            
1683 13           return 1;
  3            
  1            
  2            
  3            
  1            
  3            
1684             }
1685             }
1686 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
1687             }
1688              
1689 26           static SHM_VAL_INT_TYPE SHM_FN(incr_by)(ShmHandle *h,
  4            
  2            
  3            
  11            
  2            
  4            
1690             #ifdef SHM_KEY_IS_INT
1691             SHM_KEY_INT_TYPE key,
1692             #else
1693             const char *key_str, uint32_t key_len, bool key_utf8,
1694             #endif
1695             SHM_VAL_INT_TYPE delta, int *ok) {
1696 26           ShmHeader *hdr = h->hdr;
  4            
  2            
  3            
  11            
  2            
  4            
1697 26           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  4            
  2            
  3            
  11            
  2            
  4            
1698 26 50         uint32_t now = h->expires_at ? (uint32_t)time(NULL) : 0;
  4 50          
  2 50          
  3 100          
  11 50          
  2 50          
  4            
1699              
1700             /* fast path: existing key under read lock + atomic add (no LRU/TTL) */
1701 26 50         if (!h->lru_prev && !h->expires_at) {
  4 50          
  2 50          
  3 50          
  11 50          
  2 50          
  4 100          
    100          
    50          
    50          
    50          
    50          
1702 19           shm_rwlock_rdlock(hdr);
  4            
  2            
  3            
  4            
  2            
  4            
1703             uint32_t idx;
1704             #ifdef SHM_KEY_IS_INT
1705 10 100         if (SHM_FN(find_slot)(h, key, &idx)) {
  4 100          
  2 100          
  4            
1706             #else
1707 9 100         if (SHM_FN(find_slot)(h, key_str, key_len, key_utf8, &idx)) {
  4 100          
  2 100          
  3            
1708             #endif
1709 13           SHM_VAL_INT_TYPE result =
  3            
  1            
  2            
  3            
  1            
  3            
1710 13           __atomic_add_fetch(&nodes[idx].value, delta, __ATOMIC_ACQ_REL);
  3            
  1            
  2            
  3            
  1            
  3            
1711 13           shm_rwlock_rdunlock(hdr);
  3            
  1            
  2            
  3            
  1            
  3            
1712 13           *ok = 1;
  3            
  1            
  2            
  3            
  1            
  3            
1713 13           return result;
  3            
  1            
  2            
  3            
  1            
  3            
1714             }
1715 6           shm_rwlock_rdunlock(hdr);
  1            
  1            
  1            
  1            
  1            
  1            
1716             }
1717              
1718             /* slow path: find-or-insert under write lock */
1719 13           shm_rwlock_wrlock(hdr);
  1            
  1            
  1            
  8            
  1            
  1            
1720 13           shm_seqlock_write_begin(&hdr->seq);
  1            
  1            
  1            
  8            
  1            
  1            
1721              
1722 13           SHM_FN(maybe_grow)(h);
  1            
  1            
  1            
  8            
  1            
  1            
1723 13           uint32_t mask = hdr->table_cap - 1;
  1            
  1            
  1            
  8            
  1            
  1            
1724             #ifdef SHM_KEY_IS_INT
1725 10           uint32_t hash = SHM_HASH_KEY(key);
  8            
  1            
  1            
1726             #else
1727 3           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  1            
  1            
  1            
1728             #endif
1729 13           uint32_t pos = hash & mask;
  1            
  1            
  1            
  8            
  1            
  1            
1730 13           uint32_t insert_pos = UINT32_MAX;
  1            
  1            
  1            
  8            
  1            
  1            
1731              
1732 13 50         for (uint32_t i = 0; i <= mask; i++) {
  1 50          
  1 50          
  1 50          
  8 50          
  1 50          
  1            
1733 13           uint32_t slot = (pos + i) & mask;
  1            
  1            
  1            
  8            
  1            
  1            
1734 13 50         if (h->states[slot] == SHM_EMPTY) {
  1 50          
  1 50          
  1 100          
  8 50          
  1 50          
  1            
1735 7 50         if (insert_pos == UINT32_MAX) insert_pos = slot;
  1 50          
  1 50          
  1 50          
  2 50          
  1 50          
  1            
1736 7           break;
  1            
  1            
  1            
  2            
  1            
  1            
1737             }
1738 6 0         if (h->states[slot] == SHM_TOMBSTONE) {
  0 0          
  0 0          
  0 50          
  6 0          
  0 0          
  0            
1739 0 0         if (insert_pos == UINT32_MAX) insert_pos = slot;
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
1740 0           continue;
  0            
  0            
  0            
  0            
  0            
  0            
1741             }
1742             #ifdef SHM_KEY_IS_INT
1743 6 50         if (SHM_KEY_EQ(&nodes[slot], key)) {
  6 0          
  0 0          
  0            
1744             #else
1745 0 0         if (SHM_KEY_EQ_STR(&nodes[slot], h->arena, key_str, key_len, key_utf8)) {
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
1746             #endif
1747             /* TTL check */
1748 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          
1749 2           SHM_FN(expire_at)(h, slot);
  0            
  0            
  0            
  2            
  0            
  0            
1750             /* treat as not found — will insert below */
1751 2 0         if (insert_pos == UINT32_MAX) insert_pos = slot;
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
1752 2           break;
  0            
  0            
  0            
  2            
  0            
  0            
1753             }
1754              
1755 4           nodes[slot].value += delta;
  0            
  0            
  0            
  4            
  0            
  0            
1756 4           SHM_VAL_INT_TYPE result = nodes[slot].value;
  0            
  0            
  0            
  4            
  0            
  0            
1757 4 0         if (h->lru_prev) shm_lru_promote(h, slot);
  0 0          
  0 0          
  0 100          
  4 0          
  0 0          
  0            
1758 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          
1759 0           h->expires_at[slot] = shm_expiry_ts(hdr->default_ttl);
  0            
  0            
  0            
  0            
  0            
  0            
1760 4           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  4            
  0            
  0            
1761 4           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  4            
  0            
  0            
1762 4           *ok = 1;
  0            
  0            
  0            
  4            
  0            
  0            
1763 4           return result;
  0            
  0            
  0            
  4            
  0            
  0            
1764             }
1765             }
1766              
1767 9 50         if (insert_pos == UINT32_MAX) {
  1 50          
  1 50          
  1 50          
  4 50          
  1 50          
  1            
1768 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
1769 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
1770 0           *ok = 0;
  0            
  0            
  0            
  0            
  0            
  0            
1771 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
1772             }
1773              
1774             /* LRU eviction only when actually inserting */
1775 9 50         if (hdr->max_size > 0 && hdr->size >= hdr->max_size)
  1 0          
  1 50          
  1 0          
  4 50          
  1 0          
  1 100          
    50          
    50          
    0          
    50          
    0          
1776 0           SHM_FN(lru_evict_one)(h);
  0            
  0            
  0            
  0            
  0            
  0            
1777              
1778 9           int was_tombstone = (h->states[insert_pos] == SHM_TOMBSTONE);
  1            
  1            
  1            
  4            
  1            
  1            
1779             #ifdef SHM_KEY_IS_INT
1780 6           nodes[insert_pos].key = key;
  4            
  1            
  1            
1781             #else
1782 3           uint32_t koff = shm_arena_alloc(hdr, h->arena, key_len);
  1            
  1            
  1            
1783 3 50         if (koff == 0 && key_len > 0) {
  1 0          
  1 50          
  1 0          
    50          
    0          
1784 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
1785 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
1786 0           *ok = 0;
  0            
  0            
  0            
1787 0           return 0;
  0            
  0            
  0            
1788             }
1789 3           memcpy(h->arena + koff, key_str, key_len);
  1            
  1            
  1            
1790 3           nodes[insert_pos].key_off = koff;
  1            
  1            
  1            
1791 3 50         nodes[insert_pos].key_len = SHM_PACK_LEN(key_len, key_utf8);
  1 50          
  1 50          
  1            
1792             #endif
1793 9           nodes[insert_pos].value = delta;
  1            
  1            
  1            
  4            
  1            
  1            
1794 9           h->states[insert_pos] = SHM_LIVE;
  1            
  1            
  1            
  4            
  1            
  1            
1795 9           hdr->size++;
  1            
  1            
  1            
  4            
  1            
  1            
1796 9 50         if (was_tombstone) hdr->tombstones--;
  1 50          
  1 50          
  1 100          
  4 50          
  1 50          
  1            
1797              
1798 9 50         if (h->lru_prev) shm_lru_push_front(h, insert_pos);
  1 50          
  1 50          
  1 100          
  4 50          
  1 50          
  1            
1799 9 50         if (h->expires_at && hdr->default_ttl > 0)
  1 0          
  1 50          
  1 0          
  4 50          
  1 0          
  1 100          
    50          
    50          
    0          
    50          
    0          
1800 2           h->expires_at[insert_pos] = shm_expiry_ts(hdr->default_ttl);
  0            
  0            
  0            
  2            
  0            
  0            
1801              
1802 9           shm_seqlock_write_end(&hdr->seq);
  1            
  1            
  1            
  4            
  1            
  1            
1803 9           shm_rwlock_wrunlock(hdr);
  1            
  1            
  1            
  4            
  1            
  1            
1804 9           *ok = 1;
  1            
  1            
  1            
  4            
  1            
  1            
1805 9           return delta;
  1            
  1            
  1            
  4            
  1            
  1            
1806             }
1807              
1808             #endif /* SHM_HAS_COUNTERS */
1809              
1810             /* ---- Size ---- */
1811              
1812 44           static inline uint32_t SHM_FN(size)(ShmHandle *h) {
  5            
  2            
  0            
  0            
  2            
  0            
  1            
  31            
  2            
  1            
1813 44           return __atomic_load_n(&h->hdr->size, __ATOMIC_ACQUIRE);
  5            
  2            
  0            
  0            
  2            
  0            
  1            
  31            
  2            
  1            
1814             }
1815              
1816             /* ---- Max entries ---- */
1817              
1818 2           static inline uint32_t SHM_FN(max_entries)(ShmHandle *h) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
1819 2           return h->hdr->max_table_cap * 3 / 4;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
1820             }
1821              
1822             /* ---- Accessors ---- */
1823              
1824 4           static inline uint32_t SHM_FN(max_size)(ShmHandle *h) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
1825 4           return h->hdr->max_size;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
1826             }
1827              
1828 4           static inline uint32_t SHM_FN(ttl)(ShmHandle *h) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
1829 4           return h->hdr->default_ttl;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
1830             }
1831              
1832             /* ---- TTL remaining for a key (-1 = not found/expired, 0 = permanent) ---- */
1833              
1834 15           static int64_t SHM_FN(ttl_remaining)(ShmHandle *h,
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  13            
  0            
  0            
1835             #ifdef SHM_KEY_IS_INT
1836             SHM_KEY_INT_TYPE key
1837             #else
1838             const char *key_str, uint32_t key_len, bool key_utf8
1839             #endif
1840             ) {
1841 15 50         if (!h->expires_at) return -1; /* TTL not enabled */
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  13 0          
  0 0          
  0            
1842              
1843 14           ShmHeader *hdr = h->hdr;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
1844 14           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
1845 14           uint8_t *states = h->states;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
1846              
1847 14           shm_rwlock_rdlock(hdr);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
1848              
1849 14           uint32_t mask = hdr->table_cap - 1;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
1850             #ifdef SHM_KEY_IS_INT
1851 12           uint32_t hash = SHM_HASH_KEY(key);
  0            
  0            
  0            
  12            
  0            
  0            
1852             #else
1853 2           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  2            
  0            
  0            
  0            
1854             #endif
1855 14           uint32_t pos = hash & mask;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
1856              
1857 15 50         for (uint32_t i = 0; i <= mask; i++) {
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  12 0          
  0 0          
  0            
1858 15           uint32_t idx = (pos + i) & mask;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  12            
  0            
  0            
1859 15 100         if (states[idx] == SHM_EMPTY) break;
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  12 0          
  0 0          
  0            
1860 13 50         if (states[idx] == SHM_TOMBSTONE) continue;
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  11 0          
  0 0          
  0            
1861             #ifdef SHM_KEY_IS_INT
1862 11 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 0          
  0 50          
  11 0          
  0 0          
  0            
1863             #else
1864 2 100         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  2 50          
  0 50          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1865             #endif
1866 12           uint32_t exp = h->expires_at[idx];
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  11            
  0            
  0            
1867 12 50         if (exp == 0) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  11 0          
  0 0          
  0            
1868 4           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
1869 4           return 0; /* permanent entry */
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
1870             }
1871 8           uint32_t now = (uint32_t)time(NULL);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1872 8 50         if (now >= exp) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  7 0          
  0 0          
  0            
1873 0           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1874 0           return -1; /* expired */
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1875             }
1876 8           int64_t remaining = (int64_t)(exp - now);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1877 8           shm_rwlock_rdunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1878 8           return remaining;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1879             }
1880             }
1881              
1882 2           shm_rwlock_rdunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1883 2           return -1; /* not found */
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1884             }
1885              
1886             /* ---- Stats ---- */
1887              
1888 11           static inline uint32_t SHM_FN(capacity)(ShmHandle *h) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
1889 11           return h->hdr->table_cap;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  10            
  0            
  0            
1890             }
1891              
1892 4           static inline uint32_t SHM_FN(tombstones)(ShmHandle *h) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
1893 4           return h->hdr->tombstones;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
1894             }
1895              
1896 4           static inline size_t SHM_FN(mmap_size)(ShmHandle *h) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1897 4           return h->mmap_size;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
1898             }
1899              
1900             /* ---- Flush expired entries (partial / gradual) ---- */
1901              
1902             /* Scan up to `limit` slots starting from the shared flush_cursor.
1903             * Returns the number of entries actually expired.
1904             * Sets *done_out to 1 if the cursor wrapped around (full cycle complete).
1905             * The wrlock is held only for `limit` slots, not the entire table. */
1906 25           static uint32_t SHM_FN(flush_expired_partial)(ShmHandle *h, uint32_t limit, int *done_out) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  24            
  0            
  0            
1907 25 50         if (!h->expires_at) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  24 0          
  0 0          
  0            
1908 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            
1909 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1910             }
1911 24 50         if (done_out) *done_out = 0;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  23 0          
  0 0          
  0            
1912              
1913 24           ShmHeader *hdr = h->hdr;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  23            
  0            
  0            
1914 24           uint8_t *states = h->states;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  23            
  0            
  0            
1915 24           uint32_t now = (uint32_t)time(NULL);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  23            
  0            
  0            
1916 24           uint32_t flushed = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  23            
  0            
  0            
1917              
1918 24           shm_rwlock_wrlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  23            
  0            
  0            
1919 24           shm_seqlock_write_begin(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  23            
  0            
  0            
1920              
1921 24           uint32_t cap = hdr->table_cap;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  23            
  0            
  0            
1922 24           uint32_t start = hdr->flush_cursor;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  23            
  0            
  0            
1923 24 50         if (start >= cap) start = 0; /* clamp after shrink */
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  23 0          
  0 0          
  0            
1924 24 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          
  23 0          
  0 0          
  0            
1925 24 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          
  23 0          
  0 0          
  0            
1926              
1927 614 100         for (uint32_t n = 0; n < limit; n++) {
  17 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  597 0          
  0 0          
  0            
1928 590           uint32_t i = (start + n) % cap;
  16            
  0            
  0            
  0            
  0            
  0            
  0            
  574            
  0            
  0            
1929 590 100         if (states[i] == SHM_LIVE && h->expires_at[i] != 0 && now >= h->expires_at[i]) {
  16 50          
  0 50          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  574 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
1930 166           SHM_FN(expire_at)(h, i);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  164            
  0            
  0            
1931 166           flushed++;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  164            
  0            
  0            
1932             }
1933             }
1934              
1935 24           uint32_t next = (start + limit) % cap;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  23            
  0            
  0            
1936             /* Full cycle complete when this chunk crossed the end of the table */
1937 24 50         int done = (limit >= cap || start + limit >= cap);
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  23 0          
  0 0          
  0 0          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
    0          
    0          
1938 24 50         hdr->flush_cursor = done ? 0 : next;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  23 0          
  0 0          
  0            
1939              
1940 24 50         if (done_out) *done_out = done;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  23 0          
  0 0          
  0            
1941              
1942             /* Only shrink/compact when a full cycle is done, otherwise the rehash
1943             * can move entries to slots the cursor already passed. */
1944 24 50         if (done && flushed > 0)
  1 50          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  23 0          
  0 0          
  0 0          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
    0          
    0          
1945 8           SHM_FN(maybe_shrink)(h);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1946              
1947 24           shm_seqlock_write_end(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  23            
  0            
  0            
1948 24           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  23            
  0            
  0            
1949 24           return flushed;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  23            
  0            
  0            
1950             }
1951              
1952             /* Convenience: full scan in one call (original behavior). */
1953 7           static uint32_t SHM_FN(flush_expired)(ShmHandle *h) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
1954 7 50         if (!h->expires_at) return 0;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  6 0          
  0 0          
  0            
1955             int done;
1956 6           return SHM_FN(flush_expired_partial)(h, UINT32_MAX, &done);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
1957             }
1958              
1959             /* ---- Touch (refresh TTL / promote LRU without changing value) ---- */
1960              
1961 11           static int SHM_FN(touch)(ShmHandle *h,
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
1962             #ifdef SHM_KEY_IS_INT
1963             SHM_KEY_INT_TYPE key
1964             #else
1965             const char *key_str, uint32_t key_len, bool key_utf8
1966             #endif
1967             ) {
1968 11 50         if (!h->lru_prev && !h->expires_at) return 0; /* nothing to do */
  2 50          
  1 50          
  0 50          
  0 0          
  0 0          
  0 0          
  0 0          
  8 0          
  0 0          
  0 0          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
    0          
    0          
1969              
1970 10           ShmHeader *hdr = h->hdr;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1971 10           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1972 10           uint8_t *states = h->states;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1973 10 50         uint32_t now = h->expires_at ? (uint32_t)time(NULL) : 0;
  2 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  7 0          
  0 0          
  0            
1974              
1975 10           shm_rwlock_wrlock(hdr);
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1976              
1977 10           uint32_t mask = hdr->table_cap - 1;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1978             #ifdef SHM_KEY_IS_INT
1979 7           uint32_t hash = SHM_HASH_KEY(key);
  0            
  0            
  0            
  7            
  0            
  0            
1980             #else
1981 3           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  2            
  1            
  0            
  0            
1982             #endif
1983 10           uint32_t pos = hash & mask;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1984              
1985 10 50         for (uint32_t i = 0; i <= mask; i++) {
  2 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  7 0          
  0 0          
  0            
1986 10           uint32_t idx = (pos + i) & mask;
  2            
  1            
  0            
  0            
  0            
  0            
  0            
  7            
  0            
  0            
1987 10 100         if (states[idx] == SHM_EMPTY) break;
  2 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  7 0          
  0 0          
  0            
1988 8 50         if (states[idx] == SHM_TOMBSTONE) continue;
  1 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  6 0          
  0 0          
  0            
1989             #ifdef SHM_KEY_IS_INT
1990 6 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 0          
  0 50          
  6 0          
  0 0          
  0            
1991             #else
1992 2 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  1 50          
  1 50          
  0 50          
  0 50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
1993             #endif
1994 8 50         if (SHM_IS_EXPIRED(h, idx, now)) {
  1 50          
  1 50          
  0 50          
  0 50          
  0 50          
  0 0          
  0 0          
  6 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
1995 1           shm_seqlock_write_begin(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1996 1           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1997 1           SHM_FN(maybe_shrink)(h);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1998 1           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
1999 1           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2000 1           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2001             }
2002 7           shm_seqlock_write_begin(&hdr->seq);
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
2003 7 50         if (h->lru_prev) shm_lru_promote(h, idx);
  1 50          
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  5 0          
  0 0          
  0            
2004 7 50         if (h->expires_at && hdr->default_ttl > 0 && h->expires_at[idx] != 0) {
  1 50          
  1 50          
  0 50          
  0 50          
  0 50          
  0 0          
  0 0          
  5 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
2005 5           h->expires_at[idx] = shm_expiry_ts(hdr->default_ttl);
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
2006             }
2007 7           shm_seqlock_write_end(&hdr->seq);
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
2008 7           shm_rwlock_wrunlock(hdr);
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
2009 7           return 1;
  1            
  1            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
2010             }
2011             }
2012              
2013 2           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2014 2           return 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2015             }
2016              
2017             /* ---- Reserve (pre-grow table to target capacity) ---- */
2018              
2019 5           static int SHM_FN(reserve)(ShmHandle *h, uint32_t target) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2020 5           ShmHeader *hdr = h->hdr;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  4            
  0            
  0            
2021 5 50         if (target > hdr->max_table_cap) return 0;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  4 0          
  0 0          
  0            
2022             /* compute min capacity for target entries at <75% load */
2023 4           uint32_t needed = shm_next_pow2(target + target / 3 + 1);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  3            
  0            
  0            
2024 4 50         if (needed < SHM_INITIAL_CAP) needed = SHM_INITIAL_CAP;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0            
2025 4 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          
  3 0          
  0 0          
  0            
2026 3 50         if (needed > hdr->max_table_cap) return 0; /* exceeds max */
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
2027              
2028 3           shm_rwlock_wrlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2029 3           shm_seqlock_write_begin(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2030 3           int ok = 1;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2031 3 50         if (needed > hdr->table_cap)
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0            
2032 3           ok = SHM_FN(resize)(h, needed);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2033 3           shm_seqlock_write_end(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2034 3           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2035 3           return ok;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2036             }
2037              
2038             /* ---- Cache stats accessors ---- */
2039              
2040 9           static inline uint64_t SHM_FN(stat_evictions)(ShmHandle *h) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
2041 9           return __atomic_load_n(&h->hdr->stat_evictions, __ATOMIC_RELAXED);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  8            
  0            
  0            
2042             }
2043              
2044 5           static inline uint64_t SHM_FN(stat_expired)(ShmHandle *h) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
2045 5           return __atomic_load_n(&h->hdr->stat_expired, __ATOMIC_RELAXED);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  0            
  0            
2046             }
2047              
2048 1           static inline uint32_t SHM_FN(stat_recoveries)(ShmHandle *h) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2049 1           return __atomic_load_n(&h->hdr->stat_recoveries, __ATOMIC_RELAXED);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2050             }
2051              
2052             /* ---- Clear ---- */
2053              
2054 8           static void SHM_FN(clear)(ShmHandle *h) {
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  1            
  1            
2055 8           ShmHeader *hdr = h->hdr;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  1            
  1            
2056              
2057 8           shm_rwlock_wrlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  1            
  1            
2058 8           shm_seqlock_write_begin(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  1            
  1            
2059              
2060 8           memset(h->states, SHM_EMPTY, hdr->table_cap);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  1            
  1            
2061 8           hdr->size = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  1            
  1            
2062 8           hdr->tombstones = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  1            
  1            
2063              
2064             /* shrink back to initial capacity */
2065 8 50         if (hdr->table_cap > SHM_INITIAL_CAP) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  5 50          
  1 50          
  1            
2066 2           uint32_t old_cap = hdr->table_cap;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2067 2           hdr->table_cap = SHM_INITIAL_CAP;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2068 2           size_t node_shrink = (size_t)(old_cap - SHM_INITIAL_CAP) * sizeof(SHM_NODE_TYPE);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2069 2           madvise((char *)h->nodes + (size_t)SHM_INITIAL_CAP * sizeof(SHM_NODE_TYPE),
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2070             node_shrink, MADV_DONTNEED);
2071 2           madvise(h->states + SHM_INITIAL_CAP, old_cap - SHM_INITIAL_CAP, MADV_DONTNEED);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2072             }
2073              
2074             /* reset arena */
2075 8 50         if (h->arena) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  5 50          
  1 50          
  1            
2076 1           hdr->arena_bump = SHM_ARENA_MIN_ALLOC;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2077 1           memset(hdr->arena_free, 0, sizeof(hdr->arena_free));
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2078 1           madvise(h->arena, (size_t)hdr->arena_cap, MADV_DONTNEED);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2079             }
2080              
2081             /* reset LRU (full max_table_cap range) */
2082 8 50         if (h->lru_prev) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  5 50          
  1 50          
  1            
2083 2           memset(h->lru_prev, 0xFF, hdr->max_table_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2084 2           memset(h->lru_next, 0xFF, hdr->max_table_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2085 2           hdr->lru_head = SHM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2086 2           hdr->lru_tail = SHM_LRU_NONE;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  2            
  0            
  0            
2087             }
2088              
2089             /* reset TTL (full max_table_cap range) */
2090 8 50         if (h->expires_at) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  5 50          
  1 50          
  1            
2091 0           memset(h->expires_at, 0, hdr->max_table_cap * sizeof(uint32_t));
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2092 0           hdr->flush_cursor = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2093             }
2094              
2095 8           hdr->table_gen++;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  1            
  1            
2096              
2097 8           shm_seqlock_write_end(&hdr->seq);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  1            
  1            
2098 8           shm_rwlock_wrunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  1            
  1            
2099              
2100 8           h->iter_pos = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  1            
  1            
2101 8 50         if (h->iter_active) {
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  5 50          
  1 50          
  1            
2102 1           h->iter_active = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2103 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            
2104             }
2105 8           h->deferred = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  1            
  1            
2106 8           }
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  5            
  1            
  1            
2107              
2108             /* ---- Iterator reset ---- */
2109              
2110 1           static inline void SHM_FN(iter_reset)(ShmHandle *h) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2111 1 0         if (h->iter_active) {
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0            
2112 1           h->iter_active = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2113 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            
2114             }
2115 1           h->iter_pos = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2116 1           }
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2117              
2118             /* ---- Get or set (atomic: lookup + insert under single wrlock) ---- */
2119              
2120 17           static int SHM_FN(get_or_set)(ShmHandle *h,
  4            
  0            
  0            
  2            
  0            
  0            
  2            
  5            
  2            
  2            
2121             #ifdef SHM_KEY_IS_INT
2122             SHM_KEY_INT_TYPE key,
2123             #else
2124             const char *key_str, uint32_t key_len, bool key_utf8,
2125             #endif
2126             #ifdef SHM_VAL_IS_STR
2127             const char *def_str, uint32_t def_len, bool def_utf8,
2128             const char **out_str, uint32_t *out_len, bool *out_utf8
2129             #else
2130             SHM_VAL_INT_TYPE def_value,
2131             SHM_VAL_INT_TYPE *out_value
2132             #endif
2133             ) {
2134 17           ShmHeader *hdr = h->hdr;
  4            
  0            
  0            
  2            
  0            
  0            
  2            
  5            
  2            
  2            
2135 17           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  4            
  0            
  0            
  2            
  0            
  0            
  2            
  5            
  2            
  2            
2136 17           uint8_t *states = h->states;
  4            
  0            
  0            
  2            
  0            
  0            
  2            
  5            
  2            
  2            
2137 17 50         uint32_t now = h->expires_at ? (uint32_t)time(NULL) : 0;
  4 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0 50          
  2 100          
  5 50          
  2 50          
  2            
2138              
2139 17           shm_rwlock_wrlock(hdr);
  4            
  0            
  0            
  2            
  0            
  0            
  2            
  5            
  2            
  2            
2140 17           shm_seqlock_write_begin(&hdr->seq);
  4            
  0            
  0            
  2            
  0            
  0            
  2            
  5            
  2            
  2            
2141              
2142 17           SHM_FN(maybe_grow)(h);
  4            
  0            
  0            
  2            
  0            
  0            
  2            
  5            
  2            
  2            
2143 17           uint32_t mask = hdr->table_cap - 1;
  4            
  0            
  0            
  2            
  0            
  0            
  2            
  5            
  2            
  2            
2144             #ifdef SHM_KEY_IS_INT
2145 11           uint32_t hash = SHM_HASH_KEY(key);
  0            
  0            
  2            
  5            
  2            
  2            
2146             #else
2147 6           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
  4            
  0            
  0            
  2            
2148             #endif
2149 17           uint32_t pos = hash & mask;
  4            
  0            
  0            
  2            
  0            
  0            
  2            
  5            
  2            
  2            
2150 17           uint32_t insert_pos = UINT32_MAX;
  4            
  0            
  0            
  2            
  0            
  0            
  2            
  5            
  2            
  2            
2151              
2152 17 50         for (uint32_t i = 0; i <= mask; i++) {
  4 0          
  0 0          
  0 50          
  2 0          
  0 0          
  0 50          
  2 50          
  5 50          
  2 50          
  2            
2153 17           uint32_t idx = (pos + i) & mask;
  4            
  0            
  0            
  2            
  0            
  0            
  2            
  5            
  2            
  2            
2154              
2155 17 100         if (states[idx] == SHM_EMPTY) {
  4 0          
  0 0          
  0 100          
  2 0          
  0 0          
  0 100          
  2 100          
  5 100          
  2 100          
  2            
2156 7 50         if (insert_pos == UINT32_MAX) insert_pos = idx;
  2 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  1 50          
  1 50          
  1            
2157 7           break;
  2            
  0            
  0            
  1            
  0            
  0            
  1            
  1            
  1            
  1            
2158             }
2159 10 50         if (states[idx] == SHM_TOMBSTONE) {
  2 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  4 50          
  1 50          
  1            
2160 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            
2161 0           continue;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2162             }
2163             #ifdef SHM_KEY_IS_INT
2164 7 0         if (SHM_KEY_EQ(&nodes[idx], key)) {
  0 0          
  0 50          
  1 50          
  4 50          
  1 50          
  1            
2165             #else
2166 3 50         if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
  2 50          
  0 50          
  0 0          
  1 0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
2167             #endif
2168             /* TTL check */
2169 10 50         if (SHM_IS_EXPIRED(h, idx, now)) {
  2 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          
    100          
    100          
    50          
    0          
    0          
    50          
    0          
    0          
2170 1           SHM_FN(expire_at)(h, idx);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2171 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            
2172 1           break;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2173             }
2174              
2175             #ifdef SHM_VAL_IS_STR
2176             {
2177 3           uint32_t vl = SHM_UNPACK_LEN(nodes[idx].val_len);
  2            
  0            
  0            
  1            
2178 3 50         if (!shm_ensure_copy_buf(h, vl)) {
  2 0          
  0 0          
  0 50          
  1            
2179 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2180 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2181 0           return 0;
  0            
  0            
  0            
  0            
2182             }
2183 3           memcpy(h->copy_buf, h->arena + nodes[idx].val_off, vl);
  2            
  0            
  0            
  1            
2184 3           *out_str = h->copy_buf;
  2            
  0            
  0            
  1            
2185 3           *out_len = vl;
  2            
  0            
  0            
  1            
2186 3           *out_utf8 = SHM_UNPACK_UTF8(nodes[idx].val_len);
  2            
  0            
  0            
  1            
2187             }
2188             #else
2189 6           *out_value = nodes[idx].value;
  0            
  0            
  1            
  3            
  1            
  1            
2190             #endif
2191 9 50         if (h->lru_prev) shm_lru_promote(h, idx);
  2 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  3 50          
  1 50          
  1            
2192 9 50         if (h->expires_at && hdr->default_ttl > 0 && h->expires_at[idx] != 0)
  2 0          
  0 0          
  0 0          
  1 0          
  0 0          
  0 0          
  1 0          
  3 0          
  1 50          
  1 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    100          
    50          
    100          
    50          
    0          
    0          
    50          
    0          
    0          
2193 1           h->expires_at[idx] = shm_expiry_ts(hdr->default_ttl);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2194 9           shm_seqlock_write_end(&hdr->seq);
  2            
  0            
  0            
  1            
  0            
  0            
  1            
  3            
  1            
  1            
2195 9           shm_rwlock_wrunlock(hdr);
  2            
  0            
  0            
  1            
  0            
  0            
  1            
  3            
  1            
  1            
2196 9           return 1;
  2            
  0            
  0            
  1            
  0            
  0            
  1            
  3            
  1            
  1            
2197             }
2198             }
2199              
2200             /* not found — insert default value */
2201 8 50         if (insert_pos == UINT32_MAX) {
  2 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  2 50          
  1 50          
  1            
2202 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2203 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2204 0           return 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2205             }
2206              
2207             /* LRU eviction only when actually inserting a new entry */
2208 8 50         if (hdr->max_size > 0 && hdr->size >= hdr->max_size)
  2 0          
  0 0          
  0 0          
  1 0          
  0 0          
  0 50          
  1 0          
  2 0          
  1 0          
  1 0          
    0          
    50          
    0          
    50          
    0          
    50          
    0          
    50          
    0          
2209 0           SHM_FN(lru_evict_one)(h);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2210              
2211 8           int was_tombstone = (states[insert_pos] == SHM_TOMBSTONE);
  2            
  0            
  0            
  1            
  0            
  0            
  1            
  2            
  1            
  1            
2212              
2213             #ifdef SHM_KEY_IS_INT
2214 5           nodes[insert_pos].key = key;
  0            
  0            
  1            
  2            
  1            
  1            
2215             #else
2216 3           uint32_t koff = shm_arena_alloc(hdr, h->arena, key_len);
  2            
  0            
  0            
  1            
2217 3 50         if (koff == 0 && key_len > 0) {
  2 0          
  0 0          
  0 0          
  1 0          
    0          
    50          
    0          
2218 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2219 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2220 0           return 0;
  0            
  0            
  0            
  0            
2221             }
2222 3           memcpy(h->arena + koff, key_str, key_len);
  2            
  0            
  0            
  1            
2223 3           nodes[insert_pos].key_off = koff;
  2            
  0            
  0            
  1            
2224 3 50         nodes[insert_pos].key_len = SHM_PACK_LEN(key_len, key_utf8);
  2 0          
  0 0          
  0 50          
  1            
2225             #endif
2226              
2227             #ifdef SHM_VAL_IS_STR
2228 3 50         if (!shm_ensure_copy_buf(h, def_len > 0 ? def_len : 1)) {
  2 50          
  0 0          
  0 0          
  1 0          
    0          
    50          
    50          
2229             #ifndef SHM_KEY_IS_INT
2230 0           shm_arena_free_block(hdr, h->arena, koff, key_len);
  0            
2231             #endif
2232 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2233 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2234 0           return 0;
  0            
  0            
  0            
  0            
2235             }
2236 3           uint32_t voff = shm_arena_alloc(hdr, h->arena, def_len);
  2            
  0            
  0            
  1            
2237 3 50         if (voff == 0 && def_len > 0) {
  2 0          
  0 0          
  0 0          
  1 0          
    0          
    50          
    0          
2238             #ifndef SHM_KEY_IS_INT
2239 0           shm_arena_free_block(hdr, h->arena, koff, key_len);
  0            
2240             #endif
2241 0           shm_seqlock_write_end(&hdr->seq);
  0            
  0            
  0            
  0            
2242 0           shm_rwlock_wrunlock(hdr);
  0            
  0            
  0            
  0            
2243 0           return 0;
  0            
  0            
  0            
  0            
2244             }
2245 3           memcpy(h->arena + voff, def_str, def_len);
  2            
  0            
  0            
  1            
2246 3           nodes[insert_pos].val_off = voff;
  2            
  0            
  0            
  1            
2247 3 50         nodes[insert_pos].val_len = SHM_PACK_LEN(def_len, def_utf8);
  2 0          
  0 0          
  0 50          
  1            
2248             #else
2249 5           nodes[insert_pos].value = def_value;
  0            
  0            
  1            
  2            
  1            
  1            
2250             #endif
2251              
2252 8           states[insert_pos] = SHM_LIVE;
  2            
  0            
  0            
  1            
  0            
  0            
  1            
  2            
  1            
  1            
2253 8           hdr->size++;
  2            
  0            
  0            
  1            
  0            
  0            
  1            
  2            
  1            
  1            
2254 8 50         if (was_tombstone) hdr->tombstones--;
  2 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 100          
  2 50          
  1 50          
  1            
2255              
2256 8 50         if (h->lru_prev) shm_lru_push_front(h, insert_pos);
  2 0          
  0 0          
  0 50          
  1 0          
  0 0          
  0 50          
  1 50          
  2 50          
  1 50          
  1            
2257 8 50         if (h->expires_at && hdr->default_ttl > 0)
  2 0          
  0 0          
  0 0          
  1 0          
  0 0          
  0 50          
  1 0          
  2 0          
  1 0          
  1 0          
    0          
    50          
    0          
    100          
    50          
    50          
    0          
    50          
    0          
2258 1           h->expires_at[insert_pos] = shm_expiry_ts(hdr->default_ttl);
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2259              
2260             #ifdef SHM_VAL_IS_STR
2261 3           memcpy(h->copy_buf, def_str, def_len);
  2            
  0            
  0            
  1            
2262 3           *out_str = h->copy_buf;
  2            
  0            
  0            
  1            
2263 3           *out_len = def_len;
  2            
  0            
  0            
  1            
2264 3           *out_utf8 = def_utf8;
  2            
  0            
  0            
  1            
2265             #else
2266 5           *out_value = def_value;
  0            
  0            
  1            
  2            
  1            
  1            
2267             #endif
2268 8           shm_seqlock_write_end(&hdr->seq);
  2            
  0            
  0            
  1            
  0            
  0            
  1            
  2            
  1            
  1            
2269 8           shm_rwlock_wrunlock(hdr);
  2            
  0            
  0            
  1            
  0            
  0            
  1            
  2            
  1            
  1            
2270 8           return 2; /* 2 = inserted new */
  2            
  0            
  0            
  1            
  0            
  0            
  1            
  2            
  1            
  1            
2271             }
2272              
2273             /* ---- Each (iterator) ---- */
2274              
2275 328           static int SHM_FN(each)(ShmHandle *h,
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  325            
  0            
  0            
2276             #ifdef SHM_KEY_IS_INT
2277             SHM_KEY_INT_TYPE *out_key,
2278             #else
2279             const char **out_key_str, uint32_t *out_key_len, bool *out_key_utf8,
2280             #endif
2281             #ifdef SHM_VAL_IS_STR
2282             const char **out_val_str, uint32_t *out_val_len, bool *out_val_utf8
2283             #else
2284             SHM_VAL_INT_TYPE *out_value
2285             #endif
2286             ) {
2287 328           ShmHeader *hdr = h->hdr;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  325            
  0            
  0            
2288 328           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  325            
  0            
  0            
2289 328           uint8_t *states = h->states;
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  325            
  0            
  0            
2290 328 50         uint32_t now = h->expires_at ? (uint32_t)time(NULL) : 0;
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  325 0          
  0 0          
  0            
2291              
2292 328           shm_rwlock_rdlock(hdr);
  3            
  0            
  0            
  0            
  0            
  0            
  0            
  325            
  0            
  0            
2293              
2294 328 100         if (!h->iter_active) {
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  325 0          
  0 0          
  0            
2295 10           h->iter_active = 1;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  9            
  0            
  0            
2296 10           h->iter_gen = hdr->table_gen;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  9            
  0            
  0            
2297 10           h->iterating++;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  9            
  0            
  0            
2298             }
2299              
2300             /* Auto-reset on cross-process resize */
2301 328 50         if (h->iter_gen != hdr->table_gen) {
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  325 0          
  0 0          
  0            
2302 0           h->iter_pos = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2303 0           h->iter_gen = hdr->table_gen;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2304             }
2305              
2306 877 100         while (h->iter_pos < hdr->table_cap) {
  17 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  860 0          
  0 0          
  0            
2307 870           uint32_t pos = h->iter_pos++;
  16            
  0            
  0            
  0            
  0            
  0            
  0            
  854            
  0            
  0            
2308 870 100         if (states[pos] == SHM_LIVE) {
  16 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 100          
  854 0          
  0 0          
  0            
2309             /* skip expired entries */
2310 321 50         if (SHM_IS_EXPIRED(h, pos, now))
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  319 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
2311 0           continue;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2312              
2313             #ifdef SHM_KEY_IS_INT
2314 319           *out_key = nodes[pos].key;
  0            
  0            
  0            
  319            
  0            
  0            
2315             #else
2316             {
2317 2           uint32_t kl = SHM_UNPACK_LEN(nodes[pos].key_len);
  2            
  0            
  0            
  0            
2318             #ifdef SHM_VAL_IS_STR
2319 2           uint32_t vl = SHM_UNPACK_LEN(nodes[pos].val_len);
  2            
2320 2           uint64_t total64 = (uint64_t)kl + vl;
  2            
2321 2 50         if (total64 > UINT32_MAX) {
  2            
2322 0           h->iter_pos = 0;
  0            
2323 0           h->iter_active = 0;
  0            
2324 0 0         if (h->iterating > 0) h->iterating--;
  0            
2325 0           shm_rwlock_rdunlock(hdr);
  0            
2326 0           return 0;
  0            
2327             }
2328 2           uint32_t total = (uint32_t)total64;
  2            
2329             #else
2330 0           uint32_t total = kl;
  0            
  0            
  0            
2331             #endif
2332 2 50         if (!shm_ensure_copy_buf(h, total)) {
  2 0          
  0 0          
  0 0          
  0            
2333 0           h->iter_pos = 0;
  0            
  0            
  0            
  0            
2334 0           h->iter_active = 0;
  0            
  0            
  0            
  0            
2335 0 0         if (h->iterating > 0) h->iterating--;
  0 0          
  0 0          
  0 0          
  0            
2336 0           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
2337 0           return 0;
  0            
  0            
  0            
  0            
2338             }
2339 2           memcpy(h->copy_buf, h->arena + nodes[pos].key_off, kl);
  2            
  0            
  0            
  0            
2340 2           *out_key_str = h->copy_buf;
  2            
  0            
  0            
  0            
2341 2           *out_key_len = kl;
  2            
  0            
  0            
  0            
2342 2           *out_key_utf8 = SHM_UNPACK_UTF8(nodes[pos].key_len);
  2            
  0            
  0            
  0            
2343             }
2344             #endif
2345             #ifdef SHM_VAL_IS_STR
2346             {
2347 2           uint32_t vl = SHM_UNPACK_LEN(nodes[pos].val_len);
  2            
  0            
  0            
  0            
2348             #ifndef SHM_KEY_IS_INT
2349 2           uint32_t kl = SHM_UNPACK_LEN(nodes[pos].key_len);
  2            
2350 2           memcpy(h->copy_buf + kl, h->arena + nodes[pos].val_off, vl);
  2            
2351 2           *out_val_str = h->copy_buf + kl;
  2            
2352             #else
2353 0 0         if (!shm_ensure_copy_buf(h, vl)) {
  0 0          
  0 0          
  0            
2354 0           h->iter_pos = 0;
  0            
  0            
  0            
2355 0           h->iter_active = 0;
  0            
  0            
  0            
2356 0 0         if (h->iterating > 0) h->iterating--;
  0 0          
  0 0          
  0            
2357 0           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
2358 0           return 0;
  0            
  0            
  0            
2359             }
2360 0           memcpy(h->copy_buf, h->arena + nodes[pos].val_off, vl);
  0            
  0            
  0            
2361 0           *out_val_str = h->copy_buf;
  0            
  0            
  0            
2362             #endif
2363 2           *out_val_len = vl;
  2            
  0            
  0            
  0            
2364 2           *out_val_utf8 = SHM_UNPACK_UTF8(nodes[pos].val_len);
  2            
  0            
  0            
  0            
2365             }
2366             #else
2367 319           *out_value = nodes[pos].value;
  0            
  0            
  0            
  319            
  0            
  0            
2368             #endif
2369 321           shm_rwlock_rdunlock(hdr);
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  319            
  0            
  0            
2370 321           return 1;
  2            
  0            
  0            
  0            
  0            
  0            
  0            
  319            
  0            
  0            
2371             }
2372             }
2373              
2374 7           h->iter_pos = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
2375 7           h->iter_active = 0;
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
2376 7 50         if (h->iterating > 0) h->iterating--;
  1 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 50          
  6 0          
  0 0          
  0            
2377 7           shm_rwlock_rdunlock(hdr);
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
2378 7           return 0; /* caller should call flush_deferred */
  1            
  0            
  0            
  0            
  0            
  0            
  0            
  6            
  0            
  0            
2379             }
2380              
2381             /* ---- Cursor iteration ---- */
2382              
2383 104           static int SHM_FN(cursor_next)(ShmCursor *c,
  7            
  6            
  0            
  0            
  0            
  0            
  3            
  88            
  0            
  0            
2384             #ifdef SHM_KEY_IS_INT
2385             SHM_KEY_INT_TYPE *out_key,
2386             #else
2387             const char **out_key_str, uint32_t *out_key_len, bool *out_key_utf8,
2388             #endif
2389             #ifdef SHM_VAL_IS_STR
2390             const char **out_val_str, uint32_t *out_val_len, bool *out_val_utf8
2391             #else
2392             SHM_VAL_INT_TYPE *out_value
2393             #endif
2394             ) {
2395 104           ShmHandle *h = c->handle;
  7            
  6            
  0            
  0            
  0            
  0            
  3            
  88            
  0            
  0            
2396 104           ShmHeader *hdr = h->hdr;
  7            
  6            
  0            
  0            
  0            
  0            
  3            
  88            
  0            
  0            
2397 104           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
  7            
  6            
  0            
  0            
  0            
  0            
  3            
  88            
  0            
  0            
2398 104           uint8_t *states = h->states;
  7            
  6            
  0            
  0            
  0            
  0            
  3            
  88            
  0            
  0            
2399 104 50         uint32_t now = h->expires_at ? (uint32_t)time(NULL) : 0;
  7 50          
  6 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3 50          
  88 0          
  0 0          
  0            
2400              
2401 104           shm_rwlock_rdlock(hdr);
  7            
  6            
  0            
  0            
  0            
  0            
  3            
  88            
  0            
  0            
2402              
2403             /* Auto-reset on cross-process resize */
2404 104 50         if (c->gen != hdr->table_gen) {
  7 50          
  6 0          
  0 0          
  0 0          
  0 0          
  0 50          
  3 50          
  88 0          
  0 0          
  0            
2405 0           c->iter_pos = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2406 0           c->gen = hdr->table_gen;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2407             }
2408              
2409 307 100         while (c->iter_pos < hdr->table_cap) {
  18 100          
  17 0          
  0 0          
  0 0          
  0 0          
  0 100          
  17 100          
  255 0          
  0 0          
  0            
2410 296           uint32_t pos = c->iter_pos++;
  17            
  16            
  0            
  0            
  0            
  0            
  16            
  247            
  0            
  0            
2411 296 100         if (states[pos] == SHM_LIVE) {
  17 100          
  16 0          
  0 0          
  0 0          
  0 0          
  0 100          
  16 100          
  247 0          
  0 0          
  0            
2412 93 50         if (SHM_IS_EXPIRED(h, pos, now))
  6 0          
  5 0          
  0 50          
  0 0          
  0 0          
  0 0          
  2 0          
  80 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
2413 0           continue;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2414              
2415             #ifdef SHM_KEY_IS_INT
2416 82           *out_key = nodes[pos].key;
  0            
  0            
  2            
  80            
  0            
  0            
2417             #else
2418             {
2419 11           uint32_t kl = SHM_UNPACK_LEN(nodes[pos].key_len);
  6            
  5            
  0            
  0            
2420             #ifdef SHM_VAL_IS_STR
2421 6           uint32_t vl = SHM_UNPACK_LEN(nodes[pos].val_len);
  6            
2422 6           uint64_t total64 = (uint64_t)kl + vl;
  6            
2423 6 50         if (total64 > UINT32_MAX) {
  6            
2424 0           shm_rwlock_rdunlock(hdr);
  0            
2425 0           return 0;
  0            
2426             }
2427 6           uint32_t total = (uint32_t)total64;
  6            
2428             #else
2429 5           uint32_t total = kl;
  5            
  0            
  0            
2430             #endif
2431 11 50         if (!shm_cursor_ensure_copy_buf(c, total)) {
  6 50          
  5 0          
  0 0          
  0            
2432 0           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
  0            
2433 0           return 0;
  0            
  0            
  0            
  0            
2434             }
2435 11           memcpy(c->copy_buf, h->arena + nodes[pos].key_off, kl);
  6            
  5            
  0            
  0            
2436 11           *out_key_str = c->copy_buf;
  6            
  5            
  0            
  0            
2437 11           *out_key_len = kl;
  6            
  5            
  0            
  0            
2438 11           *out_key_utf8 = SHM_UNPACK_UTF8(nodes[pos].key_len);
  6            
  5            
  0            
  0            
2439             }
2440             #endif
2441             #ifdef SHM_VAL_IS_STR
2442             {
2443 8           uint32_t vl = SHM_UNPACK_LEN(nodes[pos].val_len);
  6            
  0            
  0            
  2            
2444             #ifndef SHM_KEY_IS_INT
2445 6           uint32_t kl = SHM_UNPACK_LEN(nodes[pos].key_len);
  6            
2446 6           memcpy(c->copy_buf + kl, h->arena + nodes[pos].val_off, vl);
  6            
2447 6           *out_val_str = c->copy_buf + kl;
  6            
2448             #else
2449 2 0         if (!shm_cursor_ensure_copy_buf(c, vl)) {
  0 0          
  0 50          
  2            
2450 0           shm_rwlock_rdunlock(hdr);
  0            
  0            
  0            
2451 0           return 0;
  0            
  0            
  0            
2452             }
2453 2           memcpy(c->copy_buf, h->arena + nodes[pos].val_off, vl);
  0            
  0            
  2            
2454 2           *out_val_str = c->copy_buf;
  0            
  0            
  2            
2455             #endif
2456 8           *out_val_len = vl;
  6            
  0            
  0            
  2            
2457 8           *out_val_utf8 = SHM_UNPACK_UTF8(nodes[pos].val_len);
  6            
  0            
  0            
  2            
2458             }
2459             #else
2460 85           *out_value = nodes[pos].value;
  5            
  0            
  0            
  80            
  0            
  0            
2461             #endif
2462 93           shm_rwlock_rdunlock(hdr);
  6            
  5            
  0            
  0            
  0            
  0            
  2            
  80            
  0            
  0            
2463 93           return 1;
  6            
  5            
  0            
  0            
  0            
  0            
  2            
  80            
  0            
  0            
2464             }
2465             }
2466              
2467 11           c->iter_pos = 0;
  1            
  1            
  0            
  0            
  0            
  0            
  1            
  8            
  0            
  0            
2468 11           shm_rwlock_rdunlock(hdr);
  1            
  1            
  0            
  0            
  0            
  0            
  1            
  8            
  0            
  0            
2469 11           return 0;
  1            
  1            
  0            
  0            
  0            
  0            
  1            
  8            
  0            
  0            
2470             }
2471              
2472 1           static inline void SHM_FN(cursor_reset)(ShmCursor *c) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2473 1           c->iter_pos = 0;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2474 1           c->gen = c->handle->hdr->table_gen;
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2475 1           }
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  1            
  0            
  0            
2476              
2477             /* Position cursor at the slot of a specific key. Returns 1 if found, 0 if not.
2478             Next cursor_next call will return this key's entry, then continue forward. */
2479 5           static int SHM_FN(cursor_seek)(ShmCursor *c,
2480             #ifdef SHM_KEY_IS_INT
2481             SHM_KEY_INT_TYPE key
2482             #else
2483             const char *key_str, uint32_t key_len, bool key_utf8
2484             #endif
2485             ) {
2486 5           ShmHandle *h = c->handle;
2487 5           ShmHeader *hdr = h->hdr;
2488 5           SHM_NODE_TYPE *nodes = (SHM_NODE_TYPE *)h->nodes;
2489 5           uint8_t *states = h->states;
2490 5           uint32_t now = h->expires_at ? (uint32_t)time(NULL) : 0;
2491              
2492 5           shm_rwlock_rdlock(hdr);
2493              
2494 5           uint32_t mask = hdr->table_cap - 1;
2495             #ifdef SHM_KEY_IS_INT
2496 4           uint32_t hash = SHM_HASH_KEY(key);
2497             #else
2498 1           uint32_t hash = SHM_HASH_KEY_STR(key_str, key_len);
2499             #endif
2500 5           uint32_t pos = hash & mask;
2501              
2502 6           for (uint32_t i = 0; i <= mask; i++) {
2503 6           uint32_t idx = (pos + i) & mask;
2504 6           if (states[idx] == SHM_EMPTY) break;
2505 5           if (states[idx] == SHM_TOMBSTONE) continue;
2506             #ifdef SHM_KEY_IS_INT
2507 4           if (SHM_KEY_EQ(&nodes[idx], key)) {
2508             #else
2509 1           if (SHM_KEY_EQ_STR(&nodes[idx], h->arena, key_str, key_len, key_utf8)) {
2510             #endif
2511 4           if (SHM_IS_EXPIRED(h, idx, now)) {
2512 1           shm_rwlock_rdunlock(hdr);
2513 1           return 0;
2514             }
2515 3           c->iter_pos = idx; /* next cursor_next starts here */
2516 3           shm_rwlock_rdunlock(hdr);
2517 3           return 1;
2518             }
2519             }
2520              
2521 1           shm_rwlock_rdunlock(hdr);
2522 1           return 0;
2523             }
2524              
2525             /* ---- Undefine template macros ---- */
2526              
2527             #undef SHM_PASTE2
2528             #undef SHM_PASTE
2529             #undef SHM_FN
2530             #undef SHM_NODE_TYPE
2531             #undef SHM_PREFIX
2532             #undef SHM_VARIANT_ID
2533              
2534             #ifdef SHM_KEY_IS_INT
2535             #undef SHM_KEY_IS_INT
2536             #undef SHM_KEY_INT_TYPE
2537             #undef SHM_HASH_KEY
2538             #undef SHM_KEY_EQ
2539             #else
2540             #undef SHM_HASH_KEY_STR
2541             #undef SHM_KEY_EQ_STR
2542             #endif
2543              
2544             #ifdef SHM_VAL_IS_STR
2545             #undef SHM_VAL_IS_STR
2546             #else
2547             #undef SHM_VAL_INT_TYPE
2548             #endif
2549              
2550             #ifdef SHM_HAS_COUNTERS
2551             #undef SHM_HAS_COUNTERS
2552             #endif