Compare commits

..

10 Commits

  1. 22
      LICENSE
  2. 50
      README.md
  3. 95
      circbuf.c
  4. 50
      circbuf.h
  5. 9
      circbuf/README.md
  6. 197
      circbuf/circbuf.c
  7. 124
      circbuf/circbuf.h
  8. 10
      circbuf_deque/README.md
  9. 260
      circbuf_deque/circbuf.c
  10. 151
      circbuf_deque/circbuf.h
  11. 14
      matcher/README.md
  12. 33
      matcher/matcher.c
  13. 39
      matcher/matcher.h
  14. 11
      meanbuf/README.md
  15. 64
      meanbuf/meanbuf.c
  16. 31
      meanbuf/meanbuf.h

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Ondřej Hruška
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -1,44 +1,16 @@
Circular byte buffer Buffers
==================== =======
This is a circular buffer implementation, useful mainly for embedded systems (buffer for UART RX/TX queues etc). This is a collection of useful buffer modules for C programs.
It should be reliable with producent / consumer threads (no race conditions, as no length variable is used). - **circbuf** - generic circular buffer, queue, stack
- **matcher** - pattern matcher for character streams
- **meanbuf** - averaging (smoothing) buffer.
*Please see READMEs in the project folders for more details.*
Usage The buffers were developed for embedded microcontrollers (STM32, AVR),
----- but can be used anywhere, they are fully portable - only requirement is `malloc()`.
If you don't have malloc, you'll have to customize the init routines.
```c
#include <stdint.h>
#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.
License
-------
I don't care, public domain (I guess).
Pull requests to add new buffers or improve the current ones are welcome!

@ -1,95 +0,0 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <malloc.h>
#include "circbuf.h"
void cbuf_init(circbuf_t *inst, uint16_t length)
{
inst->buffer = malloc(length);
inst->capacity = length;
cbuf_clear(inst);
}
void cbuf_deinit(circbuf_t *inst)
{
if (inst->buffer != NULL) {
free(inst->buffer);
}
}
bool cbuf_full(circbuf_t *inst)
{
if (inst->read_pos == 0) {
return inst->write_pos == inst->capacity - 1;
} else {
return inst->write_pos == inst->read_pos;
}
}
bool cbuf_empty(circbuf_t *inst)
{
if (inst->write_pos == 0) {
return inst->read_pos == inst->capacity - 1;
} else {
return inst->read_pos == inst->write_pos - 1;
}
}
bool cbuf_write(circbuf_t *inst, uint8_t b)
{
if (cbuf_full(inst)) return false;
inst->buffer[inst->write_pos] = b;
inst->write_pos++;
// wrap
if (inst->write_pos >= inst->capacity) {
inst->write_pos = 0;
}
return true;
}
bool cbuf_read(circbuf_t *inst, uint8_t *b)
{
if (cbuf_empty(inst)) return false;
inst->read_pos++;
// wrap
if (inst->read_pos >= inst->capacity) {
inst->read_pos = 0;
}
*b = inst->buffer[inst->read_pos];
// zero out the read byte (for debug)
//inst->buffer[inst->read_pos] = 0;
return true;
}
bool cbuf_peek(circbuf_t *inst, uint8_t *b)
{
if (cbuf_empty(inst)) return false;
*b = inst->buffer[inst->read_pos];
return true;
}
void cbuf_clear(circbuf_t *inst)
{
inst->read_pos = inst->capacity - 1;
inst->write_pos = 0;
}

@ -1,50 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
// Circular Character Buffer implementation
typedef struct {
uint8_t *buffer;
uint16_t capacity;
uint16_t read_pos;
uint16_t write_pos;
} circbuf_t;
/** Init a buffer */
void cbuf_init(circbuf_t *inst, uint16_t length);
/** 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 byte at the read cursor, without incrementing it.
* False on empty.
*/
bool cbuf_peek(circbuf_t *inst, uint8_t *b);
/** Remove all data from buffer */
void cbuf_clear(circbuf_t *inst);

@ -0,0 +1,9 @@
Circular buffer
===============
This is a C implementation of a circular buffer, which can also be used
as a queue or stack.
To achieve thread safety (to some extent) in a producent-consumer situation,
there is no length variable, only write pointers. Due to not using a length variable,
one element in the buffer is always left unused.

@ -0,0 +1,197 @@
#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 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 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);
}
void cbuf_init(CircBuf *cb, void *buf, circbuf_size_t capacity, circbuf_size_t elem_size)
{
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->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;
}
bool cbuf_push(CircBuf *cb, const void *source)
{
if (cb == NULL || source == NULL || cbuf_full(cb)) {
return false;
}
write_buffer(cb, cb->nw, source);
// increment
cb->nw++;
if (cb->nw == cb->cap) { cb->nw = 0; }
return true;
}
bool cbuf_pop(CircBuf *cb, void *dest)
{
if (cb == NULL || dest == NULL || cbuf_empty(cb)) {
return false;
}
// increment
if (cb->nw == 0) {
cb->nw = cb->cap - 1;
} else {
cb->nw--;
}
read_buffer(cb, cb->nw, dest);
#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;
}
void cbuf_clear(CircBuf *cb)
{
if (cb == NULL) {
return;
}
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
}

@ -0,0 +1,124 @@
/**
* @file circbuf.h
* @author Ondřej Hruška, 2016,2023
*
* Circular buffer / queue / stack.
*
* 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
*/
#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
*
* @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);
/**
* @brief Push to front
* @param cb : buffer
* @param source : pointer to a value (will be copied)
* @return success
*/
bool cbuf_push(CircBuf *cb, const void *source);
/**
* @brief Pop from front
* @param cb : buffer
* @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,260 @@
#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 || cb->num_used == 0) {
return false;
}
cb->num_used--;
circbuf_size_t f = front_writepos(cb);
if (dest != NULL) {
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 || 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 || 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 || cb->num_used == 0) {
return false;
}
if (dest != NULL) {
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 || 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,151 @@
/**
* @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
// size_t can be replaced by a more suitable type for circbuf, e.g. uint8_t for tiny buffers
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

@ -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.

@ -0,0 +1,33 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#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)
{
// If mismatch, rewind (and check at 0)
if (m->pattern[m->cursor] != b) {
m->cursor = 0;
}
// Check for match
if (m->pattern[m->cursor] == b) {
// Good char
m->cursor++;
if (m->pattern[m->cursor] == 0) { // end of pattern
m->cursor = 0; // rewind
return true; // indicate success
}
}
return false;
}

@ -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 <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
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);

@ -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).

@ -0,0 +1,64 @@
#include <stdint.h>
#include <malloc.h>
#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 */
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.
mb->cap = size;
mb->nw = 0;
mb->mean = 0;
// clean buffer
for (uint16_t i = 0; i < size; i++) {
mb->buf[i] = 0;
}
return 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 *mb, float f)
{
// add sample
mb->buf[mb->nw++] = f;
if (mb->nw == mb->cap) mb->nw = 0;
// calculate average
float acc = 0;
for (size_t i = 0; i < mb->cap; i++) {
acc += mb->buf[i];
}
acc /= mb->cap;
return mb->mean = acc;
}

@ -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 <stdlib.h>
#include <stdint.h>
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);
Loading…
Cancel
Save