diff --git a/README.md b/README.md index aa489a0..5137ed1 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,15 @@ -Circular byte buffer -==================== +Buffers +======= -**(and other buffers)** +This is a collection of useful buffer modules for C programs. -This is a circular buffer implementation, useful mainly for embedded systems (buffer for UART RX/TX queues etc). +- **circbuf** - generic circular buffer, queue, stack +- **matcher** - pattern matcher for character streams +- **meanbuf** - averaging (smoothing) buffer. -It should be reliable with producent / consumer threads (no race conditions, as no length variable is used). - -A Matching buffer, and Averaging buffer are also included—see their header files for instructions. - - -Usage ------ - -```c -#include -#include "circbuf.h" - -circbuf_t cb; // buffer instance - -void main() -{ - char c; - cbuf_init(&cb, 32); // init the buffer - - // now it's ready for use! - - cbuf_write(&cb, 'A'); - - if (cbuf_read(&cb, &c)) { - printf("%c", c); - } - - cbuf_deinit(&cb); // free the backing array (in embedded system you don't usually need to, allocate once and keep it) -} -``` - -Most functions return a success flag (true - success), so make sure to check the return values. - -False is returned on buffer overflow / underflow. See the header file for details. +*Please see READMEs in the project folders for more details.* +The buffers were developed for embedded microcontrollers (STM32, AVR), +but can be used anywhere, they are fully portable. +Pull requests to add new buffers or improve the current ones are welcome! diff --git a/circbuf.c b/circbuf.c deleted file mode 100644 index 4236ba3..0000000 --- a/circbuf.c +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include -#include -#include - -#include "circbuf.h" - - -void cbuf_init(circbuf_t *inst, size_t capacity) -{ - inst->buf = malloc(capacity); - inst->cap = capacity; - - cbuf_clear(inst); -} - - -void cbuf_deinit(circbuf_t *inst) -{ - if (inst->buf != NULL) { - free(inst->buf); - } -} - - -bool cbuf_full(circbuf_t *inst) -{ - return (inst->lr == inst->nw); -} - - -static bool can_read_at(circbuf_t *inst, size_t i) -{ - if (inst->lr < inst->nw) { - return i > inst->lr && i < inst->nw; - } else { - // NW < LR - return (i < inst->cap && i > inst->lr) || i < inst->nw; - } -} - - -bool cbuf_empty(circbuf_t *inst) -{ - return ((inst->lr + 1) % inst->cap) == inst->nw; -} - - -bool cbuf_write(circbuf_t *inst, uint8_t b) -{ - if (cbuf_full(inst)) return false; - - inst->buf[inst->nw] = b; - - // increment - inst->nw++; - if (inst->nw == inst->cap) inst->nw = 0; - - return true; -} - - -bool cbuf_read(circbuf_t *inst, uint8_t *b) -{ - if (cbuf_empty(inst)) return false; - - // increment - inst->lr++; - if (inst->lr == inst->cap) inst->lr = 0; - - *b = inst->buf[inst->lr]; - - //zero out the read byte (for debug) - inst->buf[inst->lr] = 0; - - return true; -} - - -bool cbuf_peek(circbuf_t *inst, size_t nth, uint8_t *b) -{ - // check if can read there (can't use modulo, could "wrap") - size_t n = inst->lr; - for (size_t i = 0; i <= nth; i++) { - // increment N - n++; - if (n == inst->cap) n = 0; - - if (!can_read_at(inst, n)) return false; - } - - *b = inst->buf[n]; - return true; -} - - -void cbuf_clear(circbuf_t *inst) -{ - inst->lr = inst->cap - 1; - inst->nw = 0; -} diff --git a/circbuf.h b/circbuf.h deleted file mode 100644 index 4e15bfb..0000000 --- a/circbuf.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include -#include -#include - -// Circular Character Buffer implementation - -typedef struct CircularBuffer_struct { - uint8_t *buf; - size_t cap; - size_t lr; // last read pos - size_t nw; // next write pos -} circbuf_t; - - -/** Init a buffer */ -void cbuf_init(circbuf_t *inst, size_t capacity); - - -/** Deinit a buffer (free the memory) */ -void cbuf_deinit(circbuf_t *inst); - - -/** Test for full buffer */ -bool cbuf_full(circbuf_t *inst); - - -/** Test for empty buffer */ -bool cbuf_empty(circbuf_t *inst); - - -/** Write a byte to buffer, returns success */ -bool cbuf_write(circbuf_t *inst, uint8_t b); - - -/** - * Read a byte from the buffer, return susccess. - * If `b` is NULL, the read byte is discarded. - */ -bool cbuf_read(circbuf_t *inst, uint8_t *b); - - -/** - * Get n-th byte from the buffer, without removing it. - * Returns false if offset is invalid (ie. empty buffer). - */ -bool cbuf_peek(circbuf_t *inst, size_t nth, uint8_t *b); - - -/** Remove all data from buffer */ -void cbuf_clear(circbuf_t *inst); diff --git a/circbuf/README.md b/circbuf/README.md new file mode 100644 index 0000000..96263c0 --- /dev/null +++ b/circbuf/README.md @@ -0,0 +1,51 @@ +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 +#include "circbuf.h" + +CircBuf *cb; // buffer instance + +void main() +{ + cb = cbuf_make(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. diff --git a/circbuf/circbuf.c b/circbuf/circbuf.c new file mode 100644 index 0000000..65d214e --- /dev/null +++ b/circbuf/circbuf.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include + +#include "circbuf.h" + +// --- Circbuf data structure ---- + +/** Offset in void* buffer */ +#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) +{ + 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 void read_buffer(const CircBuf *cb, size_t index, void *dest) +{ + memcpy(dest, PV_OFFS(cb->buf, cb->elem_size, index), cb->elem_size); +} + + +/** Create a cbuf */ +CircBuf *cbuf_make(size_t capacity, size_t elem_size) +{ + // add one, because one is always unused. + capacity++; + + // Allocate the structure + CircBuf *cb = malloc(sizeof(CircBuf)); + if (cb == NULL) { + return NULL; + } + + // 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); + } +} + + +/** Check if cbuf is full */ +bool cbuf_full(const CircBuf *cb) +{ + if (cb == NULL) return false; + + return (cb->lr == cb->nw); +} + + +/** Check if cbuf is empty */ +bool cbuf_empty(const CircBuf *cb) +{ + if (cb == NULL) return true; + + 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; + + 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; +} + + +/** 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; + + // increment + cb->lr++; + if (cb->lr == cb->cap) cb->lr = 0; + + read_buffer(cb, cb->lr, dest); + + return true; +} + + +/** Clear a cbuf */ +void cbuf_clear(CircBuf *cb) +{ + if (cb == NULL) return; + + cb->lr = cb->cap - 1; + cb->nw = 0; +} diff --git a/circbuf/circbuf.h b/circbuf/circbuf.h new file mode 100644 index 0000000..6e83f4f --- /dev/null +++ b/circbuf/circbuf.h @@ -0,0 +1,93 @@ +/** + * @file circbuf.h + * @author Ondřej Hruška, 2016 + * + * 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. + * + * ------------------------------------- + * + * NW LR + * append -> [][][][] -> pop + * <- push + * + * NW - next write pointer (stack base) + * LR - last read position (stack top) + * + * ------------------------------------- + * + * MIT license + */ + +#pragma once + +#include +#include +#include + + +typedef struct circbuf_struct CircBuf; + + +/** + * @brief Initialize a circular buffer. The buffer is malloc'd. + * @param capacity : buffer capacity + * @param elem_size : size of one element + * @return pointer to the buffer instance + */ +CircBuf *cbuf_make(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); + + +/** Test for full buffer */ +bool cbuf_full(const CircBuf *cb); + + +/** Test for empty buffer */ +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). + * + * @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 destionation. If NULL, value is discarded. + * @return success + */ +bool cbuf_pop(CircBuf *cb, void *dest); + + +/** @brief Remove all data from buffer */ +void cbuf_clear(CircBuf *cb); diff --git a/matcher.h b/matcher.h deleted file mode 100644 index 6aedcb7..0000000 --- a/matcher.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include -#include - -typedef struct { - const char *pattern; - size_t cursor; -} matcher_t; - - -/** - * Consume an incoming character. - * If this char was the last char of the pattern, returns true and rewinds. - * - * If the char is not in the pattern, resets match state. - * - * @returns true if the char concluded the expected pattern. - */ -bool matcher_test(matcher_t * mb, uint8_t b); diff --git a/matcher/README.md b/matcher/README.md new file mode 100644 index 0000000..7dec5a1 --- /dev/null +++ b/matcher/README.md @@ -0,0 +1,14 @@ +Character stream pattern matcher +================================ + +Matcher can be used for detecting a pattern in a stream of characters. +With each incoming character, call `matcher_test()`. + +It will return true if the character completed a match. + +Applications +------------ + +This buffer was designed for parsing responses to AT commands from ESP8266. + +It can be used for any similar purpose. diff --git a/matcher.c b/matcher/matcher.c similarity index 91% rename from matcher.c rename to matcher/matcher.c index 312a644..558d3c2 100644 --- a/matcher.c +++ b/matcher/matcher.c @@ -5,6 +5,11 @@ #include "matcher.h" +void matcher_reset(matcher_t *m) +{ + m->cursor = 0; +} + /** Handle incoming char. Returns true if this char completed the match. */ bool matcher_test(matcher_t * m, uint8_t b) diff --git a/matcher/matcher.h b/matcher/matcher.h new file mode 100644 index 0000000..4d71468 --- /dev/null +++ b/matcher/matcher.h @@ -0,0 +1,39 @@ +/** + * @file matcher.h + * @author Ondřej Hruška, 2016 + * + * String matching utility. + * + * Matcher can be used for detecting a pattern in a stream of characters. + * With each incoming character, call matcher_test(). + * + * It will return true if the character completed a match. + * + * MIT license + */ + +#pragma once + +#include +#include +#include + +typedef struct { + const char *pattern; + size_t cursor; +} matcher_t; + + +/** reset match progress */ +void matcher_reset(matcher_t *m); + + +/** + * Consume an incoming character. + * If this char was the last char of the pattern, returns true and resets matcher. + * + * If the char is not in the pattern, resets matcher. + * + * @returns true if the char concluded the expected pattern. + */ +bool matcher_test(matcher_t * mb, uint8_t b); diff --git a/meanbuf.h b/meanbuf.h deleted file mode 100644 index 4e655d9..0000000 --- a/meanbuf.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include - - -typedef struct { - float * buf; // buffer (allocated at init) - size_t cap; // capacity - size_t nw; // next write index - float mean; // updated on write -} meanbuf_t; - - -/** Init a buffer */ -void meanbuf_init(meanbuf_t *mb, size_t size); - -/** Deinit a buffer (free buffer array) */ -void meanbuf_deinit(meanbuf_t *mb); - -/** Add a value to the buffer. Returns current mean. */ -float meanbuf_add(meanbuf_t *mb, float f); diff --git a/meanbuf/README.md b/meanbuf/README.md new file mode 100644 index 0000000..9062838 --- /dev/null +++ b/meanbuf/README.md @@ -0,0 +1,11 @@ +Averaging float buffer +====================== + +*(You can adjust it to use doubles, if you prefer.)* + +The `meanbuf_create()` function allocates a buffer. + +You can then call `meanbuf_add()` to add a new value into the buffer (and remove the oldest). +This function returns the current average value. + +This buffer can be used for **signal smoothing** (such as from an analogue sensor). diff --git a/meanbuf.c b/meanbuf/meanbuf.c similarity index 63% rename from meanbuf.c rename to meanbuf/meanbuf.c index 65ccfa4..8fb9dcf 100644 --- a/meanbuf.c +++ b/meanbuf/meanbuf.c @@ -4,9 +4,19 @@ #include "meanbuf.h" +struct meanbuf_struct { + float * buf; // buffer (allocated at init) + size_t cap; // capacity + size_t nw; // next write index + float mean; // updated on write +}; + + /** Init a buffer */ -void meanbuf_init(meanbuf_t *mb, size_t size) +MeanBuf *meanbuf_create(size_t size) { + MeanBuf *mb = malloc(sizeof(MeanBuf)); + if (size < 1) size = 1; mb->buf = calloc(size, sizeof(float)); // calloc, so it starts with zeros. @@ -18,19 +28,25 @@ void meanbuf_init(meanbuf_t *mb, size_t size) for (uint16_t i = 0; i < size; i++) { mb->buf[i] = 0; } + + return mb; } -void meanbuf_deinit(meanbuf_t *mb) +void meanbuf_destroy(MeanBuf *mb) { + if (mb == NULL) return; + if (mb->buf != NULL) { free(mb->buf); } + + free(mb); } /** Add a value to the buffer. Returns current mean. */ -float meanbuf_add(meanbuf_t *mb, float f) +float meanbuf_add(MeanBuf *mb, float f) { // add sample mb->buf[mb->nw++] = f; diff --git a/meanbuf/meanbuf.h b/meanbuf/meanbuf.h new file mode 100644 index 0000000..1d96010 --- /dev/null +++ b/meanbuf/meanbuf.h @@ -0,0 +1,31 @@ +/** + * @file meanbuf.h + * @author Ondřej Hruška, 2016 + * + * Averaging float buffer. (You can adjust it to use doubles, if you prefer.) + * + * The meanbuf_create() function allocates a buffer. + * + * You can then call meanbuf_add() to add a new value into the buffer (and remove the oldest). + * This function returns the current average value. + * + * This buffer can be used for signal smoothing (such as from an analogue sensor). + * + * MIT license + */ + + +#pragma once +#include +#include + +typedef struct meanbuf_struct MeanBuf; + +/** Init a buffer */ +MeanBuf *meanbuf_create(size_t size); + +/** Deinit a buffer (free buffer array) */ +void meanbuf_destroy(MeanBuf *mb); + +/** Add a value to the buffer. Returns current mean. */ +float meanbuf_add(MeanBuf *mb, float f);