improved circbuf
This commit is contained in:
+2
-44
@@ -4,48 +4,6 @@ Circular buffer
|
||||
This is a C implementation of a circular buffer, which can also be used
|
||||
as a queue or stack.
|
||||
|
||||
Supported operations: `push`, `pop`, `append`.
|
||||
|
||||
To achieve thread safety (to some extent) in a producent-consumer situation,
|
||||
there is no length variable, only write pointers.
|
||||
|
||||
The buffer has a fixed size, values are copied when inserted. The buffer is created using `malloc`.
|
||||
If your platform does not support `malloc`, you will have to customize the initialization routine.
|
||||
|
||||
Applications
|
||||
------------
|
||||
|
||||
- Buffer for USARTs or similar communication interfaces
|
||||
- Event queue
|
||||
- Object stack
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
```c
|
||||
#include <stdint.h>
|
||||
#include "circbuf.h"
|
||||
|
||||
CircBuf *cb; // buffer instance
|
||||
|
||||
void main()
|
||||
{
|
||||
cb = cbuf_create(32, sizeof(char)); // create a char buffer of size 32.
|
||||
|
||||
// now it's ready for use!
|
||||
|
||||
// write functions return true on success:
|
||||
cbuf_append(cb, 'A'); // append A (using as a queue)
|
||||
cbuf_push(cb, 'B'); // push B (using as a stack)
|
||||
|
||||
// read all
|
||||
char c;
|
||||
while (cbuf_pop(cb, &c)) {
|
||||
printf("%c", c);
|
||||
}
|
||||
|
||||
cbuf_destroy(cb); // free the buffer
|
||||
}
|
||||
```
|
||||
|
||||
For details how to use each function, please read the header file.
|
||||
there is no length variable, only write pointers. Due to not using a length variable,
|
||||
one element in the buffer is always left unused.
|
||||
|
||||
+125
-95
@@ -1,7 +1,6 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "circbuf.h"
|
||||
|
||||
@@ -11,25 +10,15 @@
|
||||
#define PV_OFFS(pvBuf, elem_size, index) ((uint8_t*)(pvBuf) + ((elem_size)*(index)))
|
||||
|
||||
|
||||
// Instance structure
|
||||
struct circbuf_struct {
|
||||
void *buf;
|
||||
size_t elem_size;
|
||||
size_t cap;
|
||||
size_t lr; // last read pos
|
||||
size_t nw; // next write pos
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Write data to a CircBuf slot
|
||||
* @param cb : circbuf
|
||||
* @param index : slot index
|
||||
* @param source : data source
|
||||
*/
|
||||
static void write_buffer(CircBuf *cb, size_t index, const void *source)
|
||||
static void write_buffer(CircBuf *cb, circbuf_size_t index, const void *source)
|
||||
{
|
||||
memcpy(PV_OFFS(cb->buf, cb->elem_size, index), source, cb->elem_size);
|
||||
memcpy(PV_OFFS(cb->buf, cb->elem_size, index), source, cb->elem_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,129 +28,170 @@ static void write_buffer(CircBuf *cb, size_t index, const void *source)
|
||||
* @param index : slot index
|
||||
* @param dest : destination buffer
|
||||
*/
|
||||
static void read_buffer(const CircBuf *cb, size_t index, void *dest)
|
||||
static void read_buffer(const CircBuf *cb, circbuf_size_t index, void *dest)
|
||||
{
|
||||
memcpy(dest, PV_OFFS(cb->buf, cb->elem_size, index), cb->elem_size);
|
||||
memcpy(dest, PV_OFFS(cb->buf, cb->elem_size, index), cb->elem_size);
|
||||
}
|
||||
|
||||
|
||||
/** Create a cbuf */
|
||||
CircBuf *cbuf_create(size_t capacity, size_t elem_size)
|
||||
void cbuf_init(CircBuf *cb, void *buf, circbuf_size_t capacity, circbuf_size_t elem_size)
|
||||
{
|
||||
// add one, because one is always unused.
|
||||
capacity++;
|
||||
cb->buf = buf;
|
||||
|
||||
// Allocate the structure
|
||||
CircBuf *cb = malloc(sizeof(CircBuf));
|
||||
if (cb == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// set capacity, clear state
|
||||
cb->elem_size = elem_size;
|
||||
cb->cap = capacity;
|
||||
|
||||
// allocate the buffer
|
||||
cb->buf = malloc(capacity * elem_size);
|
||||
if (cb->buf == NULL) {
|
||||
free(cb); // fail - free structure.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// set capacity, clear state
|
||||
cb->elem_size = elem_size;
|
||||
cb->cap = capacity;
|
||||
cbuf_clear(cb);
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
|
||||
/** Release cbuf memory */
|
||||
void cbuf_destroy(CircBuf *cb)
|
||||
{
|
||||
if (cb != NULL) {
|
||||
if (cb->buf != NULL) {
|
||||
free(cb->buf);
|
||||
}
|
||||
|
||||
free(cb);
|
||||
}
|
||||
cbuf_clear(cb);
|
||||
}
|
||||
|
||||
|
||||
/** Check if cbuf is full */
|
||||
bool cbuf_full(const CircBuf *cb)
|
||||
{
|
||||
if (cb == NULL) return false;
|
||||
if (cb == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (cb->lr == cb->nw);
|
||||
return (cb->lr == cb->nw);
|
||||
}
|
||||
|
||||
|
||||
/** Check if cbuf is empty */
|
||||
bool cbuf_empty(const CircBuf *cb)
|
||||
{
|
||||
if (cb == NULL) return true;
|
||||
if (cb == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ((cb->lr + 1) % cb->cap) == cb->nw;
|
||||
return ((cb->lr + 1) % cb->cap) == cb->nw;
|
||||
}
|
||||
|
||||
|
||||
/** Write a byte to the buffer, if space left */
|
||||
bool cbuf_append(CircBuf *cb, const void *source)
|
||||
{
|
||||
if (cb == NULL) return false;
|
||||
if (source == NULL) return false;
|
||||
if (cbuf_full(cb)) return false;
|
||||
|
||||
write_buffer(cb, cb->nw, source);
|
||||
|
||||
// increment
|
||||
cb->nw++;
|
||||
if (cb->nw == cb->cap) cb->nw = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Push value to the end, like a stack. */
|
||||
bool cbuf_push(CircBuf *cb, const void *source)
|
||||
{
|
||||
if (cb == NULL) return false;
|
||||
if (source == NULL) return false;
|
||||
if (cbuf_full(cb)) return false;
|
||||
if (cb == NULL || source == NULL || cbuf_full(cb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_buffer(cb, cb->lr, source);
|
||||
write_buffer(cb, cb->nw, source);
|
||||
|
||||
// move lr back
|
||||
if (cb->lr == 0) {
|
||||
cb->lr = cb->cap - 1; // wrap to the end
|
||||
} else {
|
||||
cb->lr--;
|
||||
}
|
||||
// increment
|
||||
cb->nw++;
|
||||
if (cb->nw == cb->cap) { cb->nw = 0; }
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Read one byte, if not empty. */
|
||||
bool cbuf_pop(CircBuf *cb, void *dest)
|
||||
{
|
||||
if (cb == NULL || dest == NULL) return false;
|
||||
if (cbuf_empty(cb)) return false;
|
||||
if (cb == NULL || dest == NULL || cbuf_empty(cb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// increment
|
||||
cb->lr++;
|
||||
if (cb->lr == cb->cap) cb->lr = 0;
|
||||
// increment
|
||||
if (cb->nw == 0) {
|
||||
cb->nw = cb->cap - 1;
|
||||
} else {
|
||||
cb->nw--;
|
||||
}
|
||||
|
||||
read_buffer(cb, cb->lr, dest);
|
||||
read_buffer(cb, cb->nw, dest);
|
||||
|
||||
return true;
|
||||
#ifdef CIRCBUF_ZERO_FREE_SLOTS
|
||||
memset(PV_OFFS(cb->buf, cb->elem_size, cb->nw), 0, cb->elem_size);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool cbuf_peek(const CircBuf *cb, void *dest) {
|
||||
if (cb == NULL || dest == NULL || cbuf_empty(cb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
circbuf_size_t index = cb->nw;
|
||||
if (index == 0) {
|
||||
index = cb->cap - 1;
|
||||
} else {
|
||||
index--;
|
||||
}
|
||||
|
||||
read_buffer(cb, index, dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool cbuf_push_back(CircBuf *cb, const void *source)
|
||||
{
|
||||
if (cb == NULL || source == NULL || cbuf_full(cb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_buffer(cb, cb->lr, source);
|
||||
|
||||
// move lr back
|
||||
if (cb->lr == 0) {
|
||||
cb->lr = cb->cap - 1; // wrap to the end
|
||||
} else {
|
||||
cb->lr--;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool cbuf_peek_back(const CircBuf *cb, void *dest) {
|
||||
if (cb == NULL || dest == NULL || cbuf_empty(cb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
circbuf_size_t index = cb->lr;
|
||||
if (index == cb->cap - 1) {
|
||||
index = 0;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
|
||||
read_buffer(cb, index, dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool cbuf_pop_back(CircBuf *cb, void *dest)
|
||||
{
|
||||
if (cb == NULL || dest == NULL || cbuf_empty(cb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// increment
|
||||
cb->lr++;
|
||||
if (cb->lr == cb->cap) {
|
||||
cb->lr = 0;
|
||||
}
|
||||
|
||||
read_buffer(cb, cb->lr, dest);
|
||||
|
||||
#ifdef CIRCBUF_ZERO_FREE_SLOTS
|
||||
memset(PV_OFFS(cb->buf, cb->elem_size, cb->lr), 0, cb->elem_size);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Clear a cbuf */
|
||||
void cbuf_clear(CircBuf *cb)
|
||||
{
|
||||
if (cb == NULL) return;
|
||||
if (cb == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
cb->lr = cb->cap - 1;
|
||||
cb->nw = 0;
|
||||
cb->lr = cb->cap - 1;
|
||||
cb->nw = 0;
|
||||
|
||||
#ifdef CIRCBUF_ZERO_FREE_SLOTS
|
||||
memset(PV_OFFS(cb->buf, cb->elem_size, 0), 0, cb->cap * cb->elem_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
+64
-33
@@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @file circbuf.h
|
||||
* @author Ondřej Hruška, 2016
|
||||
* @author Ondřej Hruška, 2016,2023
|
||||
*
|
||||
* Circular buffer / queue / stack.
|
||||
* Slots are pre-allocated, values are copied into the buffer.
|
||||
*
|
||||
* The buffer may be used as a stack, event queue or a simple buffer.
|
||||
*
|
||||
@@ -21,35 +20,39 @@
|
||||
* MIT license
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef CIRCBUF_H
|
||||
#define CIRCBUF_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Enable to zero a freed slots after pop, useful for debugging
|
||||
#define CIRCBUF_ZERO_FREE_SLOTS
|
||||
|
||||
|
||||
typedef uint32_t circbuf_size_t;
|
||||
|
||||
/** Instance structure - public to allow static allocation, but consider the structure internal matter */
|
||||
struct circbuf_struct {
|
||||
void *buf;
|
||||
circbuf_size_t elem_size;
|
||||
circbuf_size_t cap;
|
||||
circbuf_size_t lr; // last read pos
|
||||
circbuf_size_t nw; // next write pos
|
||||
};
|
||||
|
||||
typedef struct circbuf_struct CircBuf;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize a circular buffer. The buffer is malloc'd.
|
||||
* @brief Initialize a circular buffer
|
||||
*
|
||||
* @param[in,out] cb - pointer to the buffer to init, can be statically or dynamically allocated
|
||||
* @param buf : backing buffer, can be statically or dynamically allocated
|
||||
* @param capacity : buffer capacity
|
||||
* @param elem_size : size of one element
|
||||
* @return pointer to the buffer instance
|
||||
*/
|
||||
CircBuf *cbuf_create(size_t capacity, size_t elem_size);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Destroy a buffer, freeing used memory.
|
||||
*
|
||||
* @attention
|
||||
* If the buffer items have malloc'd members, you have
|
||||
* to free them manually to avoid a memory leak.
|
||||
*
|
||||
* @param cb : buffer
|
||||
*/
|
||||
void cbuf_destroy(CircBuf *cb);
|
||||
void cbuf_init(CircBuf *cb, void *buf, circbuf_size_t capacity, circbuf_size_t elem_size);
|
||||
|
||||
|
||||
/** Test for full buffer */
|
||||
@@ -61,17 +64,7 @@ bool cbuf_empty(const CircBuf *cb);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Append a value to the buffer (FIFO)
|
||||
* @param cb : buffer
|
||||
* @param source : pointer to a value (will be copied)
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_append(CircBuf *cb, const void *source);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Push a value into the circbuf (LIFO).
|
||||
*
|
||||
* @brief Push to front
|
||||
* @param cb : buffer
|
||||
* @param source : pointer to a value (will be copied)
|
||||
* @return success
|
||||
@@ -80,14 +73,52 @@ bool cbuf_push(CircBuf *cb, const void *source);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read a value from the buffer, return susccess.
|
||||
*
|
||||
* @brief Pop from front
|
||||
* @param cb : buffer
|
||||
* @param dest : read destionation. If NULL, value is discarded.
|
||||
* @param dest : read destination. If NULL, value is discarded.
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_pop(CircBuf *cb, void *dest);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Push to end
|
||||
* @param cb : buffer
|
||||
* @param source : pointer to a value (will be copied)
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_push_back(CircBuf *cb, const void *source);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Pop from end
|
||||
* @param cb : buffer
|
||||
* @param dest : read destination. If NULL, value is discarded.
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_pop_back(CircBuf *cb, void *dest);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Copy the frontmost element without changing the buffer
|
||||
* @param cb : buffer
|
||||
* @param dest : read destination
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_peek(const CircBuf *cb, void *dest);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Copy the backmost element without changing the buffer
|
||||
* @param cb : buffer
|
||||
* @param dest : read destination
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_peek_back(const CircBuf *cb, void *dest);
|
||||
|
||||
|
||||
/** @brief Remove all data from buffer */
|
||||
void cbuf_clear(CircBuf *cb);
|
||||
|
||||
|
||||
#endif // CIRCBUF_H
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
Circular buffer (enhanced)
|
||||
==========================
|
||||
|
||||
This variant of a circular buffer uses static allocation.
|
||||
|
||||
This buffer is not meant for producer-consumer synchronization, it's geared towards use cases where the buffer is accessed from a single thread, such as to keep a running average.
|
||||
|
||||
The full capacity is utilized thanks to internally storing its length. The buffer has both front and back sets of push/pop/peek functions, as well as a way to iterate and mutate its elements.
|
||||
|
||||
See the header for usage details.
|
||||
@@ -0,0 +1,255 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "circbuf.h"
|
||||
|
||||
// --- Circbuf data structure ----
|
||||
|
||||
/** Offset in void* buffer */
|
||||
#define PV_OFFS(pvBuf, elem_size, index) ((uint8_t*)(pvBuf) + ((elem_size)*(index)))
|
||||
|
||||
|
||||
/**
|
||||
* @brief Write data to a CircBuf slot
|
||||
* @param cb : circbuf
|
||||
* @param index : slot index
|
||||
* @param source : data source
|
||||
*/
|
||||
static inline void write_buffer(CircBuf *cb, circbuf_size_t index, const void *source)
|
||||
{
|
||||
memcpy(PV_OFFS(cb->buf, cb->elem_size, index), source, cb->elem_size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Copy data from a CircBuf slot to a buffer
|
||||
* @param cb : circbuf
|
||||
* @param index : slot index
|
||||
* @param dest : destination buffer
|
||||
*/
|
||||
static inline void read_buffer(const CircBuf *cb, circbuf_size_t index, void *dest)
|
||||
{
|
||||
memcpy(dest, PV_OFFS(cb->buf, cb->elem_size, index), cb->elem_size);
|
||||
}
|
||||
|
||||
/** Get index of the front position (for write) */
|
||||
static inline circbuf_size_t front_writepos(const CircBuf *cb) {
|
||||
return (cb->back + cb->num_used) % cb->cap;
|
||||
}
|
||||
|
||||
|
||||
void cbuf_init(CircBuf *cb, void *buf, circbuf_size_t capacity, circbuf_size_t elem_size)
|
||||
{
|
||||
// allocate the buffer
|
||||
cb->buf = buf;
|
||||
|
||||
// set capacity, clear state
|
||||
cb->elem_size = elem_size;
|
||||
cb->cap = capacity;
|
||||
|
||||
cbuf_clear(cb);
|
||||
}
|
||||
|
||||
|
||||
/** Check if cbuf is full */
|
||||
bool cbuf_full(const CircBuf *cb)
|
||||
{
|
||||
if (cb == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cb->num_used == cb->cap;
|
||||
}
|
||||
|
||||
|
||||
/** Check if cbuf is empty */
|
||||
bool cbuf_empty(const CircBuf *cb)
|
||||
{
|
||||
if (cb == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return cb->num_used == 0;
|
||||
}
|
||||
|
||||
|
||||
/** Get the max capacity of the buffer */
|
||||
circbuf_size_t cbuf_capacity(const CircBuf *cb)
|
||||
{
|
||||
return cb->cap;
|
||||
}
|
||||
|
||||
|
||||
/** Get the current number of items in the buffer */
|
||||
circbuf_size_t cbuf_count(const CircBuf *cb)
|
||||
{
|
||||
return cb->num_used;
|
||||
}
|
||||
|
||||
|
||||
/** Push one element to the front_writepos */
|
||||
bool cbuf_push(CircBuf *cb, const void *source)
|
||||
{
|
||||
if (cb == NULL || source == NULL || cb->num_used == cb->cap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_buffer(cb, front_writepos(cb), source);
|
||||
|
||||
// increment
|
||||
cb->num_used++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Pop one element from the front_writepos */
|
||||
bool cbuf_pop(CircBuf *cb, void *dest)
|
||||
{
|
||||
if (cb == NULL || dest == NULL || cb->num_used == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cb->num_used--;
|
||||
|
||||
circbuf_size_t f = front_writepos(cb);
|
||||
read_buffer(cb, f, dest);
|
||||
|
||||
#ifdef CIRCBUF_ZERO_FREE_SLOTS
|
||||
memset(PV_OFFS(cb->buf, cb->elem_size, f), 0, cb->elem_size);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Peek at the front_writepos element */
|
||||
bool cbuf_peek(const CircBuf *cb, void *dest)
|
||||
{
|
||||
if (cb == NULL || dest == NULL || cb->num_used == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
circbuf_size_t f = (cb->back + cb->num_used - 1) % cb->cap;
|
||||
read_buffer(cb, f, dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void * cbuf_ptr(const CircBuf *cb)
|
||||
{
|
||||
if (cb == NULL || dest == NULL || cb->num_used == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
circbuf_size_t f = (cb->back + cb->num_used - 1) % cb->cap;
|
||||
return PV_OFFS(cb->buf, cb->elem_size, f);
|
||||
}
|
||||
|
||||
|
||||
/** Peek at the nth element (counted from back) */
|
||||
bool cbuf_nth(const CircBuf *cb, circbuf_size_t num, void *dest)
|
||||
{
|
||||
if (cb == NULL || dest == NULL || num > cb->num_used) {
|
||||
return false;
|
||||
}
|
||||
|
||||
circbuf_size_t index = (cb->back + num) % cb->cap;
|
||||
read_buffer(cb, index, dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void *cbuf_ptr_nth(const CircBuf *cb, circbuf_size_t num)
|
||||
{
|
||||
if (cb == NULL || dest == NULL || num > cb->num_used) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
circbuf_size_t index = (cb->back + num) % cb->cap;
|
||||
return PV_OFFS(cb->buf, cb->elem_size, index);
|
||||
}
|
||||
|
||||
|
||||
/** Push one element to the back */
|
||||
bool cbuf_push_back(CircBuf *cb, const void *source)
|
||||
{
|
||||
if (cb == NULL || source == NULL || cb->num_used == cb->cap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// move lr back
|
||||
if (cb->back == 0) {
|
||||
cb->back = cb->cap - 1; // wrap to the end
|
||||
} else {
|
||||
cb->back--;
|
||||
}
|
||||
cb->num_used++;
|
||||
|
||||
write_buffer(cb, cb->back, source);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Pop one element from the back */
|
||||
bool cbuf_pop_back(CircBuf *cb, void *dest)
|
||||
{
|
||||
if (cb == NULL || dest == NULL || cb->num_used == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
read_buffer(cb, cb->back, dest);
|
||||
|
||||
#ifdef CIRCBUF_ZERO_FREE_SLOTS
|
||||
memset(PV_OFFS(cb->buf, cb->elem_size, cb->back), 0, cb->elem_size);
|
||||
#endif
|
||||
|
||||
// increment
|
||||
cb->back++;
|
||||
if (cb->back == cb->cap) {
|
||||
cb->back = 0;
|
||||
}
|
||||
cb->num_used--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Pop one element from the back */
|
||||
bool cbuf_peek_back(const CircBuf *cb, void *dest)
|
||||
{
|
||||
if (cb == NULL || dest == NULL || cb->num_used == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
read_buffer(cb, cb->back, dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void* cbuf_ptr_back(const CircBuf *cb)
|
||||
{
|
||||
if (cb == NULL || dest == NULL || cb->num_used == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return PV_OFFS(cb->buf, cb->elem_size, cb->back);
|
||||
}
|
||||
|
||||
|
||||
/** Clear a cbuf */
|
||||
void cbuf_clear(CircBuf *cb)
|
||||
{
|
||||
if (cb == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
cb->num_used = 0;
|
||||
cb->back = 0;
|
||||
|
||||
#ifdef CIRCBUF_ZERO_FREE_SLOTS
|
||||
memset(PV_OFFS(cb->buf, cb->elem_size, 0), 0, cb->cap * cb->elem_size);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* @file circbuf.h
|
||||
* @author Ondřej Hruška, 2016,2023
|
||||
*
|
||||
* Circular buffer / queue / stack.
|
||||
* Slots are pre-allocated, values are copied into the buffer.
|
||||
*
|
||||
* The buffer may be used as a stack, event queue or a simple buffer.
|
||||
*
|
||||
* MIT license
|
||||
*/
|
||||
|
||||
#ifndef CIRCBUF_H
|
||||
#define CIRCBUF_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Enable to zero a freed slots after pop, useful for debugging
|
||||
#define CIRCBUF_ZERO_FREE_SLOTS
|
||||
|
||||
typedef uint32_t circbuf_size_t;
|
||||
|
||||
/** Instance structure - public to allow static allocation, but consider the structure internal matter */
|
||||
struct circbuf_struct {
|
||||
void *buf;
|
||||
circbuf_size_t num_used;
|
||||
circbuf_size_t elem_size;
|
||||
circbuf_size_t cap;
|
||||
circbuf_size_t back;
|
||||
// circbuf_size_t front;
|
||||
};
|
||||
|
||||
typedef struct circbuf_struct CircBuf;
|
||||
|
||||
/**
|
||||
* @brief Initialize a circular buffer
|
||||
*
|
||||
* @param[in,out] cb - pointer to the buffer to init, can be statically or dynamically allocated
|
||||
* @param buf : backing buffer, can be statically or dynamically allocated
|
||||
* @param capacity : buffer capacity
|
||||
* @param elem_size : size of one element
|
||||
*/
|
||||
void cbuf_init(CircBuf *cb, void *buf, circbuf_size_t capacity, circbuf_size_t elem_size);
|
||||
|
||||
|
||||
/** Test for full buffer */
|
||||
bool cbuf_full(const CircBuf *cb);
|
||||
|
||||
|
||||
/** Test for empty buffer */
|
||||
bool cbuf_empty(const CircBuf *cb);
|
||||
|
||||
|
||||
/** Get the max capacity of the buffer */
|
||||
circbuf_size_t cbuf_capacity(const CircBuf *cb);
|
||||
|
||||
|
||||
/** Get the current number of items in the buffer */
|
||||
circbuf_size_t cbuf_count(const CircBuf *cb);
|
||||
|
||||
|
||||
/** Peek at the nth element (counted from back) */
|
||||
bool cbuf_nth(const CircBuf *cb, circbuf_size_t num, void *dest);
|
||||
|
||||
|
||||
/** Get a mutable reference to nth element (counted from back) */
|
||||
void *cbuf_ptr_nth(const CircBuf *cb, circbuf_size_t num);
|
||||
|
||||
|
||||
/** @brief Remove all data from buffer */
|
||||
void cbuf_clear(CircBuf *cb);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Append a value to the buffer (FIFO)
|
||||
* @param cb : buffer
|
||||
* @param source : pointer to a value (will be copied)
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_push(CircBuf *cb, const void *source);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read a value from the buffer, return susccess.
|
||||
*
|
||||
* @param cb : buffer
|
||||
* @param dest : read destination. If NULL, value is discarded.
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_pop(CircBuf *cb, void *dest);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Copy the frontmost element without changing the buffer
|
||||
* @param cb : buffer
|
||||
* @param dest : read destination
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_peek(const CircBuf *cb, void *dest);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get a mutable reference to the front element
|
||||
* @param cb : buffer
|
||||
* @param dest : read destination
|
||||
* @return reference or NULL
|
||||
*/
|
||||
void * cbuf_ptr(const CircBuf *cb);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Push a value into the circbuf (LIFO).
|
||||
*
|
||||
* @param cb : buffer
|
||||
* @param source : pointer to a value (will be copied)
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_push_back(CircBuf *cb, const void *source);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read a value from the buffer, return susccess.
|
||||
*
|
||||
* @param cb : buffer
|
||||
* @param dest : read destination. If NULL, value is discarded.
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_pop_back(CircBuf *cb, void *dest);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Copy the backmost element without changing the buffer
|
||||
* @param cb : buffer
|
||||
* @param dest : read destination
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_peek_back(const CircBuf *cb, void *dest);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get a mutable reference to the backmost element
|
||||
* @param cb : buffer
|
||||
* @param dest : read destination
|
||||
* @return reference or NULL
|
||||
*/
|
||||
void* cbuf_ptr_back(const CircBuf *cb);
|
||||
|
||||
#endif // CIRCBUF_H
|
||||
Reference in New Issue
Block a user