Program Listing for File RingBuffer.hpp
↰ Return to documentation for file (src/navtk/utils/RingBuffer.hpp)
#pragma once
#include <iterator>
#include <stdexcept>
#include <navtk/errors.hpp>
namespace navtk {
namespace utils {
template <typename T>
class RingBufferIterator;
template <typename T>
class RingBuffer {
public:
using value_type = T;
using iterator = RingBufferIterator<T>;
using const_iterator = RingBufferIterator<const T>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
explicit RingBuffer(std::size_t capacity) : capacity(capacity) {
if (capacity == 0) {
throw std::invalid_argument("Buffer size cannot be 0.");
}
// Workaround for GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87544
// See issue navtk#895 for details.
if (capacity >= std::size_t((std::numeric_limits<std::ptrdiff_t>::max)())) {
throw std::invalid_argument(
"Buffer size cannot be greater than or equal to "
"std::size_t((std::numeric_limits<std::ptrdiff_t>::max)())");
}
buffer = new T[capacity];
}
~RingBuffer() { delete[] buffer; }
RingBuffer(const RingBuffer& other)
: capacity(other.capacity), buffer(new T[other.capacity]()) {
buffer_size = other.buffer_size;
std::copy(other.begin(), other.end(), buffer);
}
RingBuffer& operator=(const RingBuffer& other) {
if (this != &other) {
// Free the existing resource.
delete[] buffer;
capacity = other.capacity;
buffer = new T[capacity];
buffer_size = other.buffer_size;
std::copy(other.begin(), other.end(), buffer);
}
return *this;
}
RingBuffer(RingBuffer&& other) : capacity(0), buffer(nullptr) { *this = std::move(other); }
RingBuffer& operator=(RingBuffer&& other) {
if (this != &other) {
// Free the existing resource.
delete[] buffer;
// Copy the data pointer and its length from the
// source object.
capacity = other.capacity;
buffer = other.buffer;
buffer_size = other.buffer_size;
begin_index = other.begin_index;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other.capacity = 0;
other.buffer = nullptr;
other.buffer_size = 0;
other.begin_index = 0;
}
return *this;
}
std::size_t size() const { return buffer_size; }
bool empty() const { return buffer_size == 0; }
bool full() const { return buffer_size == capacity; }
T& front() { return first_value(); }
const T& front() const { return first_value(); }
T& back() { return last_value(); }
const T& back() const { return last_value(); }
void pop_front() {
if (buffer_size == 0) return;
T t = std::move(first_value());
(void)t;
++begin_index %= capacity;
--buffer_size;
}
void pop_back() {
if (buffer_size == 0) return;
T t = std::move(last_value());
(void)t;
--buffer_size;
}
void push_front(const T& value) {
if (buffer_size < capacity) {
++buffer_size;
}
begin_index = (begin_index + capacity - 1) % capacity;
first_value() = value;
}
void push_front(T&& value) {
if (buffer_size < capacity) {
++buffer_size;
}
begin_index = (begin_index + capacity - 1) % capacity;
first_value() = std::move(value);
}
void push_back(const T& value) {
if (buffer_size < capacity) {
++buffer_size;
} else {
++begin_index %= capacity;
}
last_value() = value;
}
void push_back(T&& value) {
if (buffer_size < capacity) {
++buffer_size;
} else {
++begin_index %= capacity;
}
last_value() = std::move(value);
}
iterator erase(const_iterator first, const_iterator last) {
auto number_of_elements_to_pop = last - first;
iterator return_val = end();
if (first == cbegin()) {
for (auto i = 0; i < number_of_elements_to_pop; i++) {
pop_front();
return_val = begin();
}
} else if (last == cend()) {
for (auto i = 0; i < number_of_elements_to_pop; i++) {
pop_back();
return_val = end();
}
} else {
log_or_throw("unimplemented operation erase from middle of RingBuffer");
}
return return_val;
}
iterator begin() { return iterator(buffer, begin_index, capacity); }
const_iterator begin() const { return const_iterator(buffer, begin_index, capacity); }
const_iterator cbegin() const { return begin(); }
iterator end() { return iterator(buffer, begin_index + buffer_size, capacity); }
const_iterator end() const {
return const_iterator(buffer, begin_index + buffer_size, capacity);
}
const_iterator cend() const { return end(); }
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
const_reverse_iterator crbegin() const { return rbegin(); }
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
const_reverse_iterator crend() const { return rend(); }
private:
std::size_t capacity;
T* buffer;
std::size_t buffer_size = 0;
std::size_t begin_index = 0;
T& first_value() { return buffer[begin_index]; }
const T& first_value() const { return buffer[begin_index]; }
T& last_value() { return buffer[(begin_index + buffer_size - 1) % capacity]; }
const T& last_value() const { return buffer[(begin_index + buffer_size - 1) % capacity]; }
};
template <typename T>
class RingBufferIterator {
public:
using difference_type = ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
using iterator_category = std::random_access_iterator_tag;
RingBufferIterator(T* buffer = new T[1](), std::size_t offset = 0, std::size_t capacity = 1)
: capacity(capacity), buffer(buffer), offset(offset) {}
bool operator==(const RingBufferIterator& rhs) {
return rhs.buffer == buffer && rhs.offset == offset;
}
bool operator!=(const RingBufferIterator& rhs) { return !(*this == rhs); }
RingBufferIterator& operator++() {
++offset;
return *this;
}
RingBufferIterator operator++(int) {
auto t = *this;
++offset;
return t;
}
RingBufferIterator operator+(int amount) const {
return RingBufferIterator(buffer, offset + amount, capacity);
}
RingBufferIterator operator-(int amount) const {
return RingBufferIterator(buffer, offset - amount, capacity);
}
RingBufferIterator& operator--() {
--offset;
return *this;
}
RingBufferIterator operator--(int) {
auto t = *this;
--offset;
return t;
}
std::ptrdiff_t operator-(RingBufferIterator const& rhs) const { return offset - rhs.offset; }
RingBufferIterator& operator+=(int amount) {
offset += amount;
return *this;
}
RingBufferIterator& operator-=(int amount) {
offset -= amount;
return *this;
}
bool operator<(RingBufferIterator const& rhs) const { return offset < rhs.offset; }
bool operator<=(RingBufferIterator const& rhs) const { return offset <= rhs.offset; }
bool operator>(RingBufferIterator const& rhs) const { return offset > rhs.offset; }
bool operator>=(RingBufferIterator const& rhs) const { return offset >= rhs.offset; }
T& operator[](int index) const { return *(*this + index); }
T& operator*() const { return buffer[offset % capacity]; }
T* operator->() const { return &buffer[offset % capacity]; }
private:
std::size_t capacity;
T* buffer;
std::size_t offset;
};
} // namespace utils
} // namespace navtk