You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
7.9 KiB
C

// Resource/Inspiration:
// https://embedjournal.com/implementing-circular-buffer-embedded-c/
/*
Penguin's Circular Buffer -- a simple floating queue designed for low memory
usage (mainly for embedded)
This is a ring buffer with limited capabilities. It is meant as a container
for moving data. Normally included features such as checks to see if the
buffer is full or empty have been omitted because this type of buffer is
being implemented mainly for sensor data usage. Data is almost never read
individually, and even if it is, it isn't meant to be cleared on read. It is
a simple moving buffer that automatically writes over old data for the
purpose of keeping track of the most up to date data.
Notes:
- Initialization is mandatory using p_cb_<type>_init.
- If a circular buffer is not initialized, you will run into a LOT of
hard-to-debug problems. Just initialize it.
- By default, all cb types should be defined as disabled. This is to save
on size. Enable the ones you want to use.
Behavior:
- Oldest data is overwritten
- popping of data isn't implemented. If this feature is required, use a
ring buffer.
Acronyms:
- p: penguin
- pb: penguin buffer
- cb: circular buffer
- uX, where X is bit size: unsigned X bit integer
- iX, where X is bit size: signed X bit integer
*/
#ifndef _PCIRCULARBUFFER_H_
#define _PCIRCULARBUFFER_H_
#include "p_buffer.h"
#define DEFINE_CBUFFER(type) \
typedef struct cbuffer_##type##_t \
{ \
type* buffer; \
int head; \
int csize; \
int msize; \
\
PB_STATUS (*push)(struct cbuffer_##type##_t * cbuffer, type value); \
PB_STATUS (*empty)(struct cbuffer_##type##_t * cbuffer); \
PB_STATUS (*is_filled)(struct cbuffer_##type##_t * cbuffer); \
} cbuffer_##type##_t; \
\
PB_STATUS cbuffer_##type##_is_filled(cbuffer_##type##_t* cbuffer) \
{ \
return (cbuffer->csize == cbuffer->msize); \
} \
\
PB_STATUS cbuffer_##type##_push(cbuffer_##type##_t* cbuffer, type value) \
{ \
PB_STATUS ret = PB_GOOD; \
\
if (!cbuffer) \
{ \
ret = PB_NULL_BUFFER; \
} \
else \
{ \
cbuffer->csize = \
(cbuffer->csize >= cbuffer->msize ? cbuffer->msize \
: cbuffer->csize + 1); \
cbuffer->buffer[cbuffer->head] = value; \
cbuffer->head = (cbuffer->head + 1) % cbuffer->msize; \
} \
\
return ret; \
} \
PB_STATUS cbuffer_##type##_empty(cbuffer_##type##_t* cbuffer) \
{ \
PB_STATUS ret = PB_GOOD; \
do \
{ \
if (!cbuffer) \
{ \
ret = PB_NULL_BUFFER; \
break; \
} \
\
if (!cbuffer->buffer) \
{ \
ret = PB_NULL_DATA; \
break; \
} \
memset(cbuffer->buffer, 0, sizeof(type) * cbuffer->msize); \
cbuffer->head = 0; \
cbuffer->csize = 0; \
} while (0); \
return ret; \
} \
\
PB_STATUS cbuffer_##type##_init(cbuffer_##type##_t* circ_buffer, \
type* buff, int total_length) \
{ \
PB_STATUS ret = PB_GOOD; \
do \
{ \
if (!buff) \
{ \
ret = PB_NULL_DATA; \
break; \
} \
\
if (total_length > MAX_CBUFFER_SIZE || total_length <= 0) \
{ \
ret = PB_BAD_BUFFER_SIZE; \
break; \
} \
memset(circ_buffer, 0, sizeof(cbuffer_##type##_t)); \
memset(buff, 0, sizeof(type) * total_length); \
circ_buffer->buffer = buff; \
circ_buffer->msize = total_length; \
circ_buffer->csize = 0; \
circ_buffer->head = 0; \
circ_buffer->push = cbuffer_##type##_push; \
circ_buffer->empty = cbuffer_##type##_empty; \
circ_buffer->is_filled = cbuffer_##type##_is_filled; \
circ_buffer->empty(circ_buffer); \
} while (0); \
\
return ret; \
}
#endif