#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 (aconsume(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