add simple ring buffer
This commit is contained in:
parent
35a2297ab2
commit
a7b9feaedc
4 changed files with 198 additions and 1 deletions
|
|
@ -16,6 +16,7 @@ file(GLOB TEST_SOURCES
|
||||||
./tests/ListTest.cpp
|
./tests/ListTest.cpp
|
||||||
./tests/MapTest.cpp
|
./tests/MapTest.cpp
|
||||||
./tests/TreeTest.cpp
|
./tests/TreeTest.cpp
|
||||||
|
./tests/RingBufferByte.cpp
|
||||||
./tests/Tests.cpp
|
./tests/Tests.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
0
Containers/public/LinearRingBuffer.hpp
Normal file
0
Containers/public/LinearRingBuffer.hpp
Normal file
127
Containers/public/RingBufferByte.hpp
Normal file
127
Containers/public/RingBufferByte.hpp
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
template <bool ALLOW_BUFFER_OVERRIDE>
|
||||||
|
class RingBufferByte {
|
||||||
|
public:
|
||||||
|
explicit RingBufferByte(size_t maxCapacity) { buffer_.resize(maxCapacity); }
|
||||||
|
|
||||||
|
~RingBufferByte() = default;
|
||||||
|
|
||||||
|
void pushBack(const uint8_t* buffer, size_t size) {
|
||||||
|
const auto REQUIRED_SIZE = size + sizeof(size);
|
||||||
|
const auto DEPOSIT = static_cast<int>(freeSpace()) - static_cast<int>(REQUIRED_SIZE);
|
||||||
|
|
||||||
|
if (DEPOSIT < 0) {
|
||||||
|
if (ALLOW_BUFFER_OVERRIDE) {
|
||||||
|
freeupSpace(static_cast<size_t>(-DEPOSIT));
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("ring buffer overflow");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
push(reinterpret_cast<const uint8_t*>(&size), sizeof(size));
|
||||||
|
push(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto frontSize() const -> size_t {
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
if (dataSize() < sizeof(size)) {
|
||||||
|
throw std::runtime_error("no data in the buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
peek(reinterpret_cast<uint8_t*>(&size), sizeof(size));
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto popFront(uint8_t* buffer) -> size_t {
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
if (dataSize() < sizeof(size)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pop(reinterpret_cast<uint8_t*>(&size), sizeof(size));
|
||||||
|
pop(buffer, size);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
end_ = 0;
|
||||||
|
start_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const { return start_ == end_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void push(const uint8_t* buffer, size_t size) {
|
||||||
|
for (size_t simpleIdx = 0; simpleIdx < size; simpleIdx++) {
|
||||||
|
buffer_[end_++] = buffer == nullptr ? 0 : buffer[simpleIdx];
|
||||||
|
|
||||||
|
if (end_ == buffer_.size()) {
|
||||||
|
end_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop(uint8_t* buffer, size_t size) {
|
||||||
|
for (size_t sampleIdx = 0; sampleIdx < size; sampleIdx++) {
|
||||||
|
if (buffer != nullptr) {
|
||||||
|
buffer[sampleIdx] = buffer_[start_];
|
||||||
|
}
|
||||||
|
|
||||||
|
start_++;
|
||||||
|
|
||||||
|
if (start_ == buffer_.size()) {
|
||||||
|
start_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void peek(uint8_t* buffer, size_t size) const {
|
||||||
|
auto iter = start_;
|
||||||
|
for (size_t sampleIdx = 0; sampleIdx < size; sampleIdx++) {
|
||||||
|
buffer[sampleIdx] = buffer_[iter++];
|
||||||
|
|
||||||
|
if (iter == buffer_.size()) {
|
||||||
|
iter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeupSpace(size_t additionalRequiredSpace) {
|
||||||
|
const auto CURRENT_DEPOSIT = freeSpace();
|
||||||
|
|
||||||
|
while (CURRENT_DEPOSIT + additionalRequiredSpace >= freeSpace()) {
|
||||||
|
popFront(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t freeSpace() const {
|
||||||
|
if (start_ > end_) {
|
||||||
|
return start_ - end_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto OUT = buffer_.size() - (end_ - start_);
|
||||||
|
return OUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t dataSize() const { return buffer_.size() - freeSpace(); }
|
||||||
|
|
||||||
|
[[nodiscard]] bool hasSpace(unsigned int length) const { return freeSpace() > length; }
|
||||||
|
|
||||||
|
std::vector<uint8_t> buffer_;
|
||||||
|
|
||||||
|
size_t end_ = 0;
|
||||||
|
size_t start_ = 0;
|
||||||
|
};
|
||||||
69
Containers/tests/RingBufferByte.cpp
Normal file
69
Containers/tests/RingBufferByte.cpp
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
|
||||||
|
#include "Tests.hpp"
|
||||||
|
#include "RingBufferByte.hpp"
|
||||||
|
|
||||||
|
SUITE(RingBufferBytes) {
|
||||||
|
TEST(Simple) {
|
||||||
|
|
||||||
|
using Data = std::vector<uint8_t>;
|
||||||
|
using DataStorage = std::vector<Data>;
|
||||||
|
|
||||||
|
constexpr auto BUFFER_SIZE = 395;
|
||||||
|
constexpr auto MAX_MSG_SIZE = 31;
|
||||||
|
constexpr auto ITERATIONS = 100000;
|
||||||
|
|
||||||
|
DataStorage pushed;
|
||||||
|
DataStorage popped;
|
||||||
|
|
||||||
|
RingBufferByte<true> buffer(BUFFER_SIZE);
|
||||||
|
|
||||||
|
Data randomSamples(MAX_MSG_SIZE);
|
||||||
|
|
||||||
|
for (auto idx = 0; idx < MAX_MSG_SIZE; idx++) {
|
||||||
|
randomSamples[idx] = (idx + 5 * 31) % 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto PUSH = [&](RingBufferByte<true>& buffer, DataStorage* storage) {
|
||||||
|
const Data& pushedSample = randomSamples;
|
||||||
|
|
||||||
|
buffer.pushBack(pushedSample.data(), pushedSample.size());
|
||||||
|
|
||||||
|
if (storage) {
|
||||||
|
storage->push_back(pushedSample);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto POP = [](RingBufferByte<true>& buffer, DataStorage* storage) {
|
||||||
|
const auto poppedSampleSize = buffer.frontSize();
|
||||||
|
|
||||||
|
Data poppedSample(poppedSampleSize);
|
||||||
|
|
||||||
|
const auto SIZE = buffer.popFront(poppedSample.data());
|
||||||
|
|
||||||
|
CHECK(poppedSample.size() == SIZE);
|
||||||
|
|
||||||
|
if (storage) {
|
||||||
|
storage->push_back(poppedSample);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto iteration = 0; iteration < ITERATIONS; iteration++) {
|
||||||
|
PUSH(buffer, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!buffer.empty()) {
|
||||||
|
POP(buffer, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto iteration = 0; iteration < ITERATIONS; iteration++) {
|
||||||
|
PUSH(buffer, &pushed);
|
||||||
|
POP(buffer, &popped);
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(pushed.size() == popped.size());
|
||||||
|
|
||||||
|
for (auto idx = 0; idx < popped.size(); idx++) {
|
||||||
|
CHECK(pushed[idx] == popped[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue