/* SPDX-License-Identifier: MIT */ #pragma once #ifndef rb_size_t #define rb_size_t int #endif /** Signatures of allocators */ typedef void *(*ALLOC_T)(rb_size_t); /** Signature of memcpy */ typedef void *(*MEMCPY_T)(void *, const void *, rb_size_t); /** Signature of free */ typedef void (*FREE_T)(void *); /** * @brief Ring buffer, also known as circular buffer. */ struct RingBuf { rb_size_t struct_size; /** Size of the struct */ rb_size_t capacity; /** The physical capacity of the entire ringbuf */ rb_size_t count; /** The number of elements in the buffer */ void *write_head; /** Address of the write head */ void *read_head; /** Address of the read head */ void *buffer; /** The actual data */ void *buffer_end; /** The end of the buffer */ } __attribute__((packed)); /* * Considerations: One hot encoding for the enum values * (8 bit each with -fshort-enums) */ /** Result of a write */ enum WriteResult { WriteOk, /** The write was successful */ Full, /** The buffer is full */ InsufficientSpace /** There is not enough space to write */ }; /** Result of a read */ enum ReadResult { Empty, /** The buffer is empty */ ReadOk, /** The read was successful */ InsufficientData /** There is not enough data to read */ }; /** * @brief Initialize the ring buffer * @param rb The ring buffer to initialize * @param capacity The capacity of the ring buffer * @param alloc The allocator function * @param struct_size The size of the struct * @return void */ void rb_init(struct RingBuf *rb, rb_size_t capacity, ALLOC_T alloc, rb_size_t struct_size); /** * @brief Clear the ring buffer * @details This function will reset the read and write heads to the beginning * of the buffer, and set the count to 0. It will not free the buffer. * @param rb The ring buffer */ void rb_clear(struct RingBuf *rb); /** * @brief Insert data to the ring buffer * @param rb The ring buffer * @param item The item to insert * @param memcpy_fn The memcpy function * @return WriteResult */ enum WriteResult rb_push_back(struct RingBuf *rb, const void *item, MEMCPY_T memcpy_fn); /** * @brief Insert multiple data to the ring buffer * * @details This function is more efficient than calling rb_push_back multiple * times. It only advances the write head once, and attempts to write all the * memory in one go. * * If n is greater than the capacity, it will return Full. * If the full write will overflow, it will wrap around. * * If the buffer is full, it will return Full and not write * anything. * * @param rb The ring buffer * @param items The items to insert * @param memcpy_fn The memcpy function * @param n The number of items to insert * @return WriteResult */ enum WriteResult rb_push_many(struct RingBuf *rb, const void *items, MEMCPY_T memcpy_fn, rb_size_t n); /** * @brief Read data from the ring buffer * @param rb The ring buffer * @param item The item to read into * @return ReadResult */ enum ReadResult rb_pop_front(struct RingBuf *rb, void *item, MEMCPY_T memcpy_fn); /** * @brief Free the ring buffer * @param rb The ring buffer * @param free The free function */ void rb_destroy(struct RingBuf *rb, void(free)());