FastLED 3.9.15
Loading...
Searching...
No Matches
circular_buffer.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/stl/int.h"
4#include "fl/stl/move.h" // for fl::move
5#include "fl/stl/vector.h" // for fl::vector_inlined
7#include "fl/stl/noexcept.h"
8
9namespace fl {
10
11// Core circular buffer logic operating on caller-owned storage.
12// Uses a boolean flag (mFull) to distinguish full from empty when head == tail.
13template <typename T>
15 public:
16 circular_buffer_core() FL_NOEXCEPT : mData(nullptr), mCapacity(0), mHead(0), mTail(0), mFull(false) {}
17
18 circular_buffer_core(T* data, fl::size capacity)
19 : mData(data), mCapacity(capacity), mHead(0), mTail(0), mFull(false) {}
20
21 void assign(T* data, fl::size capacity) {
22 mData = data;
24 mHead = 0;
25 mTail = 0;
26 mFull = false;
27 }
28
29 bool push_back(const T &value) {
30 if (mCapacity == 0) return false;
31 if (mFull) {
32 // Overwrite oldest: advance tail
34 }
35 mData[mHead] = value;
37 if (mHead == mTail) {
38 mFull = true;
39 }
40 return true;
41 }
42
43 template<typename... Args>
44 bool emplace_back(Args&&... args) {
45 if (mCapacity == 0) return false;
46 if (mFull) {
47 // Overwrite oldest: advance tail
49 }
52 if (mHead == mTail) {
53 mFull = true;
54 }
55 return true;
56 }
57
58 bool pop_front(T *dst = nullptr) {
59 if (empty()) {
60 return false;
61 }
62 if (dst) {
63 *dst = fl::move(mData[mTail]);
64 }
65 mData[mTail] = T();
67 mFull = false;
68 return true;
69 }
70
71 bool push_front(const T &value) {
72 if (mCapacity == 0) return false;
73 if (mFull) {
74 // Overwrite newest: retreat head
76 }
78 mData[mTail] = value;
79 if (mHead == mTail) {
80 mFull = true;
81 }
82 return true;
83 }
84
85 template<typename... Args>
86 bool emplace_front(Args&&... args) {
87 if (mCapacity == 0) return false;
88 if (mFull) {
89 // Overwrite newest: retreat head
91 }
94 if (mHead == mTail) {
95 mFull = true;
96 }
97 return true;
98 }
99
100 bool pop_back(T *dst = nullptr) {
101 if (empty()) {
102 return false;
103 }
105 if (dst) {
106 *dst = fl::move(mData[mHead]);
107 }
108 mData[mHead] = T();
109 mFull = false;
110 return true;
111 }
112
113 T& front() { return mData[mTail]; }
114 const T& front() const { return mData[mTail]; }
115
116 T& back() { return mData[decrement(mHead)]; }
117 const T& back() const { return mData[decrement(mHead)]; }
118
119 T& operator[](fl::size index) {
120 return mData[(mTail + index) % mCapacity];
121 }
122 const T& operator[](fl::size index) const {
123 return mData[(mTail + index) % mCapacity];
124 }
125
126 fl::size size() const {
127 if (mCapacity == 0) return 0;
128 if (mFull) return mCapacity;
129 return (mHead + mCapacity - mTail) % mCapacity;
130 }
131 fl::size capacity() const { return mCapacity; }
132 bool empty() const { return !mFull && mHead == mTail; }
133 bool full() const { return mFull; }
134
135 void clear() {
136 while (!empty()) {
137 pop_front();
138 }
139 }
140
141 // Expose head/tail/full for move operations.
142 fl::size head() const { return mHead; }
143 fl::size tail() const { return mTail; }
144 bool isFull() const { return mFull; }
145 void setHead(fl::size h) { mHead = h; }
146 void setTail(fl::size t) { mTail = t; }
147 void setFull(bool f) { mFull = f; }
148
149 private:
150 fl::size increment(fl::size index) const { return (index + 1) % mCapacity; }
151 fl::size decrement(fl::size index) const {
152 return (index + mCapacity - 1) % mCapacity;
153 }
154
156 fl::size mCapacity;
157 fl::size mHead;
158 fl::size mTail;
159 bool mFull;
160};
161
162// Unified circular buffer: N > 0 for inline storage, N == 0 for dynamic.
163// Uses vector_inlined internally — inline when N > 0, heap when N == 0.
164template <typename T, fl::size N = 0>
166 public:
167 // Default constructor — pre-sizes to N (useful when N > 0).
169 mStorage.resize(N);
170 mCore.assign(mStorage.data(), N);
171 }
172
173 // PMR-aware constructor.
175 : mStorage(resource) {
176 mStorage.resize(N);
177 mCore.assign(mStorage.data(), N);
178 }
179
180 // Capacity constructor — for dynamic (N==0) or overriding static size.
181 explicit circular_buffer(fl::size capacity) {
182 mStorage.resize(capacity);
183 mCore.assign(mStorage.data(), capacity);
184 }
185
186 // Capacity constructor with PMR.
188 : mStorage(resource) {
189 mStorage.resize(capacity);
190 mCore.assign(mStorage.data(), capacity);
191 }
192
194 : mStorage(other.mStorage),
195 mCore(mStorage.data(), mStorage.size()) {
196 mCore.setHead(other.mCore.head());
197 mCore.setTail(other.mCore.tail());
198 mCore.setFull(other.mCore.isFull());
199 }
200
202 : mStorage(fl::move(other.mStorage)),
203 mCore(mStorage.data(), mStorage.size()) {
204 mCore.setHead(other.mCore.head());
205 mCore.setTail(other.mCore.tail());
206 mCore.setFull(other.mCore.isFull());
207 other.mCore.assign(nullptr, 0);
208 }
209
211 if (this != &other) {
212 mStorage = other.mStorage;
213 mCore.assign(mStorage.data(), mStorage.size());
214 mCore.setHead(other.mCore.head());
215 mCore.setTail(other.mCore.tail());
216 mCore.setFull(other.mCore.isFull());
217 }
218 return *this;
219 }
220
222 if (this != &other) {
223 mStorage = fl::move(other.mStorage);
224 mCore.assign(mStorage.data(), mStorage.size());
225 mCore.setHead(other.mCore.head());
226 mCore.setTail(other.mCore.tail());
227 mCore.setFull(other.mCore.isFull());
228 other.mCore.assign(nullptr, 0);
229 }
230 return *this;
231 }
232
233 void push(const T &value) { mCore.push_back(value); }
234 bool push_back(const T &value) { return mCore.push_back(value); }
235 bool push_front(const T &value) { return mCore.push_front(value); }
236
237 template<typename... Args>
238 bool emplace_back(Args&&... args) { return mCore.emplace_back(fl::forward<Args>(args)...); }
239
240 template<typename... Args>
241 bool emplace_front(Args&&... args) { return mCore.emplace_front(fl::forward<Args>(args)...); }
242
243 bool pop(T &value) { return mCore.pop_front(&value); }
244 bool pop_front(T *dst = nullptr) { return mCore.pop_front(dst); }
245 bool pop_back(T *dst = nullptr) { return mCore.pop_back(dst); }
246
247 T& front() { return mCore.front(); }
248 const T& front() const { return mCore.front(); }
249
250 T& back() { return mCore.back(); }
251 const T& back() const { return mCore.back(); }
252
253 T& operator[](fl::size index) { return mCore[index]; }
254 const T& operator[](fl::size index) const { return mCore[index]; }
255
256 fl::size size() const { return mCore.size(); }
257 fl::size capacity() const { return mCore.capacity(); }
258 bool empty() const { return mCore.empty(); }
259 bool full() const { return mCore.full(); }
260 void clear() { mCore.clear(); }
261
262 void resize(fl::size new_capacity) {
263 // Save existing elements
264 fl::size count = mCore.size();
265 fl::size to_save = (count < new_capacity) ? count : new_capacity;
266 // Use a temporary storage to save elements
267 vector_inlined<T, (N > 0 ? N : 1)> saved;
268 saved.resize(to_save);
269 for (fl::size i = 0; i < to_save; ++i) {
270 saved[i] = mCore[i];
271 }
272 // Resize storage
273 mStorage.resize(new_capacity);
274 mCore.assign(mStorage.data(), new_capacity);
275 // Re-insert saved elements
276 for (fl::size i = 0; i < to_save; ++i) {
277 mCore.push_back(saved[i]);
278 }
279 }
280
282 bool operator==(const circular_buffer& other) const {
283 if (size() != other.size()) return false;
284 for (fl::size i = 0; i < size(); ++i) {
285 if (mCore[i] != other.mCore[i]) return false;
286 }
287 return true;
288 }
289
291 bool operator!=(const circular_buffer& other) const {
292 return !(*this == other);
293 }
294
296 bool operator<(const circular_buffer& other) const {
297 fl::size min_size = (size() < other.size()) ? size() : other.size();
298 for (fl::size i = 0; i < min_size; ++i) {
299 if (mCore[i] < other.mCore[i]) return true;
300 if (other.mCore[i] < mCore[i]) return false;
301 }
302 return size() < other.size();
303 }
304
306 bool operator<=(const circular_buffer& other) const {
307 return *this < other || *this == other;
308 }
309
311 bool operator>(const circular_buffer& other) const {
312 return other < *this;
313 }
314
316 bool operator>=(const circular_buffer& other) const {
317 return other <= *this;
318 }
319
320 memory_resource* get_memory_resource() const { return mStorage.get_resource(); }
321
322 private:
323 vector_inlined<T, (N > 0 ? N : 1)> mStorage;
325};
326
327} // namespace fl
bool emplace_back(Args &&... args)
fl::size decrement(fl::size index) const
bool pop_back(T *dst=nullptr)
bool push_front(const T &value)
void assign(T *data, fl::size capacity)
const T & operator[](fl::size index) const
bool push_back(const T &value)
T & operator[](fl::size index)
bool pop_front(T *dst=nullptr)
fl::size increment(fl::size index) const
circular_buffer_core() FL_NOEXCEPT
circular_buffer_core(T *data, fl::size capacity)
bool emplace_front(Args &&... args)
void push(const T &value)
bool operator<(const circular_buffer &other) const
Lexicographic comparison.
circular_buffer_core< T > mCore
bool pop_back(T *dst=nullptr)
void resize(fl::size new_capacity)
bool push_front(const T &value)
const T & front() const
fl::size capacity() const
bool operator!=(const circular_buffer &other) const
Inequality comparison.
circular_buffer(const circular_buffer &other) FL_NOEXCEPT
circular_buffer & operator=(circular_buffer &&other) FL_NOEXCEPT
circular_buffer(memory_resource *resource)
bool push_back(const T &value)
memory_resource * get_memory_resource() const
bool operator>=(const circular_buffer &other) const
Greater-than-or-equal comparison.
bool operator==(const circular_buffer &other) const
Equality comparison.
circular_buffer() FL_NOEXCEPT
circular_buffer & operator=(const circular_buffer &other) FL_NOEXCEPT
bool operator>(const circular_buffer &other) const
Greater-than comparison.
T & operator[](fl::size index)
vector_inlined< T,(N > 0 ? N :1)> mStorage
circular_buffer(fl::size capacity, memory_resource *resource)
fl::size size() const
circular_buffer(fl::size capacity)
bool pop_front(T *dst=nullptr)
bool operator<=(const circular_buffer &other) const
Less-than-or-equal comparison.
bool emplace_front(Args &&... args)
const T & back() const
bool emplace_back(Args &&... args)
circular_buffer(circular_buffer &&other) FL_NOEXCEPT
const T & operator[](fl::size index) const
Polymorphic memory resource base class (PMR-style).
PMR-style polymorphic memory resource for type-erased allocation.
constexpr T && forward(typename remove_reference< T >::type &t) FL_NOEXCEPT
Definition s16x16x4.h:234
constexpr remove_reference< T >::type && move(T &&t) FL_NOEXCEPT
Definition s16x16x4.h:28
constexpr int type_rank< T >::value
VectorN< T, INLINED_SIZE > vector_inlined
Definition vector.h:1133
Base definition for an LED controller.
Definition crgb.hpp:179
corkscrew_args args
Definition old.h:149
#define FL_NOEXCEPT