/** * @file cyclic_buffer.h * @brief A cyclic buffer is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end. * * http://en.wikipedia.org/wiki/Circular_buffer * * @date Dec 8, 2013 * @author Andrey Belomutskiy, Daniel Hill * * Daniel Hill - Modified to use C++ - Mar 2, 2014 */ #pragma once #include #include #include static const short CB_MAX_SIZE = 128; template class cyclic_buffer { public: cyclic_buffer(); explicit cyclic_buffer(int size); public: void add(T value); T get(int index) const; T sum(size_t length) const; T maxValue(size_t length) const; T minValue(size_t length) const; void setSize(size_t size); bool contains(T value) const; int getSize() const; int getCount() const; void clear(); volatile T elements[maxSize]; volatile uint16_t currentIndex; protected: uint16_t size; /** * number of elements added into this buffer, would be eventually bigger then size */ volatile size_t count; }; template cyclic_buffer::cyclic_buffer() : cyclic_buffer(maxSize) { } template cyclic_buffer::cyclic_buffer(int size) { setSize(size); } template void cyclic_buffer::add(T value) { // Too lazy to make this thread safe, but at the very least let's never let currentIndex // become invalid. And yes I did see a crash due to an overrun here. uint16_t idx = currentIndex; ((T &)elements[idx]) = value; if (++idx == size) { idx = 0; } currentIndex = idx; ++count; } template bool cyclic_buffer::contains(T value) const { for (int i = 0; i < currentIndex ; i++) { if (elements[i] == value) { return true; } } return false; } template void cyclic_buffer::setSize(size_t size) { clear(); this->size = size < maxSize ? size : maxSize; } template int cyclic_buffer::getSize() const { return size; } template int cyclic_buffer::getCount() const { return count; } template T cyclic_buffer::get(int index) const { while (index < 0) { index += size; } while (index >= size) { index -= size; } return elements[index]; } template T cyclic_buffer::maxValue(size_t length) const { if (length > count) { // not enough data in buffer length = count; } int ci = currentIndex; // local copy to increase thread-safety T result = std::numeric_limits::min(); for (size_t i = 0; i < length; ++i) { int index = ci - i - 1; while (index < 0) { index += size; } if (elements[index] > result) { result = elements[index]; } } return result; } template T cyclic_buffer::minValue(size_t length) const { if (length > count) { length = count; } int ci = currentIndex; // local copy to increase thread-safety T result = std::numeric_limits::max(); for (size_t i = 0; i < length; ++i) { int index = ci - i - 1; while (index < 0) { index += size; } if (elements[index] < result) { result = elements[index]; } } return result; } template T cyclic_buffer::sum(size_t length) const { if (length > count) { length = count; } int ci = currentIndex; // local copy to increase thread-safety T result = 0; for (size_t i = 0; i < length; ++i) { int index = ci - i - 1; while (index < 0) { index += size; } result += elements[index]; } return result; } template void cyclic_buffer::clear() { memset((void*) elements, 0, sizeof(elements)); // I would usually use static_cast, but due to the elements being volatile we cannot. count = 0; currentIndex = 0; }