aboutsummaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/10-common/RingBuffer.hpp133
1 files changed, 73 insertions, 60 deletions
diff --git a/source/10-common/RingBuffer.hpp b/source/10-common/RingBuffer.hpp
index c4ff851..de8227c 100644
--- a/source/10-common/RingBuffer.hpp
+++ b/source/10-common/RingBuffer.hpp
@@ -5,66 +5,68 @@
#include <cstddef>
#include <iterator>
-template <typename T>
-class RingBuffer {
-public:
- class Sentinel {};
- class Iterator {
- public:
- friend class RingBuffer;
- using difference_type = ptrdiff_t;
- using value_type = T;
- using pointer = T*;
- using reference = T&;
- using iterator_category = std::random_access_iterator_tag;
+class RingBufferSentinel {};
- private:
- RingBuffer* mContainer;
- size_t mCurr;
- bool mNeedsWrapAround;
- bool mHasWrapped = false;
+template <typename TContainer>
+class RingBufferIterator {
+public:
+ using difference_type = TContainer::difference_type;
+ using value_type = TContainer::value_type; // C++20 relaxed usage requirements of `typename`, now locations where a type is required (like here in a using statement) it's no longer mandatory
+ using pointer = value_type*;
+ using reference = value_type&;
+ using iterator_category = std::random_access_iterator_tag;
- public:
- // NOTE: default constructed Iterator is in an undefined state
+public:
+ TContainer* container;
+ TContainer::size_type curr; // C++20 relaxed usage requirements of `typename`, same here
+ bool needsWrapAround;
+ bool hasWrappedAround = false;
- T& operator*() const {
- return mContainer->mRing[mCurr];
- }
+public:
+ reference operator*() const {
+ return container->mRing[curr];
+ }
- Iterator& operator++() {
- assert(*this != Sentinel{});
- ++mCurr;
- if (mNeedsWrapAround && mCurr == mContainer->mCapacity) {
- mHasWrapped = true;
- mCurr = 0;
- }
- return *this;
+ RingBufferIterator& operator++() {
+ assert(*this != RingBufferSentinel{});
+ ++curr;
+ if (needsWrapAround && curr == container->mCapacity) {
+ hasWrappedAround = true;
+ curr = 0;
}
+ return *this;
+ }
- bool operator==(const Iterator& that) const {
- assert(this->mContainer == that.mContainer);
- return this->mCurr == that.mCurr;
- }
+ bool operator==(const RingBufferIterator& that) const {
+ assert(this->container == that.container);
+ return this->curr == that.curr;
+ }
- bool operator==(const Sentinel&) const {
- return mCurr == mContainer->mTailIdx && (!mNeedsWrapAround || mHasWrapped);
- }
- };
+ bool operator==(const RingBufferSentinel&) const {
+ return curr == container->mTailIdx && (!needsWrapAround || hasWrappedAround);
+ }
+};
+template <typename T>
+class RingBuffer {
+public:
using value_type = T;
using reference = T&;
using const_reference = const T&;
- using iterator = Iterator;
- // using const_iterator = void; // TODO
+ friend class RingBufferIterator<RingBuffer>;
+ using iterator = RingBufferIterator<RingBuffer>;
+ friend class RingBufferIterator<const RingBuffer>;
+ using const_iterator = RingBufferIterator<const RingBuffer>;
+ using sentinel = RingBufferSentinel; // Not a part of C++'s Container named requirements, added here for convenience
using difference_type = ptrdiff_t;
using size_type = size_t;
private:
T* mRing = nullptr;
- size_t mHeadIdx = 0;
- size_t mTailIdx = 0;
- size_t mCapacity = 0;
- size_t mSize = 0;
+ size_type mHeadIdx = 0;
+ size_type mTailIdx = 0;
+ size_type mCapacity = 0;
+ size_type mSize = 0;
public:
RingBuffer() noexcept = default;
@@ -101,26 +103,37 @@ public:
return *this;
}
- Iterator begin() {
- Iterator it;
- it.mContainer = this;
- it.mCurr = mHeadIdx;
- it.mNeedsWrapAround = mHeadIdx >= mTailIdx;
- return it;
+ [[nodiscard]] iterator begin() {
+ return {
+ .container = this,
+ .curr = mHeadIdx,
+ .needsWrapAround = mHeadIdx >= mTailIdx,
+ };
}
- Sentinel end() {
- return Sentinel{};
+ [[nodiscard]] const_iterator begin() const { return cbegin(); }
+
+ // Same type for both const this and non-const `this`
+ [[nodiscard]] sentinel end() const { return sentinel{}; }
+
+ [[nodiscard]] const_iterator cbegin() const {
+ return {
+ .container = this,
+ .curr = mHeadIdx,
+ .needsWrapAround = mHeadIdx >= mTailIdx,
+ };
}
- const T& operator[](size_t i) const {
- size_t idx = mHeadIdx + i;
+ [[nodiscard]] sentinel cend() const { return sentinel{}; }
+
+ [[nodiscard]] T& operator[](size_type i) { return const_cast<T&>(const_cast<const RingBuffer&>(*this)[i]); }
+ [[nodiscard]] const T& operator[](size_type i) const {
+ size_type idx = mHeadIdx + i;
if (idx >= mCapacity) {
idx -= mCapacity;
}
return mRing[idx];
}
- T& operator[](size_t i) { return const_cast<T&>(const_cast<const RingBuffer&>(*this)[i]); }
void push_back(T t) {
if (mTailIdx == mCapacity) {
@@ -147,19 +160,19 @@ public:
}
}
- [[nodiscard]] size_t capacity() const {
+ [[nodiscard]] size_type capacity() const {
return mCapacity;
}
- [[nodiscard]] size_t size() const {
+ [[nodiscard]] size_type size() const {
return mSize;
}
[[nodiscard]] T* GetBuffer() const { return mRing; }
- [[nodiscard]] size_t GetHeadIdx() const { return mHeadIdx; }
- [[nodiscard]] size_t GetTailIdx() const { return mTailIdx; }
+ [[nodiscard]] size_type GetHeadIdx() const { return mHeadIdx; }
+ [[nodiscard]] size_type GetTailIdx() const { return mTailIdx; }
- void resize(size_t newCapacity) {
+ void resize(size_type newCapacity) {
auto size = this->size();
auto oldRing = mRing;