File Coverage

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