CTRE Phoenix 6 C++ 25.0.0-beta-4
Loading...
Searching...
No Matches
circular_buffer.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) Cross The Road Electronics.  All rights reserved.
3 * License information can be found in CTRE_LICENSE.txt
4 * For support and suggestions contact support@ctr-electronics.com or file
5 * an issue tracker at https://github.com/CrossTheRoadElec/Phoenix-Releases
6 */
7#pragma once
8
9#include <iterator>
10#include <stddef.h>
11#include <vector>
12
13namespace ctre {
14namespace phoenix6 {
15namespace swerve {
16
17/**
18 * This is a simple circular buffer so we don't need to "bucket brigade" copy
19 * old values.
20 *
21 * @tparam T Buffer element type.
22 */
23template <class T>
25 public:
26 /**
27 * Constructs a circular buffer.
28 *
29 * @param size Maximum number of buffer elements.
30 */
31 explicit circular_buffer(size_t size) : m_data(size, T{}) {}
32
37
38 class iterator {
39 public:
40 using iterator_category = std::forward_iterator_tag;
41 using value_type = T;
42 using difference_type = std::ptrdiff_t;
43 using pointer = T*;
44 using reference = T&;
45
46 iterator(circular_buffer* buffer, size_t index)
47 : m_buffer{buffer}, m_index{index} {}
48
50 ++m_index;
51 return *this;
52 }
54 iterator retval = *this;
55 ++(*this);
56 return retval;
57 }
58 bool operator==(const iterator& other) const {
59 return m_buffer == other.m_buffer && m_index == other.m_index;
60 }
61 bool operator!=(const iterator& other) const {
62 return !(*this == other);
63 }
64 reference operator*() { return (*m_buffer)[m_index]; }
65
66 private:
67 circular_buffer* m_buffer;
68 size_t m_index;
69 };
70
72 public:
73 using iterator_category = std::forward_iterator_tag;
74 using value_type = T;
75 using difference_type = std::ptrdiff_t;
76 using pointer = T*;
77 using const_reference = const T&;
78
79 const_iterator(const circular_buffer* buffer, size_t index)
80 : m_buffer{buffer}, m_index{index} {}
81
83 ++m_index;
84 return *this;
85 }
87 const_iterator retval = *this;
88 ++(*this);
89 return retval;
90 }
91 bool operator==(const iterator& other) const {
92 return m_buffer == other.m_buffer && m_index == other.m_index;
93 }
94 bool operator!=(const iterator& other) const {
95 return !(*this == other);
96 }
97 const_reference operator*() const { return (*m_buffer)[m_index]; }
98
99 private:
100 const circular_buffer* m_buffer;
101 size_t m_index;
102 };
103
104 iterator begin() { return iterator(this, 0); }
106
107 const_iterator begin() const { return const_iterator(this, 0); }
110 }
111
112 const_iterator cbegin() const { return const_iterator(this, 0); }
115 }
116
117 /**
118 * Returns number of elements in buffer
119 */
120 size_t size() const { return m_length; }
121
122 /**
123 * Returns value at front of buffer
124 */
125 T& front() { return (*this)[0]; }
126
127 /**
128 * Returns value at front of buffer
129 */
130 const T& front() const { return (*this)[0]; }
131
132 /**
133 * Returns value at back of buffer
134 *
135 * If there are no elements in the buffer, calling this function results in
136 * undefined behavior.
137 */
138 T& back() { return m_data[(m_front + m_length - 1) % m_data.size()]; }
139
140 /**
141 * Returns value at back of buffer
142 *
143 * If there are no elements in the buffer, calling this function results in
144 * undefined behavior.
145 */
146 const T& back() const {
147 return m_data[(m_front + m_length - 1) % m_data.size()];
148 }
149
150 /**
151 * Push a new value onto the front of the buffer.
152 *
153 * The value at the back is overwritten if the buffer is full.
154 */
155 void push_front(T value) {
156 if (m_data.size() == 0) {
157 return;
158 }
159
160 m_front = ModuloDec(m_front);
161
162 m_data[m_front] = value;
163
164 if (m_length < m_data.size()) {
165 m_length++;
166 }
167 }
168
169 /**
170 * Push a new value onto the back of the buffer.
171 *
172 * The value at the front is overwritten if the buffer is full.
173 */
174 void push_back(T value) {
175 if (m_data.size() == 0) {
176 return;
177 }
178
179 m_data[(m_front + m_length) % m_data.size()] = value;
180
181 if (m_length < m_data.size()) {
182 m_length++;
183 } else {
184 // Increment front if buffer is full to maintain size
185 m_front = ModuloInc(m_front);
186 }
187 }
188
189 /**
190 * Push a new value onto the front of the buffer that is constructed with the
191 * provided constructor arguments.
192 *
193 * The value at the back is overwritten if the buffer is full.
194 */
195 template <class... Args>
196 void emplace_front(Args&&... args) {
197 if (m_data.size() == 0) {
198 return;
199 }
200
201 m_front = ModuloDec(m_front);
202
203 m_data[m_front] = T{args...};
204
205 if (m_length < m_data.size()) {
206 m_length++;
207 }
208 }
209
210 /**
211 * Push a new value onto the back of the buffer that is constructed with the
212 * provided constructor arguments.
213 *
214 * The value at the front is overwritten if the buffer is full.
215 */
216 template <class... Args>
217 void emplace_back(Args&&... args) {
218 if (m_data.size() == 0) {
219 return;
220 }
221
222 m_data[(m_front + m_length) % m_data.size()] = T{args...};
223
224 if (m_length < m_data.size()) {
225 m_length++;
226 } else {
227 // Increment front if buffer is full to maintain size
228 m_front = ModuloInc(m_front);
229 }
230 }
231
232 /**
233 * Pop value at front of buffer.
234 *
235 * If there are no elements in the buffer, calling this function results in
236 * undefined behavior.
237 */
239 T& temp = m_data[m_front];
240 m_front = ModuloInc(m_front);
241 m_length--;
242 return temp;
243 }
244
245 /**
246 * Pop value at back of buffer.
247 *
248 * If there are no elements in the buffer, calling this function results in
249 * undefined behavior.
250 */
252 m_length--;
253 return m_data[(m_front + m_length) % m_data.size()];
254 }
255
256 /**
257 * Resizes internal buffer to given size.
258 */
259 void resize(size_t size)
260 {
261 if (size > m_data.size()) {
262 // Find end of buffer
263 size_t insertLocation = (m_front + m_length) % m_data.size();
264
265 // If insertion location precedes front of buffer, push front index back
266 if (insertLocation <= m_front) {
267 m_front += size - m_data.size();
268 }
269
270 // Add elements to end of buffer
271 m_data.insert(m_data.begin() + insertLocation, size - m_data.size(), 0);
272 } else if (size < m_data.size()) {
273 /* 1) Shift element block start at "front" left as many blocks as were
274 * removed up to but not exceeding buffer[0]
275 * 2) Shrink buffer, which will remove even more elements automatically if
276 * necessary
277 */
278 size_t elemsToRemove = m_data.size() - size;
279 auto frontIter = m_data.begin() + m_front;
280 if (m_front < elemsToRemove) {
281 /* Remove elements from end of buffer before shifting start of element
282 * block. Doing so saves a few copies.
283 */
284 m_data.erase(frontIter + size, m_data.end());
285
286 // Shift start of element block to left
287 m_data.erase(m_data.begin(), frontIter);
288
289 // Update metadata
290 m_front = 0;
291 } else {
292 // Shift start of element block to left
293 m_data.erase(frontIter - elemsToRemove, frontIter);
294
295 // Update metadata
296 m_front -= elemsToRemove;
297 }
298
299 /* Length only changes during a shrink if all unused spaces have been
300 * removed. Length decreases as used spaces are removed to meet the
301 * required size.
302 */
303 if (m_length > size) {
304 m_length = size;
305 }
306 }
307 }
308
309 /**
310 * Empties internal buffer.
311 */
312 void reset() {
313 m_front = 0;
314 m_length = 0;
315 }
316
317 /**
318 * @return Element at index starting from front of buffer.
319 */
320 T& operator[](size_t index) {
321 return m_data[(m_front + index) % m_data.size()];
322 }
323
324 /**
325 * @return Element at index starting from front of buffer.
326 */
327 const T& operator[](size_t index) const {
328 return m_data[(m_front + index) % m_data.size()];
329 }
330
331 private:
332 std::vector<T> m_data;
333
334 // Index of element at front of buffer
335 size_t m_front = 0;
336
337 // Number of elements used in buffer
338 size_t m_length = 0;
339
340 /**
341 * Increment an index modulo the size of the buffer.
342 *
343 * @param index Index into the buffer.
344 * @return The incremented index.
345 */
346 size_t ModuloInc(size_t index) { return (index + 1) % m_data.size(); }
347
348 /**
349 * Decrement an index modulo the size of the buffer.
350 *
351 * @param index Index into the buffer.
352 * @return The decremented index.
353 */
354 size_t ModuloDec(size_t index) {
355 if (index == 0) {
356 return m_data.size() - 1;
357 } else {
358 return index - 1;
359 }
360 }
361};
362
363}
364}
365}
std::forward_iterator_tag iterator_category
Definition circular_buffer.hpp:73
T value_type
Definition circular_buffer.hpp:74
const_iterator(const circular_buffer *buffer, size_t index)
Definition circular_buffer.hpp:79
std::ptrdiff_t difference_type
Definition circular_buffer.hpp:75
const_iterator & operator++()
Definition circular_buffer.hpp:82
bool operator!=(const iterator &other) const
Definition circular_buffer.hpp:94
bool operator==(const iterator &other) const
Definition circular_buffer.hpp:91
const_reference operator*() const
Definition circular_buffer.hpp:97
T * pointer
Definition circular_buffer.hpp:76
const_iterator operator++(int)
Definition circular_buffer.hpp:86
const T & const_reference
Definition circular_buffer.hpp:77
Definition circular_buffer.hpp:38
iterator operator++(int)
Definition circular_buffer.hpp:53
std::ptrdiff_t difference_type
Definition circular_buffer.hpp:42
iterator & operator++()
Definition circular_buffer.hpp:49
T & reference
Definition circular_buffer.hpp:44
std::forward_iterator_tag iterator_category
Definition circular_buffer.hpp:40
bool operator!=(const iterator &other) const
Definition circular_buffer.hpp:61
bool operator==(const iterator &other) const
Definition circular_buffer.hpp:58
T value_type
Definition circular_buffer.hpp:41
T * pointer
Definition circular_buffer.hpp:43
iterator(circular_buffer *buffer, size_t index)
Definition circular_buffer.hpp:46
reference operator*()
Definition circular_buffer.hpp:64
This is a simple circular buffer so we don't need to "bucket brigade" copy old values.
Definition circular_buffer.hpp:24
const_iterator end() const
Definition circular_buffer.hpp:108
void push_front(T value)
Push a new value onto the front of the buffer.
Definition circular_buffer.hpp:155
circular_buffer(const circular_buffer &)=default
circular_buffer(size_t size)
Constructs a circular buffer.
Definition circular_buffer.hpp:31
const_iterator begin() const
Definition circular_buffer.hpp:107
void resize(size_t size)
Resizes internal buffer to given size.
Definition circular_buffer.hpp:259
circular_buffer & operator=(circular_buffer &&)=default
const_iterator cbegin() const
Definition circular_buffer.hpp:112
T pop_front()
Pop value at front of buffer.
Definition circular_buffer.hpp:238
const_iterator cend() const
Definition circular_buffer.hpp:113
const T & operator[](size_t index) const
Definition circular_buffer.hpp:327
size_t size() const
Returns number of elements in buffer.
Definition circular_buffer.hpp:120
void reset()
Empties internal buffer.
Definition circular_buffer.hpp:312
circular_buffer(circular_buffer &&)=default
circular_buffer & operator=(const circular_buffer &)=default
void emplace_front(Args &&... args)
Push a new value onto the front of the buffer that is constructed with the provided constructor argum...
Definition circular_buffer.hpp:196
T & front()
Returns value at front of buffer.
Definition circular_buffer.hpp:125
iterator end()
Definition circular_buffer.hpp:105
T pop_back()
Pop value at back of buffer.
Definition circular_buffer.hpp:251
T & operator[](size_t index)
Definition circular_buffer.hpp:320
iterator begin()
Definition circular_buffer.hpp:104
const T & front() const
Returns value at front of buffer.
Definition circular_buffer.hpp:130
const T & back() const
Returns value at back of buffer.
Definition circular_buffer.hpp:146
void emplace_back(Args &&... args)
Push a new value onto the back of the buffer that is constructed with the provided constructor argume...
Definition circular_buffer.hpp:217
T & back()
Returns value at back of buffer.
Definition circular_buffer.hpp:138
void push_back(T value)
Push a new value onto the back of the buffer.
Definition circular_buffer.hpp:174
Definition StatusCodes.h:18