ArcdpsExtension
 
Loading...
Searching...
No Matches
SimpleRingBuffer.h
Go to the documentation of this file.
1#pragma once
2
3#include <cassert>
4#include <concepts>
5#include <cstddef>
6#include <iterator>
7#include <ostream>
8
9namespace ArcdpsExtension {
13 template<typename T, typename Allocator = std::allocator<T>>
14 class RingBuffer {
15 public:
16 explicit RingBuffer(size_t pInitialCapacity) {
17 mCapacityBegin = mAlloc.allocate(pInitialCapacity);
18 // mCapacityBegin = static_cast<T*>(calloc(pInitialCapacity, sizeof(T)));
19 mCapacityEnd = mCapacityBegin + pInitialCapacity;
20 mCurrent = mCapacityBegin;
21 mSizeEnd = mCapacityBegin;
22 }
23
24 virtual ~RingBuffer() {
25 if (mCapacityBegin) {
26 Clear();
27 mAlloc.deallocate(mCapacityBegin, mCapacityEnd - mCapacityBegin);
28 }
29 }
30
31 RingBuffer(const RingBuffer& pOther) {
32 size_t size = pOther.Size();
33 mCapacityBegin = mAlloc.allocate(size);
34 mCapacityEnd = mCapacityBegin + size;
35 mCurrent = mCapacityBegin;
36 mSizeEnd = mCapacityBegin;
37
38 for (const auto& other : pOther) {
39 PushBack(other);
40 }
41 }
42
43 RingBuffer(RingBuffer&& pOther) noexcept
44 : mCurrent(pOther.mCurrent),
45 mSizeEnd(pOther.mSizeEnd),
46 mCapacityBegin(pOther.mCapacityBegin),
47 mCapacityEnd(pOther.mCapacityEnd),
48 mAlloc(std::move(pOther.mAlloc)) {
49 pOther.mCapacityBegin = nullptr;
50 }
51
53 if (this == &pOther)
54 return *this;
55 size_t size = pOther.Size();
56 mCapacityBegin = mAlloc.allocate(size);
57 mCapacityEnd = mCapacityBegin + size;
58 mCurrent = mCapacityBegin;
59 mSizeEnd = mCapacityBegin;
60
61 for (const auto& other : pOther) {
62 PushBack(other);
63 }
64 return *this;
65 }
66
67 RingBuffer& operator=(RingBuffer&& pOther) noexcept {
68 if (this == &pOther)
69 return *this;
70 mCurrent = pOther.mCurrent;
71 mSizeEnd = pOther.mSizeEnd;
72 mCapacityBegin = pOther.mCapacityBegin;
73 mCapacityEnd = pOther.mCapacityEnd;
74 mAlloc = std::move(pOther.mAlloc);
75 pOther.mCapacityBegin = nullptr;
76 return *this;
77 }
78
79 void PushBack(T&& pElement);
80 void PushBack(const T& pElement);
81
82 template<typename... Args>
83 void EmplaceBack(const Args&... args);
84
85 T& Back();
86 const T& Back() const;
87
88 void Clear();
89 size_t Size() const;
90 void Resize(size_t pNewCapacity);
91
92 const T& operator[](size_t pNum) const;
93 T& operator[](size_t pNum);
94
96 public:
97 using difference_type = std::ptrdiff_t;
98 using value_type = T;
99 using pointer = T*;
100 using reference = T&;
101 using iterator_category = std::random_access_iterator_tag;
102 using iterator_concept = std::random_access_iterator_tag;
103
105
106 RingBufferIterator(pointer pPtr, bool pBegin, bool pEnd, const RingBuffer<T, Allocator>* pParent)
107 : mPtr(pPtr),
108 mBegin(pBegin),
109 mEnd(pEnd),
110 mParent(pParent) {}
111
112 // COPY & MOVE
113 RingBufferIterator(const RingBufferIterator& pOther) = default;
114 RingBufferIterator(RingBufferIterator&& pOther) noexcept = default;
116 RingBufferIterator& operator=(RingBufferIterator&& pOther) noexcept = default;
117
118 // comparators
119 bool operator==(const RingBufferIterator&) const = default;
120
121 // Relation comparison relys on uintptr_t "underflow". It basically means, that the comparison breaks, when the size is bigger than `size_t / 2`.
122 std::strong_ordering operator<=>(const RingBufferIterator& pOther) const {
123 uintptr_t thisDiff = (uintptr_t) mPtr - (uintptr_t) (mParent->mCurrent);
124 uintptr_t otherDiff = (uintptr_t) pOther.mPtr - (uintptr_t) (mParent->mCurrent);
125 if ((mEnd && !pOther.mEnd) || (pOther.mBegin && !mBegin)) {
126 return std::strong_ordering::greater;
127 }
128 if ((mBegin && pOther.mBegin) || (pOther.mEnd && !mEnd)) {
129 return std::strong_ordering::less;
130 }
131 return thisDiff <=> otherDiff;
132 }
133
134 // dereferencable
135 reference operator*() const { return *mPtr; }
136
137 // helper
138 pointer operator->() const { return mPtr; }
139
140 // Prefix increment
142 assert(mEnd == false && "Called ++ on an end iterator");
143 mBegin = false;
144 mPtr = mParent->advance(mPtr);
145 if (mParent->mCurrent == mPtr) mEnd = true;
146 return *this;
147 }
148
150 assert(mBegin == false && "Called -- on an begin iterator");
151 mEnd = false;
152 mPtr = mParent->retreat(mPtr);
153 if (mParent->mCurrent == mPtr) mBegin = true;
154 return *this;
155 }
156
157 // Postfix increment
159 RingBufferIterator tmp = *this;
160 this->operator++();
161 return tmp;
162 }
163
165 RingBufferIterator tmp = *this;
166 this->operator--();
167 return tmp;
168 }
169
170 // calculation operators
171 // with itself
173 std::ptrdiff_t thisDiff = mPtr - mParent->mCurrent;
174 std::ptrdiff_t otherDiff = pOther.mPtr - mParent->mCurrent;
175 std::ptrdiff_t fullDiff = mParent->mSizeEnd - mParent->mCapacityBegin;
176 if (thisDiff < 0 || (mEnd && thisDiff == 0)) {
177 thisDiff += fullDiff;
178 }
179 if (otherDiff < 0 || (pOther.mEnd && thisDiff == 0)) {
180 otherDiff += fullDiff;
181 }
182 return thisDiff - otherDiff;
183 }
184
185 // with numbers
187 assert(*this - mParent->begin() + pNum <= mParent->end() - mParent->begin() && "tried to move iterator after end!");
188
189 mBegin = false;
190 mPtr += pNum;
191 if (mPtr >= mParent->mSizeEnd) {
192 mPtr = mPtr - mParent->mSizeEnd + mParent->mCapacityBegin;
193 }
194 if (mParent->mCurrent == mPtr) mEnd = true;
195 return *this;
196 }
197
199 RingBufferIterator it = *this;
200 it += pNum;
201 return it;
202 }
203
205 RingBufferIterator it = pRhs;
206 it += pLhs;
207 return it;
208 }
209
211 assert(*this - mParent->begin() - pNum >= 0 && "tried to move iterator before begin!");
212
213 mEnd = false;
214 mPtr -= pNum;
215 if (mPtr < mParent->mCapacityBegin) {
216 mPtr = (T*) ((uintptr_t) mPtr + (uintptr_t) mParent->mSizeEnd - (uintptr_t) mParent->mCapacityBegin);
217 }
218 if (mParent->mCurrent == mPtr) mBegin = true;
219 return *this;
220 }
221
223 RingBufferIterator it = *this;
224 it -= pNum;
225 return it;
226 }
227
228 // for some reason the const function has to return a non-const reference...
229 // The standard says so, so we have to conform it: `{ j[n] } -> std::same_as<std::iter_reference_t<I>>;` with `const I j`.
230 // https://en.cppreference.com/w/cpp/iterator/random_access_iterator
232 return *(mParent->begin() + pNum);
233 }
234
235 /*
236 * Only use for debugging!
237 */
238 friend std::ostream& operator<<(std::ostream& pOs, const RingBufferIterator& pObj) {
239 return pOs
240 << "mPtr: " << pObj.mPtr
241 << " mBegin: " << pObj.mBegin
242 << " mEnd: " << pObj.mEnd
243 << " mParent: " << pObj.mParent;
244 }
245
246 private:
247 pointer mPtr = nullptr;
248 bool mBegin = false;
249 bool mEnd = false;
250 const RingBuffer<T>* mParent = nullptr;
251 };
252
253
254 static_assert(std::random_access_iterator<RingBufferIterator>);
255
256 // pointer pPtr, bool pBegin, bool pEnd, RingBuffer* pParent
257 RingBufferIterator begin() { return RingBufferIterator(mCurrent, true, mSizeEnd == mCapacityBegin, this); }
258 RingBufferIterator end() { return RingBufferIterator(mCurrent, mSizeEnd == mCapacityBegin, true, this); }
259 const RingBufferIterator begin() const { return RingBufferIterator(mCurrent, true, mSizeEnd == mCapacityBegin, this); }
260 const RingBufferIterator end() const { return RingBufferIterator(mCurrent, mSizeEnd == mCapacityBegin, true, this); }
261 const RingBufferIterator cbegin() const { return RingBufferIterator(mCurrent, true, mSizeEnd == mCapacityBegin, this); }
262 const RingBufferIterator cend() const { return RingBufferIterator(mCurrent, mSizeEnd == mCapacityBegin, true, this); }
263 RingBufferIterator rbegin() { return std::make_reverse_iterator(end()); }
264 RingBufferIterator rend() { return std::make_reverse_iterator(begin()); }
265 const RingBufferIterator rbegin() const { return std::make_reverse_iterator(end()); }
266 const RingBufferIterator rend() const { return std::make_reverse_iterator(begin()); }
267 const RingBufferIterator crbegin() const { return std::make_reverse_iterator(end()); }
268 const RingBufferIterator crend() const { return std::make_reverse_iterator(begin()); }
269
270 private:
271 T* mCurrent;
272 T* mSizeEnd;
273 T* mCapacityBegin;
274 T* mCapacityEnd;
275 Allocator mAlloc;
276
277 T* pushOne();
278 [[nodiscard]] T* advance(T* pElem) const;
279 [[nodiscard]] T* retreat(T* pElem) const;
280 };
281} // namespace ArcdpsExtension
282
283// test if the range is also valid
284static_assert(std::ranges::random_access_range<ArcdpsExtension::RingBuffer<uint64_t>>);
285static_assert(std::ranges::random_access_range<const ArcdpsExtension::RingBuffer<uint64_t>>);
286
287template<typename T, typename Allocator>
289 T* elem = pushOne();
290 new (elem) T(pElement);
291}
292
293template<typename T, typename Allocator>
295 T* elem = pushOne();
296 new (elem) T(pElement);
297}
298
299template<typename T, typename Allocator>
301 // If first element
302 if (mSizeEnd == mCapacityBegin) {
303 ++mSizeEnd;
304 return mCapacityBegin;
305 }
306
307 if (mSizeEnd != mCapacityEnd) {
308 T* current = mSizeEnd;
309 ++mSizeEnd;
310 return current;
311 }
312
313 // advance begin and end
314 T* current = mCurrent;
315 mCurrent = advance(mCurrent);
316
317 current->~T();
318 return current;
319}
320
321template<typename T, typename Allocator>
323 auto val = pElem + 1;
324 if (val >= mSizeEnd) return mCapacityBegin;
325 return val;
326}
327
328template<typename T, typename Allocator>
330 auto val = pElem;
331 if (val <= mCapacityBegin) val = mSizeEnd;
332 --val;
333 return val;
334}
335
336template<typename T, typename Allocator>
337template<typename... Args>
339 T* elem = pushOne();
340 new (elem) T(args...);
341}
342
343template<typename T, typename Allocator>
345 return *retreat(mCurrent);
346}
347
348template<typename T, typename Allocator>
350 return *retreat(mCurrent);
351}
352
353template<typename T, typename Allocator>
355 for (auto it = begin(); it != end(); ++it) {
356 (*it).~T();
357 }
358
359 mCurrent = mSizeEnd = mCapacityBegin;
360}
361
362template<typename T, typename Allocator>
364 return mSizeEnd - mCapacityBegin;
365}
366
367template<typename T, typename Allocator>
369 // backup old data
370 T* current = mCurrent;
371 T* sizeEnd = mSizeEnd;
372 T* capacityBegin = mCapacityBegin;
373 T* capacityEnd = mCapacityEnd;
374
375 // allocate new memory
376 mCapacityBegin = mAlloc.allocate(pNewCapacity);
377 // mCapacityBegin = static_cast<T*>(calloc(pInitialCapacity, sizeof(T)));
378 mCapacityEnd = mCapacityBegin + pNewCapacity;
379 mCurrent = mCapacityBegin;
380 mSizeEnd = mCapacityBegin;
381
382 // iterate over old elements and move them
383 // Also deconstruct them
384 // if there are old elements
385 T* val = current;
386 while (sizeEnd != capacityBegin) {
387 PushBack(std::move(*val));
388 val->~T();
389
390 val = val + 1;
391 if (val >= sizeEnd) val = capacityBegin;
392 if (val == current) break;
393 }
394
395 // deallocate old ram
396 mAlloc.deallocate(capacityBegin, capacityEnd - capacityBegin);
397}
398
399template<typename T, typename Allocator>
401 const auto var = begin() + pNum;
402 return *var;
403}
404
405template<typename T, typename Allocator>
407 auto var = begin() + pNum;
408 return *var;
409}
Definition SimpleRingBuffer.h:95
RingBufferIterator operator++(int)
Definition SimpleRingBuffer.h:158
reference operator*() const
Definition SimpleRingBuffer.h:135
T & reference
Definition SimpleRingBuffer.h:100
difference_type operator-(const RingBufferIterator &pOther) const
Definition SimpleRingBuffer.h:172
T value_type
Definition SimpleRingBuffer.h:98
pointer operator->() const
Definition SimpleRingBuffer.h:138
RingBufferIterator(const RingBufferIterator &pOther)=default
std::random_access_iterator_tag iterator_category
Definition SimpleRingBuffer.h:101
RingBufferIterator & operator--()
Definition SimpleRingBuffer.h:149
std::strong_ordering operator<=>(const RingBufferIterator &pOther) const
Definition SimpleRingBuffer.h:122
RingBufferIterator operator+(const difference_type pNum) const
Definition SimpleRingBuffer.h:198
RingBufferIterator operator-(const difference_type pNum) const
Definition SimpleRingBuffer.h:222
std::ptrdiff_t difference_type
Definition SimpleRingBuffer.h:97
std::random_access_iterator_tag iterator_concept
Definition SimpleRingBuffer.h:102
RingBufferIterator(pointer pPtr, bool pBegin, bool pEnd, const RingBuffer< T, Allocator > *pParent)
Definition SimpleRingBuffer.h:106
RingBufferIterator operator--(int)
Definition SimpleRingBuffer.h:164
RingBufferIterator & operator-=(const difference_type pNum)
Definition SimpleRingBuffer.h:210
RingBufferIterator & operator++()
Definition SimpleRingBuffer.h:141
RingBufferIterator(RingBufferIterator &&pOther) noexcept=default
RingBufferIterator & operator=(const RingBufferIterator &pOther)=default
RingBufferIterator & operator+=(const difference_type pNum)
Definition SimpleRingBuffer.h:186
friend RingBufferIterator operator+(const difference_type pLhs, const RingBufferIterator &pRhs)
Definition SimpleRingBuffer.h:204
friend std::ostream & operator<<(std::ostream &pOs, const RingBufferIterator &pObj)
Definition SimpleRingBuffer.h:238
T * pointer
Definition SimpleRingBuffer.h:99
RingBufferIterator & operator=(RingBufferIterator &&pOther) noexcept=default
reference operator[](const difference_type pNum) const
Definition SimpleRingBuffer.h:231
bool operator==(const RingBufferIterator &) const =default
Definition SimpleRingBuffer.h:14
void Resize(size_t pNewCapacity)
Definition SimpleRingBuffer.h:368
virtual ~RingBuffer()
Definition SimpleRingBuffer.h:24
const T & operator[](size_t pNum) const
Definition SimpleRingBuffer.h:400
const RingBufferIterator rend() const
Definition SimpleRingBuffer.h:266
RingBufferIterator end()
Definition SimpleRingBuffer.h:258
RingBuffer & operator=(RingBuffer &&pOther) noexcept
Definition SimpleRingBuffer.h:67
RingBuffer & operator=(const RingBuffer &pOther)
Definition SimpleRingBuffer.h:52
size_t Size() const
Definition SimpleRingBuffer.h:363
RingBufferIterator begin()
Definition SimpleRingBuffer.h:257
const RingBufferIterator cbegin() const
Definition SimpleRingBuffer.h:261
const RingBufferIterator crbegin() const
Definition SimpleRingBuffer.h:267
RingBuffer(const RingBuffer &pOther)
Definition SimpleRingBuffer.h:31
RingBufferIterator rend()
Definition SimpleRingBuffer.h:264
void PushBack(T &&pElement)
Definition SimpleRingBuffer.h:288
const RingBufferIterator cend() const
Definition SimpleRingBuffer.h:262
void Clear()
Definition SimpleRingBuffer.h:354
const RingBufferIterator crend() const
Definition SimpleRingBuffer.h:268
const RingBufferIterator begin() const
Definition SimpleRingBuffer.h:259
void EmplaceBack(const Args &... args)
Definition SimpleRingBuffer.h:338
const RingBufferIterator end() const
Definition SimpleRingBuffer.h:260
RingBufferIterator rbegin()
Definition SimpleRingBuffer.h:263
const RingBufferIterator rbegin() const
Definition SimpleRingBuffer.h:265
RingBuffer(RingBuffer &&pOther) noexcept
Definition SimpleRingBuffer.h:43
RingBuffer(size_t pInitialCapacity)
Definition SimpleRingBuffer.h:16
T & Back()
Definition SimpleRingBuffer.h:344
Definition ArcdpsExtension.h:10