burkey.co est. a long time ago

docs / libflint / bytering


Lock-free, embedded-friendly byte ring buffer (circular buffer) with no dynamic allocation. Designed for single-producer single-consumer (SPSC) use cases such as UART RX/TX buffers, sensor data streams, and DMA staging. Requires power-of-2 capacity for efficient index wrapping via bitmask. One slot is sacrificed to distinguish full from empty, so a capacity of N holds N-1 bytes.

Structs

ByteRing

typedef struct {
    uint8_t *buf;
    size_t capacity;
    size_t mask;
    volatile size_t head;
    volatile size_t tail;
} ByteRing;

head and tail are volatile to support ISR/main-loop SPSC usage without locks. The caller owns the backing buffer — ByteRing does not allocate or free it.

Functions

br_init

Initializes the byte ring buffer with a caller-provided backing buffer. capacity must be a power of 2. Returns 0 on success, -1 on error (NULL pointer, zero capacity, or non-power-of-2 capacity).

int br_init(ByteRing *br, uint8_t *buf, size_t capacity);

/* Usage */
uint8_t buf[64];
ByteRing br;
br_init(&br, buf, 64);

br_write

Writes a single byte to the buffer. Returns 0 on success, -1 if the buffer is full.

int br_write(ByteRing *br, uint8_t byte);

/* Usage */
br_write(&br, 0xAA);

br_read

Reads and removes the oldest byte from the buffer, storing it in byte. Returns 0 on success, -1 if the buffer is empty.

int br_read(ByteRing *br, uint8_t *byte);

/* Usage */
uint8_t byte;
br_read(&br, &byte);

br_peek

Reads the oldest byte without removing it, storing it in byte. Returns 0 on success, -1 if the buffer is empty.

int br_peek(ByteRing *br, uint8_t *byte);

/* Usage */
uint8_t byte;
br_peek(&br, &byte);

br_bulk_write

Writes up to n bytes from src into the buffer. Returns the number of bytes actually written, which may be less than n if the buffer fills up. Returns 0 if br or src is NULL.

size_t br_bulk_write(ByteRing *br, size_t n, const uint8_t *src);

/* Usage */
uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
size_t written = br_bulk_write(&br, 4, data);

br_bulk_read

Reads up to n bytes from the buffer into dest. Returns the number of bytes actually read, which may be less than n if the buffer doesn't contain enough data. Returns 0 if br or dest is NULL.

size_t br_bulk_read(ByteRing *br, size_t n, uint8_t *dest);

/* Usage */
uint8_t dest[4];
size_t read = br_bulk_read(&br, 4, dest);

br_reset

Resets the buffer to empty by setting head and tail to 0. Does not zero the backing buffer. The buffer can be reused after reset.

void br_reset(ByteRing *br);

Macros

br_capacity

Returns the total capacity of the buffer.

#define br_capacity(br) ((br)->capacity)

br_available

Returns the number of bytes available to read.

#define br_available(br) (((br)->tail - (br)->head) & (br)->mask)

br_free_space

Returns the number of bytes that can be written. This is capacity - 1 - available because one slot is reserved to distinguish full from empty.

#define br_free_space(br) ((br)->capacity - 1 - br_available(br))

← libflint docs