File Coverage

buf_generic.h
Criterion Covered Total %
statement 964 1979 48.7
branch 263 924 28.4
condition n/a
subroutine n/a
pod n/a
total 1227 2903 42.2


line stmt bran cond sub pod time code
1             /*
2             * buf_generic.h — Macro-template for shared-memory typed buffers.
3             *
4             * Before including, define:
5             * BUF_PREFIX — function prefix (e.g., buf_i64)
6             * BUF_ELEM_TYPE — C element type (e.g., int64_t)
7             * BUF_VARIANT_ID — unique integer for header validation
8             * BUF_ELEM_SIZE — sizeof(BUF_ELEM_TYPE) or fixed string length
9             *
10             * Optional:
11             * BUF_HAS_COUNTERS — generate incr/decr/cas (integer types only)
12             * BUF_IS_FLOAT — element is float/double (affects SV conversion)
13             * BUF_IS_FIXEDSTR — element is fixed-length char array
14             */
15              
16             /* ================================================================
17             * Part 1: Shared definitions (included once)
18             * ================================================================ */
19              
20             #ifndef BUF_DEFS_H
21             #define BUF_DEFS_H
22              
23             #include
24             #include
25             #include
26             #include
27             #include
28             #include
29             #include
30             #include
31             #include
32             #include
33             #include
34             #include
35             #include
36             #include
37             #include
38              
39             /* ---- Constants ---- */
40              
41             #define BUF_MAGIC 0x42554631U /* "BUF1" */
42             #define BUF_VERSION 1
43             #define BUF_ERR_BUFLEN 256
44              
45             /* ---- Shared memory header (128 bytes, 2 cache lines, in mmap) ---- */
46              
47             #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
48             #define BUF_STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
49             #else
50             #define BUF_STATIC_ASSERT(cond, msg)
51             #endif
52              
53             typedef struct {
54             /* ---- Cache line 0 (0-63): immutable after create ---- */
55             uint32_t magic; /* 0 */
56             uint32_t version; /* 4 */
57             uint32_t variant_id; /* 8 */
58             uint32_t elem_size; /* 12 */
59             uint64_t capacity; /* 16: number of elements */
60             uint64_t total_size; /* 24: total mmap size */
61             uint64_t data_off; /* 32: offset to data array */
62             uint8_t _reserved0[24]; /* 40-63 */
63              
64             /* ---- Cache line 1 (64-127): seqlock + rwlock + mutable state ---- */
65             uint32_t seq; /* 64: seqlock counter, odd = writer active */
66             uint32_t rwlock; /* 68: 0=unlocked, readers=1..0x7FFFFFFF, writer=0x80000000|pid */
67             uint32_t rwlock_waiters; /* 72: wake-target counter (readers+writers) */
68             uint32_t stat_recoveries; /* 76 */
69             uint32_t rwlock_writers_waiting; /* 80: reader yield signal (writers only) */
70             uint32_t _pad2; /* 84 */
71             uint64_t _reserved1[5]; /* 88-127 */
72             } BufHeader;
73              
74             BUF_STATIC_ASSERT(sizeof(BufHeader) == 128, "BufHeader must be exactly 128 bytes (2 cache lines)");
75              
76             /* ---- Process-local handle ---- */
77              
78             typedef struct {
79             BufHeader *hdr;
80             void *data; /* pointer to element array in mmap */
81             size_t mmap_size;
82             char *path; /* backing file path (strdup'd, NULL for anon) */
83             int fd; /* kept open for memfd, -1 otherwise */
84             int efd; /* eventfd for notifications, -1 if none */
85             uint8_t wr_locked; /* process-local: 1 if lock_wr is held */
86             uint8_t efd_owned; /* 1 if we created the eventfd (close on destroy) */
87             } BufHandle;
88              
89             /* ---- Futex-based read-write lock ---- */
90              
91             #define BUF_RWLOCK_SPIN_LIMIT 32
92             #define BUF_LOCK_TIMEOUT_SEC 2
93              
94 196           static inline void buf_spin_pause(void) {
95             #if defined(__x86_64__) || defined(__i386__)
96 196           __asm__ volatile("pause" ::: "memory");
97             #elif defined(__aarch64__)
98             __asm__ volatile("yield" ::: "memory");
99             #else
100             __asm__ volatile("" ::: "memory");
101             #endif
102 196           }
103              
104             #define BUF_RWLOCK_WRITER_BIT 0x80000000U
105             #define BUF_RWLOCK_PID_MASK 0x7FFFFFFFU
106             #define BUF_RWLOCK_WR(pid) (BUF_RWLOCK_WRITER_BIT | ((uint32_t)(pid) & BUF_RWLOCK_PID_MASK))
107              
108 0           static inline int buf_pid_alive(uint32_t pid) {
109 0 0         if (pid == 0) return 1;
110 0 0         return !(kill((pid_t)pid, 0) == -1 && errno == ESRCH);
    0          
111             }
112              
113 0           static inline void buf_recover_stale_lock(BufHeader *hdr, uint32_t observed_rwlock) {
114 0           uint32_t mypid = BUF_RWLOCK_WR((uint32_t)getpid());
115 0 0         if (!__atomic_compare_exchange_n(&hdr->rwlock, &observed_rwlock,
116             mypid, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
117 0           return;
118 0           uint32_t seq = __atomic_load_n(&hdr->seq, __ATOMIC_ACQUIRE);
119 0 0         if (seq & 1)
120 0           __atomic_store_n(&hdr->seq, seq + 1, __ATOMIC_RELEASE);
121 0           __atomic_add_fetch(&hdr->stat_recoveries, 1, __ATOMIC_RELAXED);
122 0           __atomic_store_n(&hdr->rwlock, 0, __ATOMIC_RELEASE);
123 0 0         if (__atomic_load_n(&hdr->rwlock_waiters, __ATOMIC_RELAXED) > 0)
124 0           syscall(SYS_futex, &hdr->rwlock, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
125             }
126              
127             static const struct timespec buf_lock_timeout = { BUF_LOCK_TIMEOUT_SEC, 0 };
128              
129 1           static inline void buf_rwlock_rdlock(BufHeader *hdr) {
130 1           uint32_t *lock = &hdr->rwlock;
131 1           uint32_t *waiters = &hdr->rwlock_waiters;
132 1           uint32_t *writers_waiting = &hdr->rwlock_writers_waiting;
133 1           for (int spin = 0; ; spin++) {
134 1           uint32_t cur = __atomic_load_n(lock, __ATOMIC_RELAXED);
135 1 50         if (cur > 0 && cur < BUF_RWLOCK_WRITER_BIT) {
    0          
136 0 0         if (__atomic_compare_exchange_n(lock, &cur, cur + 1,
137             1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
138 1           return;
139 1 50         } else if (cur == 0 && !__atomic_load_n(writers_waiting, __ATOMIC_RELAXED)) {
    50          
140 1 50         if (__atomic_compare_exchange_n(lock, &cur, 1,
141             1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
142 1           return;
143             }
144 0 0         if (__builtin_expect(spin < BUF_RWLOCK_SPIN_LIMIT, 1)) {
145 0           buf_spin_pause();
146 0           continue;
147             }
148 0           __atomic_add_fetch(waiters, 1, __ATOMIC_RELAXED);
149 0           cur = __atomic_load_n(lock, __ATOMIC_RELAXED);
150 0 0         if (cur >= BUF_RWLOCK_WRITER_BIT || cur == 0) {
    0          
151 0           long rc = syscall(SYS_futex, lock, FUTEX_WAIT, cur,
152             &buf_lock_timeout, NULL, 0);
153 0 0         if (rc == -1 && errno == ETIMEDOUT) {
    0          
154 0           __atomic_sub_fetch(waiters, 1, __ATOMIC_RELAXED);
155 0 0         if (cur >= BUF_RWLOCK_WRITER_BIT) {
156 0           uint32_t val = __atomic_load_n(lock, __ATOMIC_RELAXED);
157 0 0         if (val >= BUF_RWLOCK_WRITER_BIT) {
158 0           uint32_t pid = val & BUF_RWLOCK_PID_MASK;
159 0 0         if (!buf_pid_alive(pid))
160 0           buf_recover_stale_lock(hdr, val);
161             }
162             } else {
163             /* Yielding to writer timed out — drop one writers_waiting
164             * to recover from a potentially-crashed parked writer. */
165 0           uint32_t wc = __atomic_load_n(writers_waiting, __ATOMIC_RELAXED);
166 0 0         while (wc > 0 && !__atomic_compare_exchange_n(
167 0 0         writers_waiting, &wc, wc - 1,
168             1, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {}
169             }
170 0           spin = 0;
171 0           continue;
172             }
173             }
174 0           __atomic_sub_fetch(waiters, 1, __ATOMIC_RELAXED);
175 0           spin = 0;
176             }
177             }
178              
179 1           static inline void buf_rwlock_rdunlock(BufHeader *hdr) {
180 1           uint32_t prev = __atomic_sub_fetch(&hdr->rwlock, 1, __ATOMIC_RELEASE);
181 1 50         if (prev == 0 && __atomic_load_n(&hdr->rwlock_waiters, __ATOMIC_RELAXED) > 0)
    50          
182 0           syscall(SYS_futex, &hdr->rwlock, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
183 1           }
184              
185 553           static inline void buf_rwlock_wrlock(BufHeader *hdr) {
186 553           uint32_t *lock = &hdr->rwlock;
187 553           uint32_t *waiters = &hdr->rwlock_waiters;
188 553           uint32_t *writers_waiting = &hdr->rwlock_writers_waiting;
189 553           uint32_t mypid = BUF_RWLOCK_WR((uint32_t)getpid());
190 749           for (int spin = 0; ; spin++) {
191 749           uint32_t expected = 0;
192 749 100         if (__atomic_compare_exchange_n(lock, &expected, mypid,
193             1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
194 553           return;
195 196 50         if (__builtin_expect(spin < BUF_RWLOCK_SPIN_LIMIT, 1)) {
196 196           buf_spin_pause();
197 196           continue;
198             }
199 0           __atomic_add_fetch(waiters, 1, __ATOMIC_RELAXED);
200 0           __atomic_add_fetch(writers_waiting, 1, __ATOMIC_RELAXED);
201 0           uint32_t cur = __atomic_load_n(lock, __ATOMIC_RELAXED);
202 0 0         if (cur != 0) {
203 0           long rc = syscall(SYS_futex, lock, FUTEX_WAIT, cur,
204             &buf_lock_timeout, NULL, 0);
205 0 0         if (rc == -1 && errno == ETIMEDOUT) {
    0          
206 0           __atomic_sub_fetch(waiters, 1, __ATOMIC_RELAXED);
207 0           __atomic_sub_fetch(writers_waiting, 1, __ATOMIC_RELAXED);
208 0           uint32_t val = __atomic_load_n(lock, __ATOMIC_RELAXED);
209 0 0         if (val >= BUF_RWLOCK_WRITER_BIT) {
210 0           uint32_t pid = val & BUF_RWLOCK_PID_MASK;
211 0 0         if (!buf_pid_alive(pid))
212 0           buf_recover_stale_lock(hdr, val);
213             }
214 0           spin = 0;
215 0           continue;
216             }
217             }
218 0           __atomic_sub_fetch(waiters, 1, __ATOMIC_RELAXED);
219 0           __atomic_sub_fetch(writers_waiting, 1, __ATOMIC_RELAXED);
220 0           spin = 0;
221             }
222             }
223              
224 553           static inline void buf_rwlock_wrunlock(BufHeader *hdr) {
225 553           __atomic_store_n(&hdr->rwlock, 0, __ATOMIC_RELEASE);
226 553 50         if (__atomic_load_n(&hdr->rwlock_waiters, __ATOMIC_RELAXED) > 0)
227 0           syscall(SYS_futex, &hdr->rwlock, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
228 553           }
229              
230             /* ---- Seqlock ---- */
231              
232 34           static inline uint32_t buf_seqlock_read_begin(BufHeader *hdr) {
233 34           int spin = 0;
234 0           for (;;) {
235 34           uint32_t s = __atomic_load_n(&hdr->seq, __ATOMIC_ACQUIRE);
236 34 50         if (__builtin_expect((s & 1) == 0, 1)) return s;
237 0 0         if (__builtin_expect(spin < 100000, 1)) {
238 0           buf_spin_pause();
239 0           spin++;
240 0           continue;
241             }
242 0           uint32_t val = __atomic_load_n(&hdr->rwlock, __ATOMIC_RELAXED);
243 0 0         if (val >= BUF_RWLOCK_WRITER_BIT) {
244 0           uint32_t pid = val & BUF_RWLOCK_PID_MASK;
245 0 0         if (!buf_pid_alive(pid)) {
246 0           buf_recover_stale_lock(hdr, val);
247 0           spin = 0;
248 0           continue;
249             }
250             }
251 0           struct timespec ts = {0, 1000000};
252 0           nanosleep(&ts, NULL);
253 0           spin = 0;
254             }
255             }
256              
257 34           static inline int buf_seqlock_read_retry(uint32_t *seq, uint32_t start) {
258 34           return __atomic_load_n(seq, __ATOMIC_ACQUIRE) != start;
259             }
260              
261 553           static inline void buf_seqlock_write_begin(uint32_t *seq) {
262 553           __atomic_add_fetch(seq, 1, __ATOMIC_RELEASE);
263 553           }
264              
265 553           static inline void buf_seqlock_write_end(uint32_t *seq) {
266 553           __atomic_add_fetch(seq, 1, __ATOMIC_RELEASE);
267 553           }
268              
269             /* ---- mmap create/open ---- */
270              
271 44           static BufHandle *buf_create_map(const char *path, uint64_t capacity,
272             uint32_t elem_size, uint32_t variant_id,
273             char *errbuf) {
274 44           errbuf[0] = '\0';
275 44           int created = 0;
276 44           int fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0666);
277 44 100         if (fd >= 0) {
278 37           created = 1;
279 7 50         } else if (errno == EEXIST) {
280 7           fd = open(path, O_RDWR);
281             }
282 44 50         if (fd < 0) {
283 0           snprintf(errbuf, BUF_ERR_BUFLEN, "open(%s): %s", path, strerror(errno));
284 0           return NULL;
285             }
286              
287             /* Lock file for init race prevention */
288 44 50         if (flock(fd, LOCK_EX) < 0) {
289 0           snprintf(errbuf, BUF_ERR_BUFLEN, "flock(%s): %s", path, strerror(errno));
290 0           close(fd);
291 0           return NULL;
292             }
293              
294 44           uint64_t data_off = sizeof(BufHeader); /* 128, already cache-line aligned */
295 44 50         if (elem_size > 0 && capacity > (UINT64_MAX - data_off) / elem_size) {
    50          
296 0           snprintf(errbuf, BUF_ERR_BUFLEN, "buffer size overflow");
297 0           flock(fd, LOCK_UN);
298 0           close(fd);
299 0           return NULL;
300             }
301 44           uint64_t total_size = data_off + capacity * elem_size;
302              
303             struct stat st;
304 44 50         if (fstat(fd, &st) < 0) {
305 0           snprintf(errbuf, BUF_ERR_BUFLEN, "fstat(%s): %s", path, strerror(errno));
306 0           flock(fd, LOCK_UN);
307 0           close(fd);
308 0           return NULL;
309             }
310              
311 44 100         if (!created && st.st_size > 0 && (uint64_t)st.st_size < sizeof(BufHeader)) {
    50          
    50          
312 0           snprintf(errbuf, BUF_ERR_BUFLEN, "%s: file too small (%lld)", path, (long long)st.st_size);
313 0           flock(fd, LOCK_UN); close(fd); return NULL;
314             }
315 44 100         if (created || st.st_size == 0) {
    50          
316 37 50         if (ftruncate(fd, (off_t)total_size) < 0) {
317 0           snprintf(errbuf, BUF_ERR_BUFLEN, "ftruncate(%s): %s", path, strerror(errno));
318 0           flock(fd, LOCK_UN);
319 0           close(fd);
320 0           return NULL;
321             }
322             }
323              
324             /* Re-stat after possible truncate */
325 44 50         if (fstat(fd, &st) < 0) {
326 0           snprintf(errbuf, BUF_ERR_BUFLEN, "fstat(%s): %s", path, strerror(errno));
327 0           flock(fd, LOCK_UN);
328 0           close(fd);
329 0           return NULL;
330             }
331              
332 44           void *base = mmap(NULL, (size_t)st.st_size, PROT_READ | PROT_WRITE,
333             MAP_SHARED, fd, 0);
334 44 50         if (base == MAP_FAILED) {
335 0           snprintf(errbuf, BUF_ERR_BUFLEN, "mmap(%s): %s", path, strerror(errno));
336 0           flock(fd, LOCK_UN);
337 0           close(fd);
338 0           return NULL;
339             }
340              
341 44           BufHeader *hdr = (BufHeader *)base;
342              
343 44 100         if (created || hdr->magic == 0) {
    50          
344             /* Initialize header */
345 37           memset(hdr, 0, sizeof(BufHeader));
346 37           hdr->magic = BUF_MAGIC;
347 37           hdr->version = BUF_VERSION;
348 37           hdr->variant_id = variant_id;
349 37           hdr->elem_size = elem_size;
350 37           hdr->capacity = capacity;
351 37           hdr->total_size = (uint64_t)st.st_size;
352 37           hdr->data_off = data_off;
353             /* Zero the data area */
354 37           memset((char *)base + data_off, 0, (size_t)(capacity * elem_size));
355 37           __atomic_thread_fence(__ATOMIC_RELEASE);
356             } else {
357             /* Validate existing header */
358 7 100         if (hdr->magic != BUF_MAGIC) {
359 1           snprintf(errbuf, BUF_ERR_BUFLEN, "%s: bad magic (0x%08x)", path, hdr->magic);
360 1           goto fail;
361             }
362 6 50         if (hdr->version != BUF_VERSION) {
363 0           snprintf(errbuf, BUF_ERR_BUFLEN, "%s: version mismatch (%u != %u)",
364             path, hdr->version, BUF_VERSION);
365 0           goto fail;
366             }
367 6 100         if (hdr->variant_id != variant_id) {
368 1           snprintf(errbuf, BUF_ERR_BUFLEN, "%s: variant mismatch (%u != %u)",
369             path, hdr->variant_id, variant_id);
370 1           goto fail;
371             }
372 5 50         if (hdr->elem_size != elem_size) {
373 0           snprintf(errbuf, BUF_ERR_BUFLEN, "%s: elem_size mismatch (%u != %u)",
374             path, hdr->elem_size, elem_size);
375 0           goto fail;
376             }
377 5 50         if (hdr->elem_size == 0 ||
378 5 50         hdr->data_off < sizeof(BufHeader) ||
379 5 50         hdr->data_off >= (uint64_t)st.st_size ||
380 5 50         hdr->capacity > ((uint64_t)st.st_size - hdr->data_off) / hdr->elem_size) {
381 0           snprintf(errbuf, BUF_ERR_BUFLEN, "%s: corrupt header (data doesn't fit in file)", path);
382 0           goto fail;
383             }
384             }
385              
386 42           flock(fd, LOCK_UN);
387 42           close(fd);
388              
389 42           BufHandle *h = (BufHandle *)calloc(1, sizeof(BufHandle));
390 42 50         if (!h) {
391 0           snprintf(errbuf, BUF_ERR_BUFLEN, "calloc: out of memory");
392 0           munmap(base, (size_t)st.st_size);
393 0           return NULL;
394             }
395 42           h->hdr = hdr;
396 42           h->data = (char *)base + hdr->data_off;
397 42           h->mmap_size = (size_t)st.st_size;
398 42           h->path = strdup(path);
399 42           h->fd = -1;
400 42           h->efd = -1;
401 42           return h;
402              
403 2           fail:
404 2           munmap(base, (size_t)st.st_size);
405 2           flock(fd, LOCK_UN);
406 2           close(fd);
407 2           return NULL;
408             }
409              
410             /* ---- Anonymous mmap (no file, fork-only sharing) ---- */
411              
412 40           static BufHandle *buf_create_anon(uint64_t capacity, uint32_t elem_size,
413             uint32_t variant_id, char *errbuf) {
414 40           errbuf[0] = '\0';
415 40           uint64_t data_off = sizeof(BufHeader);
416 40 50         if (elem_size > 0 && capacity > (UINT64_MAX - data_off) / elem_size) {
    50          
417 0           snprintf(errbuf, BUF_ERR_BUFLEN, "buffer size overflow");
418 0           return NULL;
419             }
420 40           uint64_t total_size = data_off + capacity * elem_size;
421              
422 40           void *base = mmap(NULL, (size_t)total_size, PROT_READ | PROT_WRITE,
423             MAP_SHARED | MAP_ANONYMOUS, -1, 0);
424 40 50         if (base == MAP_FAILED) {
425 0           snprintf(errbuf, BUF_ERR_BUFLEN, "mmap(anon): %s", strerror(errno));
426 0           return NULL;
427             }
428              
429 40           BufHeader *hdr = (BufHeader *)base;
430 40           memset(hdr, 0, sizeof(BufHeader));
431 40           hdr->magic = BUF_MAGIC;
432 40           hdr->version = BUF_VERSION;
433 40           hdr->variant_id = variant_id;
434 40           hdr->elem_size = elem_size;
435 40           hdr->capacity = capacity;
436 40           hdr->total_size = total_size;
437 40           hdr->data_off = data_off;
438              
439 40           BufHandle *h = (BufHandle *)calloc(1, sizeof(BufHandle));
440 40 50         if (!h) {
441 0           snprintf(errbuf, BUF_ERR_BUFLEN, "calloc: out of memory");
442 0           munmap(base, (size_t)total_size);
443 0           return NULL;
444             }
445 40           h->hdr = hdr;
446 40           h->data = (char *)base + data_off;
447 40           h->mmap_size = (size_t)total_size;
448 40           h->path = NULL;
449 40           h->fd = -1;
450 40           h->efd = -1;
451 40           return h;
452             }
453              
454             /* ---- memfd (named anonymous, shareable via fd passing) ---- */
455              
456 9           static BufHandle *buf_create_memfd(const char *name, uint64_t capacity,
457             uint32_t elem_size, uint32_t variant_id,
458             char *errbuf) {
459 9           errbuf[0] = '\0';
460 9           uint64_t data_off = sizeof(BufHeader);
461 9 50         if (elem_size > 0 && capacity > (UINT64_MAX - data_off) / elem_size) {
    50          
462 0           snprintf(errbuf, BUF_ERR_BUFLEN, "buffer size overflow");
463 0           return NULL;
464             }
465 9           uint64_t total_size = data_off + capacity * elem_size;
466              
467 9           int fd = (int)syscall(SYS_memfd_create, name, MFD_CLOEXEC | MFD_ALLOW_SEALING);
468 9 50         if (fd < 0) {
469 0           snprintf(errbuf, BUF_ERR_BUFLEN, "memfd_create: %s", strerror(errno));
470 0           return NULL;
471             }
472 9 50         if (ftruncate(fd, (off_t)total_size) < 0) {
473 0           snprintf(errbuf, BUF_ERR_BUFLEN, "ftruncate(memfd): %s", strerror(errno));
474 0           close(fd);
475 0           return NULL;
476             }
477 9           (void)fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW);
478              
479 9           void *base = mmap(NULL, (size_t)total_size, PROT_READ | PROT_WRITE,
480             MAP_SHARED, fd, 0);
481 9 50         if (base == MAP_FAILED) {
482 0           snprintf(errbuf, BUF_ERR_BUFLEN, "mmap(memfd): %s", strerror(errno));
483 0           close(fd);
484 0           return NULL;
485             }
486              
487 9           BufHeader *hdr = (BufHeader *)base;
488 9           memset(hdr, 0, sizeof(BufHeader));
489 9           hdr->magic = BUF_MAGIC;
490 9           hdr->version = BUF_VERSION;
491 9           hdr->variant_id = variant_id;
492 9           hdr->elem_size = elem_size;
493 9           hdr->capacity = capacity;
494 9           hdr->total_size = total_size;
495 9           hdr->data_off = data_off;
496              
497 9           BufHandle *h = (BufHandle *)calloc(1, sizeof(BufHandle));
498 9 50         if (!h) {
499 0           snprintf(errbuf, BUF_ERR_BUFLEN, "calloc: out of memory");
500 0           munmap(base, (size_t)total_size);
501 0           close(fd);
502 0           return NULL;
503             }
504 9           h->hdr = hdr;
505 9           h->data = (char *)base + data_off;
506 9           h->mmap_size = (size_t)total_size;
507 9           h->path = NULL;
508 9           h->fd = fd;
509 9           h->efd = -1;
510 9           return h;
511             }
512              
513             /* ---- Open from fd (received via SCM_RIGHTS or dup) ---- */
514              
515 5           static BufHandle *buf_open_fd(int fd, uint32_t elem_size, uint32_t variant_id,
516             char *errbuf) {
517 5           errbuf[0] = '\0';
518             struct stat st;
519 5 50         if (fstat(fd, &st) < 0) {
520 0           snprintf(errbuf, BUF_ERR_BUFLEN, "fstat(fd=%d): %s", fd, strerror(errno));
521 0           return NULL;
522             }
523 5 50         if ((uint64_t)st.st_size < sizeof(BufHeader)) {
524 0           snprintf(errbuf, BUF_ERR_BUFLEN, "fd=%d: file too small for header", fd);
525 0           return NULL;
526             }
527              
528 5           void *base = mmap(NULL, (size_t)st.st_size, PROT_READ | PROT_WRITE,
529             MAP_SHARED, fd, 0);
530 5 50         if (base == MAP_FAILED) {
531 0           snprintf(errbuf, BUF_ERR_BUFLEN, "mmap(fd=%d): %s", fd, strerror(errno));
532 0           return NULL;
533             }
534              
535 5           BufHeader *hdr = (BufHeader *)base;
536 5 50         if (hdr->magic != BUF_MAGIC) {
537 0           snprintf(errbuf, BUF_ERR_BUFLEN, "fd=%d: bad magic (0x%08x)", fd, hdr->magic);
538 0           goto fail;
539             }
540 5 50         if (hdr->version != BUF_VERSION) {
541 0           snprintf(errbuf, BUF_ERR_BUFLEN, "fd=%d: version mismatch (%u != %u)",
542             fd, hdr->version, BUF_VERSION);
543 0           goto fail;
544             }
545 5 100         if (hdr->variant_id != variant_id) {
546 1           snprintf(errbuf, BUF_ERR_BUFLEN, "fd=%d: variant mismatch (%u != %u)",
547             fd, hdr->variant_id, variant_id);
548 1           goto fail;
549             }
550 4 50         if (hdr->elem_size != elem_size) {
551 0           snprintf(errbuf, BUF_ERR_BUFLEN, "fd=%d: elem_size mismatch (%u != %u)",
552             fd, hdr->elem_size, elem_size);
553 0           goto fail;
554             }
555 4 50         if (hdr->elem_size == 0 ||
556 4 50         hdr->data_off != sizeof(BufHeader) ||
557 4 50         hdr->total_size != (uint64_t)st.st_size ||
558 4 50         hdr->capacity > ((uint64_t)st.st_size - hdr->data_off) / hdr->elem_size) {
559 0           snprintf(errbuf, BUF_ERR_BUFLEN, "fd=%d: corrupt header", fd);
560 0           goto fail;
561             }
562              
563             {
564 4           int myfd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
565 4 50         if (myfd < 0) {
566 0           snprintf(errbuf, BUF_ERR_BUFLEN, "fcntl(F_DUPFD_CLOEXEC, fd=%d): %s",
567 0           fd, strerror(errno));
568 0           munmap(base, (size_t)st.st_size);
569 0           return NULL;
570             }
571 4           BufHandle *h = (BufHandle *)calloc(1, sizeof(BufHandle));
572 4 50         if (!h) {
573 0           snprintf(errbuf, BUF_ERR_BUFLEN, "calloc: out of memory");
574 0           close(myfd);
575 0           munmap(base, (size_t)st.st_size);
576 0           return NULL;
577             }
578 4           h->hdr = hdr;
579 4           h->data = (char *)base + hdr->data_off;
580 4           h->mmap_size = (size_t)st.st_size;
581 4           h->path = NULL;
582 4           h->fd = myfd;
583 4           h->efd = -1;
584 4           return h;
585             }
586              
587 1           fail:
588 1           munmap(base, (size_t)st.st_size);
589 1           return NULL;
590             }
591              
592             /* ---- msync ---- */
593              
594 3           static inline int buf_msync(BufHandle *h) {
595 3 50         if (!h || !h->hdr) return 0;
    50          
596 3           return msync(h->hdr, h->mmap_size, MS_SYNC);
597             }
598              
599             /* ---- Eventfd integration (opt-in notifications) ---- */
600              
601 8           static int buf_create_eventfd(BufHandle *h) {
602 8 100         if (h->efd >= 0) return h->efd;
603 7           int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
604 7 50         if (efd < 0) return -1;
605 7           h->efd = efd;
606 7           h->efd_owned = 1;
607 7           return efd;
608             }
609              
610 2           static void buf_attach_eventfd(BufHandle *h, int efd) {
611 2 50         if (h->efd >= 0 && h->efd_owned) close(h->efd);
    0          
612 2           h->efd = efd;
613 2           h->efd_owned = 0;
614 2           }
615              
616 7           static int buf_notify(BufHandle *h) {
617 7 100         if (h->efd < 0) return 0;
618 6           uint64_t val = 1;
619 6           return write(h->efd, &val, sizeof(val)) == sizeof(val);
620             }
621              
622 9           static int64_t buf_wait_notify(BufHandle *h) {
623 9 100         if (h->efd < 0) return -1;
624 8           uint64_t val = 0;
625 8 100         if (read(h->efd, &val, sizeof(val)) != sizeof(val)) return -1;
626 6           return (int64_t)val;
627             }
628              
629 95           static void buf_close_map(BufHandle *h) {
630 95 50         if (!h) return;
631 95 100         if (h->efd >= 0 && h->efd_owned) close(h->efd);
    100          
632 95 50         if (h->hdr) munmap(h->hdr, h->mmap_size);
633 95 100         if (h->fd >= 0) close(h->fd);
634 95 100         if (h->path) free(h->path);
635 95           free(h);
636             }
637              
638             #endif /* BUF_DEFS_H */
639              
640              
641             /* ================================================================
642             * Part 2: Per-variant functions (instantiated per include)
643             * ================================================================ */
644              
645             #ifndef BUF_PREFIX
646             #error "BUF_PREFIX must be defined before including buf_generic.h"
647             #endif
648              
649             #define BUF_PASTE2(a, b) a##_##b
650             #define BUF_PASTE(a, b) BUF_PASTE2(a, b)
651             #define BUF_FN(name) BUF_PASTE(BUF_PREFIX, name)
652              
653             /* ---- Create ---- */
654              
655             #ifdef BUF_IS_FIXEDSTR
656 4           static BufHandle *BUF_FN(create)(const char *path, uint64_t capacity,
657             uint32_t str_len, char *errbuf) {
658 4 50         if (str_len == 0) {
659 0           snprintf(errbuf, BUF_ERR_BUFLEN, "str_len must be > 0");
660 0           return NULL;
661             }
662 4           return buf_create_map(path, capacity, str_len, BUF_VARIANT_ID, errbuf);
663             }
664             #else
665 40           static BufHandle *BUF_FN(create)(const char *path, uint64_t capacity, char *errbuf) {
  5            
  1            
  1            
  25            
  3            
  1            
  1            
  1            
  1            
  1            
666 40           return buf_create_map(path, capacity, BUF_ELEM_SIZE, BUF_VARIANT_ID, errbuf);
  5            
  1            
  1            
  25            
  3            
  1            
  1            
  1            
  1            
  1            
667             }
668             #endif
669              
670             /* ---- Create anonymous ---- */
671              
672             #ifdef BUF_IS_FIXEDSTR
673 8           static BufHandle *BUF_FN(create_anon)(uint64_t capacity, uint32_t str_len, char *errbuf) {
674 8 50         if (str_len == 0) { snprintf(errbuf, BUF_ERR_BUFLEN, "str_len must be > 0"); return NULL; }
675 8           return buf_create_anon(capacity, str_len, BUF_VARIANT_ID, errbuf);
676             }
677 2           static BufHandle *BUF_FN(create_memfd)(const char *name, uint64_t capacity, uint32_t str_len, char *errbuf) {
678 2 50         if (str_len == 0) { snprintf(errbuf, BUF_ERR_BUFLEN, "str_len must be > 0"); return NULL; }
679 2           return buf_create_memfd(name, capacity, str_len, BUF_VARIANT_ID, errbuf);
680             }
681 1           static BufHandle *BUF_FN(open_fd)(int fd, uint32_t str_len, char *errbuf) {
682 1 50         if (str_len == 0) { snprintf(errbuf, BUF_ERR_BUFLEN, "str_len must be > 0"); return NULL; }
683 1           return buf_open_fd(fd, str_len, BUF_VARIANT_ID, errbuf);
684             }
685             #else
686 32           static BufHandle *BUF_FN(create_anon)(uint64_t capacity, char *errbuf) {
  5            
  1            
  0            
  23            
  1            
  0            
  0            
  0            
  1            
  1            
687 32           return buf_create_anon(capacity, BUF_ELEM_SIZE, BUF_VARIANT_ID, errbuf);
  5            
  1            
  0            
  23            
  1            
  0            
  0            
  0            
  1            
  1            
688             }
689 7           static BufHandle *BUF_FN(create_memfd)(const char *name, uint64_t capacity, char *errbuf) {
  0            
  0            
  0            
  7            
  0            
  0            
  0            
  0            
  0            
  0            
690 7           return buf_create_memfd(name, capacity, BUF_ELEM_SIZE, BUF_VARIANT_ID, errbuf);
  0            
  0            
  0            
  7            
  0            
  0            
  0            
  0            
  0            
  0            
691             }
692 4           static BufHandle *BUF_FN(open_fd)(int fd, char *errbuf) {
  0            
  0            
  0            
  3            
  1            
  0            
  0            
  0            
  0            
  0            
693 4           return buf_open_fd(fd, BUF_ELEM_SIZE, BUF_VARIANT_ID, errbuf);
  0            
  0            
  0            
  3            
  1            
  0            
  0            
  0            
  0            
  0            
694             }
695             #endif
696              
697             /* ---- Raw byte access (for packed binary interop) ---- */
698              
699 9           static int BUF_FN(get_raw)(BufHandle *h, uint64_t byte_off, uint64_t nbytes, void *out) {
  1            
  0            
  1            
  0            
  7            
  0            
  0            
  0            
  0            
  0            
  0            
700 9           uint64_t data_size = h->hdr->capacity * (uint64_t)h->hdr->elem_size;
  1            
  0            
  1            
  0            
  7            
  0            
  0            
  0            
  0            
  0            
  0            
701 9 50         if (nbytes > data_size || byte_off > data_size - nbytes) return 0;
  1 50          
  0 0          
  1 0          
  0 50          
  7 50          
  0 0          
  0 0          
  0 50          
  0 100          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
702 7           char *data = (char *)h->data;
  1            
  0            
  1            
  0            
  5            
  0            
  0            
  0            
  0            
  0            
  0            
703 7 50         if (h->wr_locked) {
  1 0          
  0 50          
  1 0          
  0 100          
  5 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
704 1           memcpy(out, data + byte_off, (size_t)nbytes);
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  0            
  0            
705             } else {
706             uint32_t seq_start;
707             do {
708 6           seq_start = buf_seqlock_read_begin(h->hdr);
  1            
  0            
  1            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
  0            
709 6           memcpy(out, data + byte_off, (size_t)nbytes);
  1            
  0            
  1            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
  0            
710 6 50         } while (buf_seqlock_read_retry(&h->hdr->seq, seq_start));
  1 0          
  0 50          
  1 0          
  0 50          
  4 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
711             }
712 7           return 1;
  1            
  0            
  1            
  0            
  5            
  0            
  0            
  0            
  0            
  0            
  0            
713             }
714              
715 8           static int BUF_FN(set_raw)(BufHandle *h, uint64_t byte_off, uint64_t nbytes, const void *in) {
  1            
  0            
  0            
  0            
  7            
  0            
  0            
  0            
  0            
  0            
  0            
716 8           uint64_t data_size = h->hdr->capacity * (uint64_t)h->hdr->elem_size;
  1            
  0            
  0            
  0            
  7            
  0            
  0            
  0            
  0            
  0            
  0            
717 8 50         if (nbytes > data_size || byte_off > data_size - nbytes) return 0;
  1 50          
  0 0          
  0 0          
  0 0          
  7 0          
  0 0          
  0 0          
  0 50          
  0 100          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
718 4           char *data = (char *)h->data;
  1            
  0            
  0            
  0            
  3            
  0            
  0            
  0            
  0            
  0            
  0            
719 4           int nested = h->wr_locked;
  1            
  0            
  0            
  0            
  3            
  0            
  0            
  0            
  0            
  0            
  0            
720 4 50         if (!nested) { buf_rwlock_wrlock(h->hdr); buf_seqlock_write_begin(&h->hdr->seq); }
  1 0          
  0 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
721 4           memcpy(data + byte_off, in, (size_t)nbytes);
  1            
  0            
  0            
  0            
  3            
  0            
  0            
  0            
  0            
  0            
  0            
722 4 50         if (!nested) { buf_seqlock_write_end(&h->hdr->seq); buf_rwlock_wrunlock(h->hdr); }
  1 0          
  0 0          
  0 0          
  0 50          
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
723 4           return 1;
  1            
  0            
  0            
  0            
  3            
  0            
  0            
  0            
  0            
  0            
  0            
724             }
725              
726             /* ---- Clear (zero entire buffer) ---- */
727              
728 7           static void BUF_FN(clear)(BufHandle *h) {
  1            
  0            
  1            
  0            
  5            
  0            
  0            
  0            
  0            
  0            
  0            
729 7           BufHeader *hdr = h->hdr;
  1            
  0            
  1            
  0            
  5            
  0            
  0            
  0            
  0            
  0            
  0            
730 7           int nested = h->wr_locked;
  1            
  0            
  1            
  0            
  5            
  0            
  0            
  0            
  0            
  0            
  0            
731 7 50         if (!nested) { buf_rwlock_wrlock(hdr); buf_seqlock_write_begin(&hdr->seq); }
  1 0          
  0 50          
  1 0          
  0 100          
  5 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
732 7           memset(h->data, 0, (size_t)(hdr->capacity * hdr->elem_size));
  1            
  0            
  1            
  0            
  5            
  0            
  0            
  0            
  0            
  0            
  0            
733 7 50         if (!nested) { buf_seqlock_write_end(&hdr->seq); buf_rwlock_wrunlock(hdr); }
  1 0          
  0 50          
  1 0          
  0 100          
  5 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
734 7           }
  1            
  0            
  1            
  0            
  5            
  0            
  0            
  0            
  0            
  0            
  0            
735              
736             /* ---- Single-element atomic get (lock-free for numeric types) ---- */
737              
738             #ifdef BUF_IS_FIXEDSTR
739              
740 22           static int BUF_FN(get)(BufHandle *h, uint64_t idx, char *out, uint32_t *out_len) {
741 22           BufHeader *hdr = h->hdr;
742 22           uint32_t esz = hdr->elem_size;
743 22 50         if (idx >= hdr->capacity) return 0;
744 22           char *data = (char *)h->data;
745 22 100         if (h->wr_locked) {
746 1           memcpy(out, data + idx * esz, esz);
747             } else {
748             uint32_t seq_start;
749             do {
750 21           seq_start = buf_seqlock_read_begin(hdr);
751 21           memcpy(out, data + idx * esz, esz);
752 21 50         } while (buf_seqlock_read_retry(&hdr->seq, seq_start));
753             }
754 22           uint32_t len = esz;
755 260 100         while (len > 0 && out[len - 1] == '\0') len--;
    100          
756 22           *out_len = len;
757 22           return 1;
758             }
759              
760 521           static int BUF_FN(set)(BufHandle *h, uint64_t idx, const char *val, uint32_t len) {
761 521           BufHeader *hdr = h->hdr;
762 521           uint32_t esz = hdr->elem_size;
763 521 50         if (idx >= hdr->capacity) return 0;
764 521           char *data = (char *)h->data;
765 521           int nested = h->wr_locked;
766 521 100         if (!nested) { buf_rwlock_wrlock(hdr); buf_seqlock_write_begin(&hdr->seq); }
767 521           memset(data + idx * esz, 0, esz);
768 521           uint32_t copy_len = len < esz ? len : esz;
769 521           memcpy(data + idx * esz, val, copy_len);
770 521 100         if (!nested) { buf_seqlock_write_end(&hdr->seq); buf_rwlock_wrunlock(hdr); }
771 521           return 1;
772             }
773              
774             #elif defined(BUF_IS_FLOAT)
775              
776             /* Float/double: GCC __atomic builtins don't support FP types.
777             * Use same-sized integer atomic load/store + memcpy for lock-free access. */
778              
779             #if BUF_ELEM_SIZE == 4
780             typedef uint32_t BUF_PASTE(BUF_PREFIX, _uint_t);
781             #elif BUF_ELEM_SIZE == 8
782             typedef uint64_t BUF_PASTE(BUF_PREFIX, _uint_t);
783             #else
784             #error "BUF_IS_FLOAT requires BUF_ELEM_SIZE of 4 or 8"
785             #endif
786              
787 14           static int BUF_FN(get)(BufHandle *h, uint64_t idx, BUF_ELEM_TYPE *out) {
  9            
  5            
788 14           BufHeader *hdr = h->hdr;
  9            
  5            
789 14 50         if (idx >= hdr->capacity) return 0;
  9 50          
  5            
790             typedef BUF_PASTE(BUF_PREFIX, _uint_t) uint_t;
791 14           uint_t *idata = (uint_t *)h->data;
  9            
  5            
792 14           uint_t tmp = __atomic_load_n(&idata[idx], __ATOMIC_RELAXED);
  9            
  5            
793 14           memcpy(out, &tmp, sizeof(BUF_ELEM_TYPE));
  9            
  5            
794 14           return 1;
  9            
  5            
795             }
796              
797 1016           static int BUF_FN(set)(BufHandle *h, uint64_t idx, BUF_ELEM_TYPE val) {
  1012            
  4            
798 1016           BufHeader *hdr = h->hdr;
  1012            
  4            
799 1016 50         if (idx >= hdr->capacity) return 0;
  1012 50          
  4            
800             typedef BUF_PASTE(BUF_PREFIX, _uint_t) uint_t;
801 1016           uint_t *idata = (uint_t *)h->data;
  1012            
  4            
802             uint_t tmp;
803 1016           memcpy(&tmp, &val, sizeof(BUF_ELEM_TYPE));
  1012            
  4            
804 1016           __atomic_store_n(&idata[idx], tmp, __ATOMIC_RELAXED);
  1012            
  4            
805 1016           return 1;
  1012            
  4            
806             }
807              
808             #else /* integer types */
809              
810 1276           static int BUF_FN(get)(BufHandle *h, uint64_t idx, BUF_ELEM_TYPE *out) {
  2            
  1253            
  10            
  2            
  2            
  2            
  2            
  3            
811 1276           BufHeader *hdr = h->hdr;
  2            
  1253            
  10            
  2            
  2            
  2            
  2            
  3            
812 1276 50         if (idx >= hdr->capacity) return 0;
  2 100          
  1253 50          
  10 50          
  2 50          
  2 50          
  2 50          
  2 50          
  3            
813 1273           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  2            
  1250            
  10            
  2            
  2            
  2            
  2            
  3            
814 1273           *out = __atomic_load_n(&data[idx], __ATOMIC_RELAXED);
  2            
  1250            
  10            
  2            
  2            
  2            
  2            
  3            
815 1273           return 1;
  2            
  1250            
  10            
  2            
  2            
  2            
  2            
  3            
816             }
817              
818 89           static int BUF_FN(set)(BufHandle *h, uint64_t idx, BUF_ELEM_TYPE val) {
  1            
  69            
  9            
  1            
  1            
  1            
  3            
  4            
819 89           BufHeader *hdr = h->hdr;
  1            
  69            
  9            
  1            
  1            
  1            
  3            
  4            
820 89 50         if (idx >= hdr->capacity) return 0;
  1 100          
  69 50          
  9 50          
  1 50          
  1 50          
  1 50          
  3 50          
  4            
821 86           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  1            
  66            
  9            
  1            
  1            
  1            
  3            
  4            
822 86           __atomic_store_n(&data[idx], val, __ATOMIC_RELAXED);
  1            
  66            
  9            
  1            
  1            
  1            
  3            
  4            
823 86           return 1;
  1            
  66            
  9            
  1            
  1            
  1            
  3            
  4            
824             }
825              
826             #endif /* BUF_IS_FIXEDSTR */
827              
828             /* ---- Bulk operations (seqlock-guarded) ---- */
829              
830             #ifdef BUF_IS_FIXEDSTR
831              
832 2           static int BUF_FN(get_slice)(BufHandle *h, uint64_t from, uint64_t count,
833             void *out) {
834 2           BufHeader *hdr = h->hdr;
835 2           uint32_t esz = hdr->elem_size;
836 2 50         if (count > hdr->capacity || from > hdr->capacity - count) return 0;
    50          
837 2           char *data = (char *)h->data;
838 2 100         if (h->wr_locked) {
839 1           memcpy(out, data + from * esz, count * esz);
840             } else {
841             uint32_t seq_start;
842             do {
843 1           seq_start = buf_seqlock_read_begin(hdr);
844 1           memcpy(out, data + from * esz, count * esz);
845 1 50         } while (buf_seqlock_read_retry(&hdr->seq, seq_start));
846             }
847 2           return 1;
848             }
849              
850 1           static int BUF_FN(set_slice)(BufHandle *h, uint64_t from, uint64_t count,
851             const void *in) {
852 1           BufHeader *hdr = h->hdr;
853 1           uint32_t esz = hdr->elem_size;
854 1 50         if (count > hdr->capacity || from > hdr->capacity - count) return 0;
    50          
855 1           char *data = (char *)h->data;
856 1           int nested = h->wr_locked;
857 1 50         if (!nested) { buf_rwlock_wrlock(hdr); buf_seqlock_write_begin(&hdr->seq); }
858 1           memcpy(data + from * esz, in, count * esz);
859 1 50         if (!nested) { buf_seqlock_write_end(&hdr->seq); buf_rwlock_wrunlock(hdr); }
860 1           return 1;
861             }
862              
863             #else /* numeric */
864              
865 9           static int BUF_FN(get_slice)(BufHandle *h, uint64_t from, uint64_t count,
  1            
  1            
  0            
  7            
  0            
  0            
  0            
  0            
  0            
  0            
866             BUF_ELEM_TYPE *out) {
867 9           BufHeader *hdr = h->hdr;
  1            
  1            
  0            
  7            
  0            
  0            
  0            
  0            
  0            
  0            
868 9 50         if (count > hdr->capacity || from > hdr->capacity - count) return 0;
  1 50          
  1 50          
  0 50          
  7 0          
  0 0          
  0 100          
  0 100          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
869 7           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  1            
  1            
  0            
  5            
  0            
  0            
  0            
  0            
  0            
  0            
870 7 50         if (h->wr_locked) {
  1 50          
  1 0          
  0 100          
  5 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
871 1           memcpy(out, &data[from], count * sizeof(BUF_ELEM_TYPE));
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  0            
  0            
872             } else {
873             uint32_t seq_start;
874             do {
875 6           seq_start = buf_seqlock_read_begin(hdr);
  1            
  1            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
  0            
876 6           memcpy(out, &data[from], count * sizeof(BUF_ELEM_TYPE));
  1            
  1            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
  0            
877 6 50         } while (buf_seqlock_read_retry(&hdr->seq, seq_start));
  1 50          
  1 0          
  0 50          
  4 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
878             }
879 7           return 1;
  1            
  1            
  0            
  5            
  0            
  0            
  0            
  0            
  0            
  0            
880             }
881              
882 6           static int BUF_FN(set_slice)(BufHandle *h, uint64_t from, uint64_t count,
  1            
  1            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
  0            
883             const BUF_ELEM_TYPE *in) {
884 6           BufHeader *hdr = h->hdr;
  1            
  1            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
  0            
885 6 50         if (count > hdr->capacity || from > hdr->capacity - count) return 0;
  1 50          
  1 50          
  0 50          
  4 0          
  0 0          
  0 50          
  0 100          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
886 5           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  1            
  1            
  0            
  3            
  0            
  0            
  0            
  0            
  0            
  0            
887 5           int nested = h->wr_locked;
  1            
  1            
  0            
  3            
  0            
  0            
  0            
  0            
  0            
  0            
888 5 50         if (!nested) { buf_rwlock_wrlock(hdr); buf_seqlock_write_begin(&hdr->seq); }
  1 50          
  1 0          
  0 100          
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
889 5           memcpy(&data[from], in, count * sizeof(BUF_ELEM_TYPE));
  1            
  1            
  0            
  3            
  0            
  0            
  0            
  0            
  0            
  0            
890 5 50         if (!nested) { buf_seqlock_write_end(&hdr->seq); buf_rwlock_wrunlock(hdr); }
  1 50          
  1 0          
  0 100          
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
891 5           return 1;
  1            
  1            
  0            
  3            
  0            
  0            
  0            
  0            
  0            
  0            
892             }
893              
894             #endif /* BUF_IS_FIXEDSTR */
895              
896             /* ---- Fill ---- */
897              
898             #ifdef BUF_IS_FIXEDSTR
899              
900 2           static void BUF_FN(fill)(BufHandle *h, const char *val, uint32_t len) {
901 2           BufHeader *hdr = h->hdr;
902 2           uint32_t esz = hdr->elem_size;
903 2           char *data = (char *)h->data;
904 2           int nested = h->wr_locked;
905 2 50         if (!nested) { buf_rwlock_wrlock(hdr); buf_seqlock_write_begin(&hdr->seq); }
906 2           uint32_t copy_len = len < esz ? len : esz;
907 2           memset(data, 0, (size_t)hdr->capacity * esz);
908 27 100         for (uint64_t i = 0; i < hdr->capacity; i++)
909 25           memcpy(data + i * esz, val, copy_len);
910 2 50         if (!nested) { buf_seqlock_write_end(&hdr->seq); buf_rwlock_wrunlock(hdr); }
911 2           }
912              
913             #else
914              
915 10           static void BUF_FN(fill)(BufHandle *h, BUF_ELEM_TYPE val) {
  1            
  1            
  0            
  8            
  0            
  0            
  0            
  0            
  0            
  0            
916 10           BufHeader *hdr = h->hdr;
  1            
  1            
  0            
  8            
  0            
  0            
  0            
  0            
  0            
  0            
917 10           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  1            
  1            
  0            
  8            
  0            
  0            
  0            
  0            
  0            
  0            
918 10           int nested = h->wr_locked;
  1            
  1            
  0            
  8            
  0            
  0            
  0            
  0            
  0            
  0            
919 10 50         if (!nested) { buf_rwlock_wrlock(hdr); buf_seqlock_write_begin(&hdr->seq); }
  1 50          
  1 0          
  0 100          
  8 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
920 420 100         for (uint64_t i = 0; i < hdr->capacity; i++)
  51 100          
  11 0          
  0 100          
  358 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
921 410           data[i] = val;
  50            
  10            
  0            
  350            
  0            
  0            
  0            
  0            
  0            
  0            
922 10 50         if (!nested) { buf_seqlock_write_end(&hdr->seq); buf_rwlock_wrunlock(hdr); }
  1 50          
  1 0          
  0 100          
  8 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
923 10           }
  1            
  1            
  0            
  8            
  0            
  0            
  0            
  0            
  0            
  0            
924              
925             #endif
926              
927             /* ---- Atomic operations (integer types only) ---- */
928              
929             #ifdef BUF_HAS_COUNTERS
930              
931 4519           static BUF_ELEM_TYPE BUF_FN(incr)(BufHandle *h, uint64_t idx) {
  1            
  4510            
  1            
  1            
  1            
  1            
  2            
  2            
932 4519 50         if (idx >= h->hdr->capacity) return 0;
  1 50          
  4510 50          
  1 50          
  1 50          
  1 50          
  1 50          
  2 50          
  2            
933 4519           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  1            
  4510            
  1            
  1            
  1            
  1            
  2            
  2            
934 4519           return __atomic_add_fetch(&data[idx], 1, __ATOMIC_RELAXED);
  1            
  4510            
  1            
  1            
  1            
  1            
  2            
  2            
935             }
936              
937 13           static BUF_ELEM_TYPE BUF_FN(decr)(BufHandle *h, uint64_t idx) {
  1            
  4            
  1            
  1            
  1            
  1            
  2            
  2            
938 13 50         if (idx >= h->hdr->capacity) return 0;
  1 50          
  4 50          
  1 50          
  1 50          
  1 50          
  1 50          
  2 50          
  2            
939 13           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  1            
  4            
  1            
  1            
  1            
  1            
  2            
  2            
940 13           return __atomic_sub_fetch(&data[idx], 1, __ATOMIC_RELAXED);
  1            
  4            
  1            
  1            
  1            
  1            
  2            
  2            
941             }
942              
943 14           static BUF_ELEM_TYPE BUF_FN(add)(BufHandle *h, uint64_t idx, BUF_ELEM_TYPE delta) {
  1            
  7            
  1            
  1            
  1            
  1            
  1            
  1            
944 14 50         if (idx >= h->hdr->capacity) return 0;
  1 50          
  7 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1            
945 14           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  1            
  7            
  1            
  1            
  1            
  1            
  1            
  1            
946 14           return __atomic_add_fetch(&data[idx], delta, __ATOMIC_RELAXED);
  1            
  7            
  1            
  1            
  1            
  1            
  1            
  1            
947             }
948              
949 1175           static int BUF_FN(cas)(BufHandle *h, uint64_t idx,
  1            
  1168            
  1            
  1            
  1            
  1            
  1            
  1            
950             BUF_ELEM_TYPE expected, BUF_ELEM_TYPE desired) {
951 1175 50         if (idx >= h->hdr->capacity) return 0;
  1 50          
  1168 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1 50          
  1            
952 1175           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  1            
  1168            
  1            
  1            
  1            
  1            
  1            
  1            
953 1175           return __atomic_compare_exchange_n(&data[idx], &expected, desired,
  1            
  1168            
  1            
  1            
  1            
  1            
  1            
  1            
954             0, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
955             }
956              
957 6           static BUF_ELEM_TYPE BUF_FN(cmpxchg)(BufHandle *h, uint64_t idx,
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
958             BUF_ELEM_TYPE expected, BUF_ELEM_TYPE desired) {
959 6 0         if (idx >= h->hdr->capacity) return expected;
  0 50          
  6 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
960 6           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
961 6           __atomic_compare_exchange_n(&data[idx], &expected, desired,
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
962             0, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
963 6           return expected; /* on failure, expected is updated to the current value */
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
964             }
965              
966 4           static BUF_ELEM_TYPE BUF_FN(atomic_and)(BufHandle *h, uint64_t idx, BUF_ELEM_TYPE mask) {
  0            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
967 4 0         if (idx >= h->hdr->capacity) return 0;
  0 0          
  0 50          
  4 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
968 4           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  0            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
969 4           return __atomic_and_fetch(&data[idx], mask, __ATOMIC_RELAXED);
  0            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
970             }
971              
972 4           static BUF_ELEM_TYPE BUF_FN(atomic_or)(BufHandle *h, uint64_t idx, BUF_ELEM_TYPE mask) {
  0            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
973 4 0         if (idx >= h->hdr->capacity) return 0;
  0 0          
  0 50          
  4 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
974 4           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  0            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
975 4           return __atomic_or_fetch(&data[idx], mask, __ATOMIC_RELAXED);
  0            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
976             }
977              
978 2           static BUF_ELEM_TYPE BUF_FN(atomic_xor)(BufHandle *h, uint64_t idx, BUF_ELEM_TYPE mask) {
  0            
  0            
  2            
  0            
  0            
  0            
  0            
  0            
979 2 0         if (idx >= h->hdr->capacity) return 0;
  0 0          
  0 50          
  2 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
980 2           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  0            
  0            
  2            
  0            
  0            
  0            
  0            
  0            
981 2           return __atomic_xor_fetch(&data[idx], mask, __ATOMIC_RELAXED);
  0            
  0            
  2            
  0            
  0            
  0            
  0            
  0            
982             }
983              
984 1003           static int BUF_FN(add_slice)(BufHandle *h, uint64_t from, uint64_t count,
  0            
  1002            
  1            
  0            
  0            
  0            
  0            
  0            
985             const BUF_ELEM_TYPE *deltas) {
986 1003 0         if (count > h->hdr->capacity || from > h->hdr->capacity - count) return 0;
  0 0          
  1002 50          
  1 100          
  0 50          
  0 50          
  0 0          
  0 0          
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
987 1002           BUF_ELEM_TYPE *data = (BUF_ELEM_TYPE *)h->data;
  0            
  1001            
  1            
  0            
  0            
  0            
  0            
  0            
988 4007 0         for (uint64_t i = 0; i < count; i++)
  0 100          
  4004 100          
  3 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
989 3005           __atomic_add_fetch(&data[from + i], deltas[i], __ATOMIC_RELAXED);
  0            
  3003            
  2            
  0            
  0            
  0            
  0            
  0            
990 1002           return 1;
  0            
  1001            
  1            
  0            
  0            
  0            
  0            
  0            
991             }
992              
993             #endif /* BUF_HAS_COUNTERS */
994              
995             /* ---- Diagnostics ---- */
996              
997 11           static inline uint64_t BUF_FN(capacity)(BufHandle *h) {
  1            
  1            
  1            
  0            
  8            
  0            
  0            
  0            
  0            
  0            
  0            
998 11           return h->hdr->capacity;
  1            
  1            
  1            
  0            
  8            
  0            
  0            
  0            
  0            
  0            
  0            
999             }
1000              
1001 1           static inline uint64_t BUF_FN(mmap_size)(BufHandle *h) {
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  0            
  0            
1002 1           return (uint64_t)h->mmap_size;
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  0            
  0            
1003             }
1004              
1005 9           static inline uint32_t BUF_FN(elem_size)(BufHandle *h) {
  3            
  1            
  1            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
  0            
1006 9           return h->hdr->elem_size;
  3            
  1            
  1            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
  0            
1007             }
1008              
1009             /* ---- Raw pointer access (for passing to external C/XS code) ---- */
1010              
1011 2           static inline void *BUF_FN(ptr)(BufHandle *h) {
  0            
  0            
  0            
  0            
  2            
  0            
  0            
  0            
  0            
  0            
  0            
1012 2           return h->data;
  0            
  0            
  0            
  0            
  2            
  0            
  0            
  0            
  0            
  0            
  0            
1013             }
1014              
1015 4           static inline void *BUF_FN(ptr_at)(BufHandle *h, uint64_t idx) {
  0            
  0            
  0            
  0            
  4            
  0            
  0            
  0            
  0            
  0            
  0            
1016 4 0         if (idx >= h->hdr->capacity) return NULL;
  0 0          
  0 0          
  0 0          
  0 100          
  4 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
1017 3           return (char *)h->data + idx * h->hdr->elem_size;
  0            
  0            
  0            
  0            
  3            
  0            
  0            
  0            
  0            
  0            
  0            
1018             }
1019              
1020             /* ---- Explicit locking for batch operations ---- */
1021              
1022 8           static inline void BUF_FN(lock_wr)(BufHandle *h) {
  2            
  0            
  0            
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
1023 8           buf_rwlock_wrlock(h->hdr);
  2            
  0            
  0            
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
1024 8           buf_seqlock_write_begin(&h->hdr->seq);
  2            
  0            
  0            
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
1025 8           h->wr_locked = 1;
  2            
  0            
  0            
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
1026 8           }
  2            
  0            
  0            
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
1027              
1028 8           static inline void BUF_FN(unlock_wr)(BufHandle *h) {
  2            
  0            
  0            
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
1029 8           h->wr_locked = 0;
  2            
  0            
  0            
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
1030 8           buf_seqlock_write_end(&h->hdr->seq);
  2            
  0            
  0            
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
1031 8           buf_rwlock_wrunlock(h->hdr);
  2            
  0            
  0            
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
1032 8           }
  2            
  0            
  0            
  0            
  6            
  0            
  0            
  0            
  0            
  0            
  0            
1033              
1034 1           static inline void BUF_FN(lock_rd)(BufHandle *h) {
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  0            
  0            
1035 1           buf_rwlock_rdlock(h->hdr);
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  0            
  0            
1036 1           }
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  0            
  0            
1037              
1038 1           static inline void BUF_FN(unlock_rd)(BufHandle *h) {
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  0            
  0            
1039 1           buf_rwlock_rdunlock(h->hdr);
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  0            
  0            
1040 1           }
  0            
  0            
  0            
  0            
  1            
  0            
  0            
  0            
  0            
  0            
  0            
1041              
1042             /* ---- Cleanup ---- */
1043              
1044             #undef BUF_PASTE2
1045             #undef BUF_PASTE
1046             #undef BUF_FN
1047             #undef BUF_PREFIX
1048             #undef BUF_ELEM_TYPE
1049             #undef BUF_ELEM_SIZE
1050             #undef BUF_VARIANT_ID
1051             #ifdef BUF_HAS_COUNTERS
1052             #undef BUF_HAS_COUNTERS
1053             #endif
1054             #ifdef BUF_IS_FLOAT
1055             #undef BUF_IS_FLOAT
1056             #endif
1057             #ifdef BUF_IS_FIXEDSTR
1058             #undef BUF_IS_FIXEDSTR
1059             #endif