diff options
author | rtk0c <[email protected]> | 2022-11-25 17:06:58 -0800 |
---|---|---|
committer | rtk0c <[email protected]> | 2022-11-25 17:06:58 -0800 |
commit | a0ddfdbcbc6336685362343518770f7bdefd10fe (patch) | |
tree | eeee6982bca066b2286ef266c51fdc8ffaf8f9e0 /source/10-common/RingBuffer.hpp | |
parent | 01c98830affd1189ab2b227d685f10f5dc8b6ca7 (diff) |
Changeset: 91 Basic logging infrastructure
Diffstat (limited to 'source/10-common/RingBuffer.hpp')
-rw-r--r-- | source/10-common/RingBuffer.hpp | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/source/10-common/RingBuffer.hpp b/source/10-common/RingBuffer.hpp new file mode 100644 index 0000000..c4ff851 --- /dev/null +++ b/source/10-common/RingBuffer.hpp @@ -0,0 +1,174 @@ +#pragma once + +#include <algorithm> +#include <cassert> +#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; + + private: + RingBuffer* mContainer; + size_t mCurr; + bool mNeedsWrapAround; + bool mHasWrapped = false; + + public: + // NOTE: default constructed Iterator is in an undefined state + + T& operator*() const { + return mContainer->mRing[mCurr]; + } + + Iterator& operator++() { + assert(*this != Sentinel{}); + ++mCurr; + if (mNeedsWrapAround && mCurr == mContainer->mCapacity) { + mHasWrapped = true; + mCurr = 0; + } + return *this; + } + + bool operator==(const Iterator& that) const { + assert(this->mContainer == that.mContainer); + return this->mCurr == that.mCurr; + } + + bool operator==(const Sentinel&) const { + return mCurr == mContainer->mTailIdx && (!mNeedsWrapAround || mHasWrapped); + } + }; + + using value_type = T; + using reference = T&; + using const_reference = const T&; + using iterator = Iterator; + // using const_iterator = void; // TODO + 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; + +public: + RingBuffer() noexcept = default; + + ~RingBuffer() noexcept { + delete mRing; + } + + RingBuffer(const RingBuffer&) noexcept = delete; + RingBuffer& operator=(const RingBuffer&) noexcept = delete; + + RingBuffer(RingBuffer&& that) noexcept + : mRing{ that.mRing } + , mHeadIdx{ that.mHeadIdx } + , mTailIdx{ that.mTailIdx } + , mCapacity{ that.mCapacity } + , mSize{ that.mSize } { + that.mRing = nullptr; + } + + RingBuffer& operator=(RingBuffer&& that) noexcept { + if (this != &that) { + auto oldThisRing = this->mRing; + this->mRing = that.mRing; + that.mRing = nullptr; + delete oldThisRing; + + this->mHeadIdx = that.mHeadIdx; + this->mTailIdx = that.mTailIdx; + this->mCapacity = that.mCapacity; + this->mSize = that.mSize; + } + + return *this; + } + + Iterator begin() { + Iterator it; + it.mContainer = this; + it.mCurr = mHeadIdx; + it.mNeedsWrapAround = mHeadIdx >= mTailIdx; + return it; + } + + Sentinel end() { + return Sentinel{}; + } + + const T& operator[](size_t i) const { + size_t 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) { + // Ring buffer is filled to the right, warp around to the beginning + // mHeadIdx > 0 must be true, since we checked that as condition (1) above + mRing[0] = std::move(t); + mTailIdx = 1; + } else { + mRing[mTailIdx] = std::move(t); + mTailIdx += 1; + } + + // Push mHeadIdx backwards if overwrote element in a filled buffer + bool bufferFilled = mSize == mCapacity; + if (bufferFilled && mTailIdx > mHeadIdx) { + mHeadIdx += 1; + if (mHeadIdx == mCapacity) { + mHeadIdx = 0; + } + } + + if (!bufferFilled) { + ++mSize; + } + } + + [[nodiscard]] size_t capacity() const { + return mCapacity; + } + + [[nodiscard]] size_t size() const { + return mSize; + } + + [[nodiscard]] T* GetBuffer() const { return mRing; } + [[nodiscard]] size_t GetHeadIdx() const { return mHeadIdx; } + [[nodiscard]] size_t GetTailIdx() const { return mTailIdx; } + + void resize(size_t newCapacity) { + auto size = this->size(); + + auto oldRing = mRing; + auto newRing = mRing = new T[newCapacity]; + std::rotate_copy(oldRing, oldRing + mHeadIdx, oldRing + mCapacity, newRing); + delete oldRing; + + mCapacity = newCapacity; + mHeadIdx = 0; + mTailIdx = size; + } +}; |