Program Listing for File CircularBuffer.hpp
↰ Return to documentation for file (doc/include/CircularBuffer.hpp
)
#ifndef CIRCULAR_BUFFER_HPP_
#define CIRCULAR_BUFFER_HPP_
#include <optional>
#include <vector>
template <typename T>
class CircularBuffer {
public:
CircularBuffer(size_t capacity) : buf_(capacity), capacity_(capacity){};
CircularBuffer() : buf_(20), capacity_(20){};
inline size_t capacity() const { return capacity_; }
size_t size() const;
void push(T item);
std::optional<T> front() const;
std::optional<T> back() const;
void pop();
std::optional<T> popFront();
void resize(const size_t capacity);
void reset(const size_t capacity);
inline bool empty() const { return is_empty_; }
void clear();
void toVector(std::vector<T>& vector) const;
private:
// std::mutex mutex_;
// std::unique_ptr<T[capacity_]> buf_;
std::vector<T> buf_;
size_t capacity_;
size_t head_ = 0;
size_t tail_ = 0;
bool is_empty_ = true;
};
template <typename T>
void CircularBuffer<T>::push(T item) {
if (capacity_ == 0) {
return;
}
if (empty()) {
buf_[0] = item;
is_empty_ = false;
tail_ = 0;
head_ = 0;
return;
}
tail_ = (tail_ + 1) % capacity_; // increase tail_ pointer
if (head_ == tail_) {
head_ = (head_ + 1) % capacity_; // if tail reached head increase head_ pointer
}
buf_[tail_] = item;
is_empty_ = false;
return;
} // TODO:
template <typename T>
std::optional<T> CircularBuffer<T>::front() const {
if (empty()) {
return std::nullopt;
}
return buf_[head_];
}
template <typename T>
std::optional<T> CircularBuffer<T>::back() const {
if (empty()) {
return std::nullopt;
}
return buf_[tail_];
}
template <typename T>
void CircularBuffer<T>::pop() {
if (size() <= 1) {
clear();
return;
}
head_ = (head_ + 1) % capacity_;
}
template <typename T>
std::optional<T> CircularBuffer<T>::popFront() {
std::optional<T> f = front();
pop();
return f;
}
template <typename T>
size_t CircularBuffer<T>::size() const {
if (empty() || capacity_ == 0) {
return 0;
}
return (tail_ + capacity_ - head_) % capacity_ + 1;
}
template <typename T>
void CircularBuffer<T>::resize(const size_t capacity) {
size_t initial_size = size();
std::vector<T> vec;
toVector(vec);
size_t new_size = std::min(capacity, initial_size);
std::vector<T>(capacity).swap(buf_); // resize allocated memory to capacity.
capacity_ = capacity;
std::copy(vec.end() - new_size, vec.end(), buf_.begin()); // chops buf_ keeping new_len newest elements. buf is ordered, placing head_ at 0;
head_ = 0;
if (new_size == 0) {
tail_ = 0;
is_empty_ = true;
} else {
tail_ = new_size - 1;
}
return;
}
template <typename T>
void CircularBuffer<T>::reset(const size_t capacity) {
clear();
std::vector<T>(capacity).swap(buf_);
capacity_ = capacity;
}
template <typename T>
void CircularBuffer<T>::clear() {
is_empty_ = true;
head_ = 0;
tail_ = 0;
}
template <typename T>
void CircularBuffer<T>::toVector(std::vector<T>& vec) const {
vec.clear();
if (empty() || capacity_ == 0) {
return;
}
vec.resize(size());
for (size_t i = 0; i < size(); i++) {
vec[i] = buf_[(head_ + i) % capacity_];
}
return;
}
#endif // CIRCULAR_BUFFER_HPP_
/*
// This is a main with some unitary tests for the CircularBuffer template.
#include <cstdlib>
#include <iostream>
#include "CircularBuffer.hpp"
long unsigned success_count = 0;
long unsigned fail_count = 0;
#define ASSERT_EQ(a, b) \
if (a == b) { \
success_count++; \
} else { \
fail_count++; \
printf("\033[1;33mAssertion failed at line %d\033[0m\n", __LINE__); \
}
int main(void) {
size_t total_failure_count = 0;
for (size_t c = 0; c < 10; c++) {
printf("Testing capacity = \033[1;34m%lu\033[0m\n", c);
CircularBuffer<int> buf(c);
success_count = 0;
fail_count = 0;
ASSERT_EQ(buf.capacity(), c);
ASSERT_EQ(buf.size(), 0);
ASSERT_EQ(buf.empty(), true);
ASSERT_EQ(buf.front(), std::nullopt);
ASSERT_EQ(buf.size(), 0);
ASSERT_EQ(buf.empty(), true);
ASSERT_EQ(buf.popFront(), std::nullopt);
ASSERT_EQ(buf.size(), 0);
ASSERT_EQ(buf.empty(), true);
buf.pop();
ASSERT_EQ(buf.size(), 0);
ASSERT_EQ(buf.empty(), true);
buf.clear();
ASSERT_EQ(buf.size(), 0);
ASSERT_EQ(buf.empty(), true);
std::vector<int> out;
std::vector<int> expected_out;
buf.toVector(out);
ASSERT_EQ(out, expected_out);
expected_out.clear();
out.clear();
for (size_t i = 0; i < c; i++) {
expected_out.push_back(i);
buf.push(i);
}
buf.toVector(out);
ASSERT_EQ(out, expected_out);
if (c == 0) {
ASSERT_EQ(buf.popFront(), std::nullopt);
} else {
ASSERT_EQ(buf.popFront(), std::make_optional<int>(0));
}
if (c <= 1) {
ASSERT_EQ(buf.empty(), true);
ASSERT_EQ(buf.size(), 0);
} else {
ASSERT_EQ(buf.empty(), false);
ASSERT_EQ(buf.size(), c - 1);
ASSERT_EQ(buf.front(), std::make_optional<int>(1));
buf.pop();
ASSERT_EQ(buf.size(), c - 2);
}
expected_out.clear();
out.clear();
buf.clear();
// Overflowing capacity with capacity + random pushed numbers
// generating a random number
srand((unsigned)time(NULL));
int random = rand() % (5 * (c + 1));
// pushing
for (size_t i = 0; i < c + random; i++) {
buf.push(i);
}
buf.toVector(out);
// building expected output
for (size_t i = random; i < c + random; i++) {
expected_out.push_back(i);
}
ASSERT_EQ(out, expected_out);
// Testing resize
buf.clear();
buf.resize(c + 1);
ASSERT_EQ(buf.capacity(), c + 1);
ASSERT_EQ(buf.size(), 0);
out.clear();
expected_out.clear();
for (int i = 0; i < c; i++) buf.push(i);
for (int i = c - c / 2; i < c; i++) expected_out.push_back(i);
buf.resize(c / 2);
ASSERT_EQ(buf.size(), c / 2);
buf.toVector(out);
ASSERT_EQ(out, expected_out);
if (c / 2 > 0) {
ASSERT_EQ(buf.front(), std::make_optional(expected_out[0]));
ASSERT_EQ(buf.back(), std::make_optional(expected_out.back()));
} else {
ASSERT_EQ(buf.front(), std::nullopt);
ASSERT_EQ(expected_out.size(), 0);
ASSERT_EQ(buf.size(), 0);
}
printf("Test results (random number is %d):\n\tSUCCESSES: \033[1;32m%4lu\033[0m\n\tFAILURES: \033[1;31m%4lu\033[0m\n\n", random, success_count,
fail_count);
total_failure_count += fail_count;
}
return total_failure_count;
}
*/