1/* @title: Single-Producer Single-Consumer Lock-Free FIFO Ring */
2#pragma once
3#include <atomic.h>
4#include <stdbool.h>
5#include <stddef.h>
6#include <stdint.h>
7
8struct spsc_fifo_ring {
9 atomic_size_t head; /* Written by Producer */
10 atomic_size_t tail; /* Written by Consumer */
11 size_t size; /* Capacity (pow2) */
12 size_t mask; /* size - 1 */
13 uint8_t *data;
14};
15
16#define SPSC_FIFO_RING_INIT \
17 (struct spsc_fifo_ring) { \
18 .head = 0, .tail = 0, .size = 0, .mask = 0, .data = NULL \
19 }
20
21/* (rounded to power of 2) */
22bool spsc_fifo_ring_init(struct spsc_fifo_ring *fifo, size_t size);
23
24void spsc_fifo_ring_init_with(struct spsc_fifo_ring *fifo, void *buffer,
25 size_t size);
26
27void spsc_fifo_ring_destroy(struct spsc_fifo_ring *fifo);
28
29size_t spsc_fifo_ring_write(struct spsc_fifo_ring *fifo, const void *src,
30 size_t len);
31
32size_t spsc_fifo_ring_read(struct spsc_fifo_ring *fifo, void *dst, size_t len);
33
34/* peek up to `len` bytes into `dst` without consuming */
35size_t spsc_fifo_ring_peek(const struct spsc_fifo_ring *fifo, void *dst,
36 size_t len);
37
38bool spsc_fifo_ring_push_ptr(struct spsc_fifo_ring *fifo, const void *ptr);
39bool spsc_fifo_ring_pop_ptr(struct spsc_fifo_ring *fifo, void **out_ptr);
40
41static inline size_t spsc_fifo_ring_len(const struct spsc_fifo_ring *fifo) {
42 size_t h = atomic_load_acq(&fifo->head);
43 size_t t = atomic_load_rlx(&fifo->tail);
44 return h - t;
45}
46
47static inline size_t spsc_fifo_ring_avail(const struct spsc_fifo_ring *fifo) {
48 return fifo->size - spsc_fifo_ring_len(fifo);
49}
50
51static inline bool spsc_fifo_ring_is_empty(const struct spsc_fifo_ring *fifo) {
52 return spsc_fifo_ring_len(fifo) == 0;
53}
54
55static inline bool spsc_fifo_ring_is_full(const struct spsc_fifo_ring *fifo) {
56 return spsc_fifo_ring_avail(fifo) == 0;
57}
58
59static inline void spsc_fifo_ring_reset(struct spsc_fifo_ring *fifo) {
60 atomic_store_rlx(&fifo->head, 0);
61 atomic_store_rlx(&fifo->tail, 0);
62}
63