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