#pragma once #include #include #include #include #include namespace rims { struct RingIndex { using index_t = uint16_t; constexpr RingIndex(index_t n) : N{n} { reset(); } constexpr void reset() { _head = 0; _tail = 0; _full = false; } constexpr bool empty() const { return (!_full && _head == _tail); } constexpr bool full() const { return _full; } constexpr std::size_t size() const { if (full()) { return N; } if (_head >= _tail) { return _head - _tail; } else { return N + _head - _tail; } } constexpr std::size_t capacity() const { return N; } constexpr std::size_t free() const { return capacity() - size(); } constexpr index_t increment_head() { assert(not full()); return increment_head(1); } constexpr index_t increment_head(index_t n) { assert(free() >= n); _head = (_head + n) % N; _full = _head == _tail; return prev_head(); } constexpr index_t decrement_head() { assert(not empty()); _head = (_head + N - 1) % N; _full = false; return _head; } constexpr index_t increment_tail() { assert(!empty()); return increment_tail(1); } constexpr index_t increment_tail(index_t n) { assert(size() >= n); _tail = (_tail + n) % N; _full = false; return prev_tail(); } constexpr index_t head() const { return _head; } constexpr index_t tail() const { return _tail; } constexpr index_t firstFree() const { assert(!full()); return head(); } constexpr bool headAfterTail() const { return head() >= tail(); } constexpr std::size_t spaceBack() const { if (headAfterTail()) { return capacity() - head(); } else { if (spaceMid() == 0) { return capacity() - tail() - 1; } } return 0; } constexpr std::size_t spaceFront() const { if (headAfterTail()) { return tail(); } else { if (spaceMid() == 0) { return head(); } } return 0; } constexpr std::size_t spaceMid() const { const auto head2Tail = tail() - head(); return head2Tail > 0 ? head2Tail : 0; } using area = std::pair; constexpr std::pair getFreeAreas() const { assert((spaceFront() + spaceBack() + spaceMid()) == free()); const std::size_t capacityMid = spaceMid(); if (capacityMid) { return {{head(), capacityMid}, {0, 0}}; } else { return {{head(), spaceBack()}, {0, spaceFront()}}; } } private: constexpr index_t prev_tail() const { return _tail == 0 ? N - 1 : _tail - 1; } constexpr index_t prev_head() const { return _head == 0 ? N - 1 : _head - 1; } index_t N{0}; index_t _head{0}; index_t _tail{0}; bool _full{false}; }; template class RingBuffer { public: static_assert(std::is_trivial_v); static_assert(std::is_trivially_destructible_v); static_assert(std::is_trivially_copyable_v); using value_type = T; explicit RingBuffer() : _index{N} { } T &push_back(const T &item) { if (full()) { pop_front(); } auto at = _index.head(); std::construct_at(reinterpret_cast(std::addressof(_buf[_index.increment_head()])), item); return directy_at(at); } void put_n(const T *items, std::size_t n) { static_assert(std::is_trivially_constructible_v); assert(_index.free() >= n); auto areas = _index.getFreeAreas(); assert((areas.first.second + areas.second.second) >= n); std::memcpy(_buf[areas.first.first], items, areas.first.second * sizeof(T)); if (areas.first.second <= n) std::memcpy(_buf[areas.second.first], items + areas.first.second, areas.second.second * sizeof(T)); _index.increment_head(n); } T get() { assert(not empty()); // Read data and advance the tail (we now have a free space) T val = std::move(*reinterpret_cast(std::addressof(_buf[_index.tail()]))); destroy_at(_index.increment_tail()); return val; } // removes the last (youngest) element void pop_back() { assert(not empty()); destroy_at(_index.decrement_head()); } // removes the first (oldest) element void pop_front() { assert(not empty()); destroy_at(_index.increment_tail()); } // oldest element const T &front() const { assert(not empty()); return *cbegin(); } T &front() { assert(not empty()); return *begin(); } const T &back() const { assert(not empty()); return *std::prev(cend()); } T &back() { assert(not empty()); return *(std::prev(end())); } const T &at(std::size_t index) const { assert(index < size()); return *(cbegin() + index); } T &at(std::size_t index) { assert(index < size()); return *(begin() + index); } constexpr void reset() { // destroy elements? :| _index.reset(); } constexpr bool empty() const { return _index.empty(); } constexpr bool full() const { return _index.full(); } constexpr std::size_t capacity() const { return _index.capacity(); } constexpr std::size_t size() const { return _index.size(); } constexpr std::size_t free() const { return _index.free(); } // Iterator class for ring_buffer template class iterator_base { public: using iterator_category = std::random_access_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; using pointer = std::conditional_t; using reference = std::conditional_t; using iterator_t = iterator_base; using buf_t = std::conditional_t, std::array>; constexpr iterator_base() = default; constexpr iterator_base(buf_t &buf, std::size_t index, std::size_t tail, std::size_t head) : buf_(&buf), current_(index), tail_(tail), head_(head), looped_(false) { } constexpr iterator_base(const iterator_base &rhs) = default; constexpr iterator_base(iterator_base &&rhs) = default; constexpr iterator_base &operator=(const iterator_base &rhs) = default; constexpr iterator_base &operator=(iterator_base &&rhs) = default; reference operator*() const { return *reinterpret_cast(std::addressof((*buf_)[current_])); } pointer operator->() const { return std::addressof(operator*()); } constexpr iterator_t &operator++() { current_ = (current_ + 1) % N; // Detect if we have looped over the circular buffer if (current_ == tail_ && looped_) { current_ = head_; // Move iterator to end } if (current_ == head_) { looped_ = true; } return *this; } constexpr iterator_t operator++(int) { iterator_t tmp = *this; ++(*this); return tmp; } constexpr iterator_t &operator--() { if (current_ == 0) current_ = N - 1; else current_--; // Detect if we have looped over the circular buffer if (current_ == head_ && looped_) { current_ = tail_; // Move iterator to end } if (current_ == tail_) { looped_ = true; } return *this; } constexpr iterator_t operator--(int) { iterator_t tmp = *this; --(*this); return tmp; } constexpr bool operator==(const iterator_t &other) const { return current_ == other.current_ && looped_; } constexpr iterator_t &operator+=(difference_type n) { current_ = (current_ + n) % N; return *this; } constexpr iterator_t &operator+=(const iterator_t &n) { current_ = (current_ + n.current_) % N; return *this; } constexpr iterator_t operator+(difference_type n) const { std::size_t new_index = (current_ + n) % N; return iterator_t(*buf_, new_index, tail_, head_); } constexpr friend iterator_t operator+(iterator_t it, const iterator_t &n) { it += n; return it; } constexpr friend iterator_t operator+(difference_type it, const iterator_t &n) { iterator_t tmp = n; tmp += it; return tmp; } constexpr iterator_t &operator-=(difference_type n) { current_ = (current_ + N - (n % N)) % N; return *this; } constexpr friend iterator_t operator-(const iterator_t &lhs, difference_type n) { iterator_t tmp = lhs; tmp -= n; return tmp; } constexpr friend difference_type operator-(const iterator_t &lhs, const iterator_t &rhs) { if (lhs.current_ >= rhs.current_) { return static_cast(lhs.current_ - rhs.current_); } else { return static_cast(N + lhs.current_ - rhs.current_); } } constexpr reference operator[](difference_type n) const { return *(*this + n); } // constexpr bool operator<(const iterator_t & other) const { // return (*this - other) < 0; // } // constexpr bool operator>(const iterator_t & other) const { // return other < *this; // } // constexpr bool operator<=(const iterator_t & other) const { // return !(*this > other); // } // constexpr bool operator>=(const iterator_t & other) const { // return !(*this < other); // } // constexpr bool operator!=(const iterator_t & other) const { // return !(*this == other); // } private: buf_t *buf_{nullptr}; std::size_t current_{0}; std::size_t tail_{0}; std::size_t head_{0}; bool looped_{false}; }; using iterator = iterator_base; using const_iterator = iterator_base; static_assert(std::bidirectional_iterator); // static_assert(std::random_access_iterator< iterator >); static_assert(std::bidirectional_iterator); // static_assert(std::random_access_iterator< const_iterator >); // Begin and end functions to return iterator iterator begin() { return {_buf, _index.tail(), _index.tail(), _index.head()}; } const_iterator begin() const { return {_buf, _index.tail(), _index.tail(), _index.head()}; } const_iterator cbegin() const { return {_buf, _index.tail(), _index.tail(), _index.head()}; } iterator end() { return {_buf, _index.head(), _index.tail(), _index.head()}; } const_iterator end() const { return {_buf, _index.head(), _index.tail(), _index.head()}; } const_iterator cend() const { return {_buf, _index.head(), _index.tail(), _index.head()}; } private: void destroy_at(int index) { std::destroy_at(std::addressof(_buf[index])); std::memset(std::addressof(_buf[index]), 0, sizeof(T)); } const T &directy_at(int index) const { return *reinterpret_cast(std::addressof(_buf[index])); } T &directy_at(int index) { return *reinterpret_cast(std::addressof(_buf[index])); } std::array _buf; RingIndex _index; }; } // namespace rims