Compare commits
No commits in common. 'master' and 'threadsafe' have entirely different histories.
master
...
threadsafe
@ -1,22 +0,0 @@ |
|||||||
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. |
|
||||||
|
|
@ -0,0 +1,95 @@ |
|||||||
|
#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; |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
#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); |
@ -1,9 +0,0 @@ |
|||||||
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. |
|
@ -1,197 +0,0 @@ |
|||||||
#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 |
|
||||||
} |
|
@ -1,124 +0,0 @@ |
|||||||
/**
|
|
||||||
* @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
|
|
@ -1,10 +0,0 @@ |
|||||||
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. |
|
@ -1,260 +0,0 @@ |
|||||||
#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 |
|
||||||
} |
|
@ -1,151 +0,0 @@ |
|||||||
/**
|
|
||||||
* @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
|
|
@ -1,14 +0,0 @@ |
|||||||
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. |
|
@ -1,33 +0,0 @@ |
|||||||
#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; |
|
||||||
} |
|
@ -1,39 +0,0 @@ |
|||||||
/**
|
|
||||||
* @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); |
|
@ -1,11 +0,0 @@ |
|||||||
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). |
|
@ -1,64 +0,0 @@ |
|||||||
#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; |
|
||||||
} |
|
@ -1,31 +0,0 @@ |
|||||||
/**
|
|
||||||
* @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…
Reference in new issue