1#pragma once
2
3#include <algorithm>
4#include <type_traits>
5
6#include <basis/seadNew.h>
7#include <basis/seadRawPrint.h>
8#include <basis/seadTypes.h>
9#include <math/seadMathCalcCommon.h>
10#include <prim/seadPtrUtil.h>
11
12namespace sead
13{
14class Heap;
15
16template <typename T>
17class RingBuffer
18{
19public:
20 RingBuffer() = default;
21 RingBuffer(s32 capacity, T* buffer) { setBuffer(capacity, bufferptr: buffer); }
22 template <s32 N>
23 explicit RingBuffer(T (&array)[N]) : RingBuffer(N, array)
24 {
25 }
26
27 class iterator
28 {
29 public:
30 explicit iterator(RingBuffer* buffer, s32 index = 0) : mIndex(index), mBuffer(buffer) {}
31 bool operator==(const iterator& rhs) const
32 {
33 return mIndex == rhs.mIndex && mBuffer == rhs.mBuffer;
34 }
35 bool operator!=(const iterator& rhs) const { return !operator==(rhs); }
36 iterator& operator++()
37 {
38 ++mIndex;
39 return *this;
40 }
41 T& operator*() const { return buffer()(mIndex); }
42 T* operator->() const { return &buffer()(mIndex); }
43 s32 getIndex() const { return mIndex; }
44
45 private:
46 RingBuffer& buffer() const { return *mBuffer; }
47
48 s32 mIndex;
49 RingBuffer* mBuffer;
50 };
51
52 class constIterator
53 {
54 public:
55 explicit constIterator(const RingBuffer* buffer, s32 index = 0)
56 : mIndex(index), mBuffer(buffer)
57 {
58 }
59 bool operator==(const constIterator& rhs) const
60 {
61 return mIndex == rhs.mIndex && mBuffer == rhs.mBuffer;
62 }
63 bool operator!=(const constIterator& rhs) const { return !operator==(rhs); }
64 constIterator& operator++()
65 {
66 ++mIndex;
67 return *this;
68 }
69 const T& operator*() const { return buffer()(mIndex); }
70 const T* operator->() const { return &buffer()(mIndex); }
71 s32 getIndex() const { return mIndex; }
72
73 private:
74 const RingBuffer& buffer() const { return *mBuffer; }
75
76 s32 mIndex;
77 const RingBuffer* mBuffer;
78 };
79
80 iterator begin() { return iterator(this); }
81 constIterator begin() const { return constIterator(this); }
82 iterator begin(s32 start_idx) { return iterator(this, wrapIndex(idx: start_idx)); }
83 constIterator begin(s32 start_idx) const { return constIterator(this, wrapIndex(idx: start_idx)); }
84 iterator end() { return iterator(this, mSize); }
85 constIterator end() const { return constIterator(this, mSize); }
86
87 void allocBuffer(s32 capacity, s32 alignment) { void(tryAllocBuffer(capacity, alignment)); }
88
89 void allocBuffer(s32 capacity, Heap* heap, s32 alignment = sizeof(void*))
90 {
91 static_cast<void>(tryAllocBuffer(capacity, heap, alignment));
92 }
93
94 bool tryAllocBuffer(s32 capacity, s32 alignment = sizeof(void*))
95 {
96 SEAD_ASSERT(mBuffer == nullptr);
97 if (capacity > 0)
98 {
99 T* buffer = new (alignment, std::nothrow) T[capacity];
100 if (buffer)
101 {
102 mBuffer = buffer;
103 mHead = mSize = 0;
104 mCapacity = capacity;
105 SEAD_ASSERT_MSG(PtrUtil::isAlignedPow2(mBuffer, sead::Mathi::abs(alignment)),
106 "don't set alignment for a class with destructor");
107 return true;
108 }
109 return false;
110 }
111 SEAD_ASSERT_MSG(false, "numMax[%d] must be larger than zero", capacity);
112 return false;
113 }
114
115 bool tryAllocBuffer(s32 capacity, Heap* heap, s32 alignment = sizeof(void*))
116 {
117 SEAD_ASSERT(mBuffer == nullptr);
118 if (capacity > 0)
119 {
120 T* buffer = new (heap, alignment, std::nothrow) T[capacity];
121 if (buffer)
122 {
123 mBuffer = buffer;
124 mHead = mSize = 0;
125 mCapacity = capacity;
126 SEAD_ASSERT_MSG(PtrUtil::isAlignedPow2(mBuffer, sead::Mathi::abs(alignment)),
127 "don't set alignment for a class with destructor");
128 return true;
129 }
130 return false;
131 }
132 SEAD_ASSERT_MSG(false, "numMax[%d] must be larger than zero", capacity);
133 return false;
134 }
135
136 void allocBufferAssert(s32 size, Heap* heap, s32 alignment = sizeof(void*))
137 {
138 if (!tryAllocBuffer(size, heap, alignment))
139 AllocFailAssert(heap, sizeof(T) * size, alignment);
140 }
141
142 void freeBuffer()
143 {
144 if (mBuffer)
145 {
146 delete[] mBuffer;
147 mBuffer = nullptr;
148 mCapacity = 0;
149 mHead = 0;
150 mSize = 0;
151 }
152 }
153
154 void setBuffer(s32 capacity, T* bufferptr)
155 {
156 if (capacity < 1)
157 {
158 SEAD_ASSERT_MSG(false, "numMax[%d] must be larger than zero", capacity);
159 return;
160 }
161 if (!bufferptr)
162 {
163 SEAD_ASSERT_MSG(false, "bufferptr is null");
164 return;
165 }
166 mBuffer = bufferptr;
167 mHead = mSize = 0;
168 mCapacity = capacity;
169 }
170
171 bool isBufferReady() const { return mBuffer != nullptr; }
172
173 T& operator[](s32 idx)
174 {
175 if (u32(mSize) <= u32(idx))
176 {
177 SEAD_ASSERT_MSG(false, "index exceeded [%d/%d/%d]", idx, mSize, mCapacity);
178 return mBuffer[0];
179 }
180 return *unsafeGet(idx);
181 }
182
183 const T& operator[](s32 idx) const
184 {
185 if (u32(mSize) <= u32(idx))
186 {
187 SEAD_ASSERT_MSG(false, "index exceeded [%d/%d/%d]", idx, mSize, mCapacity);
188 return mBuffer[0];
189 }
190 return *unsafeGet(idx);
191 }
192
193 T* get(s32 idx)
194 {
195 if (u32(mSize) <= u32(idx))
196 {
197 SEAD_ASSERT_MSG(false, "index exceeded [%d/%d/%d]", idx, mSize, mCapacity);
198 return nullptr;
199 }
200 return unsafeGet(idx);
201 }
202
203 const T* get(s32 idx) const
204 {
205 if (u32(mSize) <= u32(idx))
206 {
207 SEAD_ASSERT_MSG(false, "index exceeded [%d/%d/%d]", idx, mSize, mCapacity);
208 return nullptr;
209 }
210
211 return unsafeGet(idx);
212 }
213
214 T& operator()(s32 idx) { return *unsafeGet(idx); }
215 const T& operator()(s32 idx) const { return *unsafeGet(idx); }
216
217 T* unsafeGet(s32 idx) { return &mBuffer[calcRealIdx(idx)]; }
218 const T* unsafeGet(s32 idx) const { return &mBuffer[calcRealIdx(idx)]; }
219
220 T& front() { return *unsafeGet(0); }
221 const T& front() const { return *unsafeGet(0); }
222
223 T& back()
224 {
225 if (mSize < 1)
226 {
227 SEAD_ASSERT_MSG(false, "no element");
228 return mBuffer[0];
229 }
230 return *unsafeGet(mSize - 1);
231 }
232
233 const T& back() const
234 {
235 if (mSize < 1)
236 {
237 SEAD_ASSERT_MSG(false, "no element");
238 return mBuffer[0];
239 }
240 return *unsafeGet(mSize - 1);
241 }
242
243 s32 capacity() const { return mCapacity; }
244 s32 size() const { return mSize; }
245
246 bool empty() const { return mSize == 0; }
247 explicit operator bool() const { return !empty(); }
248
249 T* data() { return mBuffer; }
250 const T* data() const { return mBuffer; }
251
252 bool forcePushBack(const T& item)
253 {
254 bool isEnoughSpace = pushBack(item);
255 if (!isEnoughSpace)
256 {
257 popFront();
258 pushBack(item);
259 }
260 return isEnoughSpace;
261 }
262
263 bool pushBack(const T& item)
264 {
265 if (mSize >= mCapacity)
266 return false;
267 *unsafeGet(mSize++) = item;
268 return true;
269 }
270
271 void forcePushBackwards(const T& item, u32 offset = 1)
272 {
273 mHead = (mHead < 1 ? mCapacity : mHead) - offset;
274 ++mSize;
275 *unsafeGet(0) = item;
276 }
277
278 bool pushBackwards(const T& item)
279 {
280 if (mSize >= mCapacity)
281 return false;
282 forcePushBackwards(item);
283 return true;
284 }
285
286 T popFront()
287 {
288 if (mSize >= 1)
289 {
290 T item = *unsafeGet(0);
291 mHead = mHead + 1 < mCapacity ? mHead + 1 : 0;
292 --mSize;
293 return item;
294 }
295 SEAD_ASSERT_MSG(false, "no element");
296 return {};
297 }
298
299 void clear() { mHead = mSize = 0; }
300
301protected:
302 s32 calcRealIdx(s32 idx) const
303 {
304 s32 real_idx = mHead + idx;
305 if (real_idx >= mCapacity)
306 real_idx -= mCapacity;
307 return real_idx;
308 }
309
310 s32 wrapIndex(s32 idx) const { return u32(idx) > u32(mSize) ? 0 : idx; }
311
312 T* mBuffer = nullptr;
313 s32 mCapacity = 0;
314 s32 mHead = 0;
315 s32 mSize = 0;
316};
317
318template <typename T, s32 N>
319class FixedRingBuffer : public RingBuffer<T>
320{
321public:
322 FixedRingBuffer() { RingBuffer<T>::setBuffer(N, mData); }
323
324 void allocBuffer(s32 capacity, s32 alignment) = delete;
325 void allocBuffer(s32 capacity, Heap* heap, s32 alignment) = delete;
326 bool tryAllocBuffer(s32 capacity, s32 alignment) = delete;
327 bool tryAllocBuffer(s32 capacity, Heap* heap, s32 alignment) = delete;
328 void allocBufferAssert(s32 size, Heap* heap, s32 alignment) = delete;
329 void freeBuffer() = delete;
330 void setBuffer(s32 capacity, T* bufferptr) = delete;
331
332private:
333 T mData[N];
334};
335} // namespace sead
336