many changes :Py
This commit is contained in:
193
data/ChunkBuffer.h
Normal file
193
data/ChunkBuffer.h
Normal file
@@ -0,0 +1,193 @@
|
||||
#ifndef CHUNK_BUFFER_H
|
||||
#define CHUNK_BUFFER_H
|
||||
|
||||
#include "../Debug.h"
|
||||
|
||||
/**
|
||||
* linked list of several chunks that are either managed or unmanaged
|
||||
*/
|
||||
class ChunkBuffer {
|
||||
|
||||
public:
|
||||
|
||||
/** data of certain length */
|
||||
struct Data {
|
||||
const uint8_t* ptr;
|
||||
const uint16_t size;
|
||||
Data(const uint8_t* ptr, const uint16_t size) : ptr(ptr), size(size) {;}
|
||||
};
|
||||
|
||||
/** one data chunk within the buffer */
|
||||
struct Chunk {
|
||||
|
||||
bool managed;
|
||||
uint8_t* base; // do not modify this one!
|
||||
|
||||
uint8_t* ptr;
|
||||
uint16_t len;
|
||||
|
||||
Chunk* next = nullptr;
|
||||
|
||||
~Chunk() {
|
||||
debugMod("ChunkBuffer::Chunk", "dtor");
|
||||
if (managed) {
|
||||
free(base);
|
||||
base = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/** allocate a new managed chunk */
|
||||
static Chunk* alloc(const uint16_t size) {
|
||||
debugMod1("ChunkBuffer::Chunk", "alloc(%d)", size);
|
||||
Chunk* chunk = new Chunk();
|
||||
chunk->base = (uint8_t*)malloc(size);
|
||||
chunk->ptr = chunk->base;
|
||||
chunk->len = size;
|
||||
chunk->managed = true;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/** allocate a new wrapped chunk */
|
||||
static Chunk* wrap(uint8_t* ptr, const uint16_t size) {
|
||||
debugMod1("ChunkBuffer::Chunk", "alloc(%d)", size);
|
||||
Chunk* chunk = new Chunk();
|
||||
chunk->base = ptr;
|
||||
chunk->ptr = chunk->base;
|
||||
chunk->len = size;
|
||||
chunk->managed = false;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/** consume max(!) size bytes from this chunk */
|
||||
Data consume(const uint16_t size) {
|
||||
debugMod1("ChunkBuffer::Chunk", "consume(%d)", size);
|
||||
const uint16_t useBytes = min(size, this->len);
|
||||
Data res(ptr, useBytes);
|
||||
ptr += useBytes; // adjust pointer for next consume() call
|
||||
len -= useBytes; // adjuste len for next consume() call;
|
||||
return res;
|
||||
}
|
||||
|
||||
/** consume one byte, if available */
|
||||
int consumeByte() {
|
||||
if (len) {
|
||||
uint8_t res = *ptr;
|
||||
ptr += 1;
|
||||
len -= 1;
|
||||
return res;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return len == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Chunk() {;}
|
||||
|
||||
};
|
||||
|
||||
static uint16_t min(const uint16_t a, const uint16_t b) {
|
||||
return (a<b) ? (a) : (b);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Chunk* root;
|
||||
|
||||
public:
|
||||
|
||||
ChunkBuffer() : root(nullptr) {
|
||||
debugMod("ChunkBuffer", "ctor()");
|
||||
}
|
||||
|
||||
/** get a block of max(!) size bytes from the buffer. might return less */
|
||||
Data get(const uint16_t size) {
|
||||
|
||||
cleanup();
|
||||
|
||||
// something available?
|
||||
if (!root) {
|
||||
return Data(0,0);
|
||||
} else {
|
||||
return root->consume(size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int getByte() {
|
||||
|
||||
cleanup();
|
||||
|
||||
if (root && !root->empty()) {
|
||||
return root->consumeByte();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** append the given chunk to the buffer */
|
||||
void append(Chunk* c) {
|
||||
|
||||
debugMod("ChunkBuffer", "append()");
|
||||
|
||||
if (!root) {
|
||||
|
||||
// new chunk is the root
|
||||
root = c;
|
||||
|
||||
} else {
|
||||
|
||||
// append new chunk at the end
|
||||
Chunk* x = root;
|
||||
while (x->next) {x = x->next;}
|
||||
x->next = c;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** allocate a new managed chunk of the given size */
|
||||
Chunk* alloc(const uint16_t size) {
|
||||
debugMod1("ChunkBuffer", "alloc(%d)", size);
|
||||
return Chunk::alloc(size);
|
||||
}
|
||||
|
||||
/** get a new unmanaged wrapper chunk for the given data */
|
||||
Chunk* wrap(const uint8_t* data, const uint16_t size) {
|
||||
debugMod1("ChunkBuffer", "wrap(%d)", size);
|
||||
return Chunk::wrap((uint8_t*) data, size);
|
||||
}
|
||||
|
||||
/** number of available bytes */
|
||||
uint32_t available() const {
|
||||
uint32_t s = 0;
|
||||
Chunk* c = root;
|
||||
while(c) {
|
||||
s += c->len;
|
||||
c = c->next;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/** nothing available? */
|
||||
bool empty() const {
|
||||
return (!root || root->len == 0);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void cleanup() {
|
||||
|
||||
// current chunk empty? -> cleanup and proceed with next chunk
|
||||
if (root && root->empty()) {
|
||||
Chunk* old = root;
|
||||
root = old->next; // next chunk is the chunk (if any) after the current (empty) one
|
||||
delete old; // delete the empty chunk and free the data
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // CHUNK_BUFFER_H
|
||||
@@ -1,56 +1,97 @@
|
||||
#ifndef RINGBUFFER_H
|
||||
#define RINGBUFFER_H
|
||||
|
||||
#include "../Debug.h"
|
||||
|
||||
typedef void (*RingBufferFullCallback)();
|
||||
|
||||
|
||||
template <typename Scalar, int size> class RingBuffer {
|
||||
|
||||
private:
|
||||
|
||||
static constexpr const char* NAME = "RingBuffer";
|
||||
|
||||
Scalar data[size];
|
||||
|
||||
volatile size_t head;
|
||||
volatile size_t tail;
|
||||
volatile size_t used;
|
||||
volatile mutable size_t used;
|
||||
|
||||
public:
|
||||
|
||||
RingBuffer() {
|
||||
init();
|
||||
}
|
||||
|
||||
void init() {
|
||||
head = 0;
|
||||
tail = 0;
|
||||
used = 0;
|
||||
}
|
||||
|
||||
/** add one value to the buffer */
|
||||
void clear() {
|
||||
init();
|
||||
}
|
||||
|
||||
/** add one value to the buffer. blocks until space is available */
|
||||
void addBlocking(const Scalar value) {
|
||||
|
||||
while (used == size) {os_delay_us(1000);}
|
||||
|
||||
data[head] = value;
|
||||
head = (head + 1) % size;
|
||||
++used;
|
||||
}
|
||||
|
||||
/** add one value to the buffer */
|
||||
void addIgnore(const Scalar value) {
|
||||
|
||||
/** add one value to the buffer, if there is enough space */
|
||||
void addOrIgnore(const Scalar value) {
|
||||
if (used == size) {return;}
|
||||
|
||||
data[head] = value;
|
||||
head = (head + 1) % size;
|
||||
++used;
|
||||
}
|
||||
|
||||
/** add multiple values to the buffer */
|
||||
void add(const Scalar* value, const size_t len) {
|
||||
/** add one value to the buffer, or call the callback if there is no space available */
|
||||
void addOrCallback(const Scalar value, RingBufferFullCallback cb) {
|
||||
while(used == size) {cb();}
|
||||
data[head] = value;
|
||||
head = (head + 1) % size;
|
||||
++used;
|
||||
}
|
||||
|
||||
|
||||
/** add multiple values to the buffer. blocks until space is available */
|
||||
void addBlocking(const Scalar* value, const size_t len) {
|
||||
debugMod1(NAME, "addBlocking(%d)", len);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
add(value[i]);
|
||||
addBlocking(value[i]);
|
||||
}
|
||||
debugMod1(NAME, "used: %d", used);
|
||||
}
|
||||
|
||||
/** anything available? */
|
||||
bool hasNext() const {
|
||||
return used != 0;
|
||||
/** add multiple values to the buffer, if there is enough space */
|
||||
void addOrIgnore(const Scalar* value, const size_t len) {
|
||||
debugMod1(NAME, "addOrIgnore(%d)", len);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
addOrIgnore(value[i]);
|
||||
}
|
||||
debugMod1(NAME, "used: %d", used);
|
||||
}
|
||||
|
||||
/** add multiple values to the buffer, if there is enough space */
|
||||
void addOrCallback(const Scalar* value, const size_t len, RingBufferFullCallback cb) {
|
||||
debugMod1(NAME, "addOrCallback(%d)", len);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
addOrCallback(value[i], cb);
|
||||
}
|
||||
debugMod1(NAME, "used: %d", used);
|
||||
}
|
||||
|
||||
// /** anything available? */
|
||||
// bool hasNext() const {
|
||||
// return used != 0;
|
||||
// }
|
||||
|
||||
/** get the next value from the buffer */
|
||||
Scalar get() {
|
||||
const Scalar res = data[tail];
|
||||
tail = (tail + 1) % size;
|
||||
@@ -58,22 +99,33 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
const Scalar& getConst() {
|
||||
const Scalar& getConst() const {
|
||||
const uint8_t idx = tail;
|
||||
tail = (tail + 1) % size;
|
||||
--used;
|
||||
return data[idx];
|
||||
}
|
||||
|
||||
// /** true if the buffer is currently empty */
|
||||
// bool isEmpty() const {
|
||||
// return head == tail;
|
||||
// }
|
||||
/** true if the buffer is currently empty */
|
||||
bool empty() const {
|
||||
return used == 0;
|
||||
}
|
||||
|
||||
/** true if the buffer is currently full */
|
||||
bool full() const {
|
||||
return size == used;
|
||||
}
|
||||
|
||||
/** get the number of used elements within the buffer */
|
||||
size_t getNumUsed() const {
|
||||
return used;
|
||||
}
|
||||
|
||||
/** get the number of free elements within the buffer */
|
||||
size_t getNumFree() const {
|
||||
return size-used;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // RINGBUFFER_H
|
||||
|
||||
Reference in New Issue
Block a user