.. _program_listing_file_src_navtk_utils_RingBuffer.hpp: Program Listing for File RingBuffer.hpp ======================================= |exhale_lsh| :ref:`Return to documentation for file ` (``src/navtk/utils/RingBuffer.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #pragma once #include #include #include namespace navtk { namespace utils { template class RingBufferIterator; template class RingBuffer { public: using value_type = T; using iterator = RingBufferIterator; using const_iterator = RingBufferIterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_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::max)())) { throw std::invalid_argument( "Buffer size cannot be greater than or equal to " "std::size_t((std::numeric_limits::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 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