CTRE Phoenix 6 C++ 26.0.0-beta-1
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 reference operator*() { return (*m_buffer)[m_index]; }
62
63 private:
64 circular_buffer* m_buffer;
65 size_t m_index;
66 };
67
69 public:
70 using iterator_category = std::forward_iterator_tag;
71 using value_type = T;
72 using difference_type = std::ptrdiff_t;
73 using pointer = T*;
74 using const_reference = const T&;
75
76 const_iterator(const circular_buffer* buffer, size_t index)
77 : m_buffer{buffer}, m_index{index} {}
78
80 ++m_index;
81 return *this;
82 }
84 const_iterator retval = *this;
85 ++(*this);
86 return retval;
87 }
88 bool operator==(const iterator& other) const {
89 return m_buffer == other.m_buffer && m_index == other.m_index;
90 }
91 const_reference operator*() const { return (*m_buffer)[m_index]; }
92
93 private:
94 const circular_buffer* m_buffer;
95 size_t m_index;
96 };
97
98 iterator begin() { return iterator(this, 0); }
100
101 const_iterator begin() const { return const_iterator(this, 0); }
104 }
105
106 const_iterator cbegin() const { return const_iterator(this, 0); }
109 }
110
111 /**
112 * Returns number of elements in buffer
113 */
114 size_t size() const { return m_length; }
115
116 /**
117 * Returns value at front of buffer
118 */
119 T& front() { return (*this)[0]; }
120
121 /**
122 * Returns value at front of buffer
123 */
124 const T& front() const { return (*this)[0]; }
125
126 /**
127 * Returns value at back of buffer
128 *
129 * If there are no elements in the buffer, calling this function results in
130 * undefined behavior.
131 */
132 T& back() { return m_data[(m_front + m_length - 1) % m_data.size()]; }
133
134 /**
135 * Returns value at back of buffer
136 *
137 * If there are no elements in the buffer, calling this function results in
138 * undefined behavior.
139 */
140 const T& back() const {
141 return m_data[(m_front + m_length - 1) % m_data.size()];
142 }
143
144 /**
145 * Push a new value onto the front of the buffer.
146 *
147 * The value at the back is overwritten if the buffer is full.
148 */
149 void push_front(T value) {
150 if (m_data.size() == 0) {
151 return;
152 }
153
154 m_front = ModuloDec(m_front);
155
156 m_data[m_front] = value;
157
158 if (m_length < m_data.size()) {
159 m_length++;
160 }
161 }
162
163 /**
164 * Push a new value onto the back of the buffer.
165 *
166 * The value at the front is overwritten if the buffer is full.
167 */
168 void push_back(T value) {
169 if (m_data.size() == 0) {
170 return;
171 }
172
173 m_data[(m_front + m_length) % m_data.size()] = value;
174
175 if (m_length < m_data.size()) {
176 m_length++;
177 } else {
178 // Increment front if buffer is full to maintain size
179 m_front = ModuloInc(m_front);
180 }
181 }
182
183 /**
184 * Push a new value onto the front of the buffer that is constructed with the
185 * provided constructor arguments.
186 *
187 * The value at the back is overwritten if the buffer is full.
188 */
189 template <class... Args>
190 void emplace_front(Args&&... args) {
191 if (m_data.size() == 0) {
192 return;
193 }
194
195 m_front = ModuloDec(m_front);
196
197 m_data[m_front] = T{args...};
198
199 if (m_length < m_data.size()) {
200 m_length++;
201 }
202 }
203
204 /**
205 * Push a new value onto the back of the buffer that is constructed with the
206 * provided constructor arguments.
207 *
208 * The value at the front is overwritten if the buffer is full.
209 */
210 template <class... Args>
211 void emplace_back(Args&&... args) {
212 if (m_data.size() == 0) {
213 return;
214 }
215
216 m_data[(m_front + m_length) % m_data.size()] = T{args...};
217
218 if (m_length < m_data.size()) {
219 m_length++;
220 } else {
221 // Increment front if buffer is full to maintain size
222 m_front = ModuloInc(m_front);
223 }
224 }
225
226 /**
227 * Pop value at front of buffer.
228 *
229 * If there are no elements in the buffer, calling this function results in
230 * undefined behavior.
231 */
233 T& temp = m_data[m_front];
234 m_front = ModuloInc(m_front);
235 m_length--;
236 return temp;
237 }
238
239 /**
240 * Pop value at back of buffer.
241 *
242 * If there are no elements in the buffer, calling this function results in
243 * undefined behavior.
244 */
246 m_length--;
247 return m_data[(m_front + m_length) % m_data.size()];
248 }
249
250 /**
251 * Resizes internal buffer to given size.
252 */
253 void resize(size_t size)
254 {
255 if (size > m_data.size()) {
256 // Find end of buffer
257 size_t insertLocation = (m_front + m_length) % m_data.size();
258
259 // If insertion location precedes front of buffer, push front index back
260 if (insertLocation <= m_front) {
261 m_front += size - m_data.size();
262 }
263
264 // Add elements to end of buffer
265 m_data.insert(m_data.begin() + insertLocation, size - m_data.size(), 0);
266 } else if (size < m_data.size()) {
267 /* 1) Shift element block start at "front" left as many blocks as were
268 * removed up to but not exceeding buffer[0]
269 * 2) Shrink buffer, which will remove even more elements automatically if
270 * necessary
271 */
272 size_t elemsToRemove = m_data.size() - size;
273 auto frontIter = m_data.begin() + m_front;
274 if (m_front < elemsToRemove) {
275 /* Remove elements from end of buffer before shifting start of element
276 * block. Doing so saves a few copies.
277 */
278 m_data.erase(frontIter + size, m_data.end());
279
280 // Shift start of element block to left
281 m_data.erase(m_data.begin(), frontIter);
282
283 // Update metadata
284 m_front = 0;
285 } else {
286 // Shift start of element block to left
287 m_data.erase(frontIter - elemsToRemove, frontIter);
288
289 // Update metadata
290 m_front -= elemsToRemove;
291 }
292
293 /* Length only changes during a shrink if all unused spaces have been
294 * removed. Length decreases as used spaces are removed to meet the
295 * required size.
296 */
297 if (m_length > size) {
298 m_length = size;
299 }
300 }
301 }
302
303 /**
304 * Empties internal buffer.
305 */
306 void reset() {
307 m_front = 0;
308 m_length = 0;
309 }
310
311 /**
312 * @return Element at index starting from front of buffer.
313 */
314 T& operator[](size_t index) {
315 return m_data[(m_front + index) % m_data.size()];
316 }
317
318 /**
319 * @return Element at index starting from front of buffer.
320 */
321 const T& operator[](size_t index) const {
322 return m_data[(m_front + index) % m_data.size()];
323 }
324
325 private:
326 std::vector<T> m_data;
327
328 // Index of element at front of buffer
329 size_t m_front = 0;
330
331 // Number of elements used in buffer
332 size_t m_length = 0;
333
334 /**
335 * Increment an index modulo the size of the buffer.
336 *
337 * @param index Index into the buffer.
338 * @return The incremented index.
339 */
340 size_t ModuloInc(size_t index) { return (index + 1) % m_data.size(); }
341
342 /**
343 * Decrement an index modulo the size of the buffer.
344 *
345 * @param index Index into the buffer.
346 * @return The decremented index.
347 */
348 size_t ModuloDec(size_t index) {
349 if (index == 0) {
350 return m_data.size() - 1;
351 } else {
352 return index - 1;
353 }
354 }
355};
356
357}
358}
359}
std::forward_iterator_tag iterator_category
Definition circular_buffer.hpp:70
T value_type
Definition circular_buffer.hpp:71
const_iterator(const circular_buffer *buffer, size_t index)
Definition circular_buffer.hpp:76
std::ptrdiff_t difference_type
Definition circular_buffer.hpp:72
const_iterator & operator++()
Definition circular_buffer.hpp:79
bool operator==(const iterator &other) const
Definition circular_buffer.hpp:88
const_reference operator*() const
Definition circular_buffer.hpp:91
T * pointer
Definition circular_buffer.hpp:73
const_iterator operator++(int)
Definition circular_buffer.hpp:83
const T & const_reference
Definition circular_buffer.hpp:74
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: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:61
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:102
void push_front(T value)
Push a new value onto the front of the buffer.
Definition circular_buffer.hpp:149
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:101
void resize(size_t size)
Resizes internal buffer to given size.
Definition circular_buffer.hpp:253
circular_buffer & operator=(circular_buffer &&)=default
const_iterator cbegin() const
Definition circular_buffer.hpp:106
T pop_front()
Pop value at front of buffer.
Definition circular_buffer.hpp:232
const_iterator cend() const
Definition circular_buffer.hpp:107
const T & operator[](size_t index) const
Definition circular_buffer.hpp:321
size_t size() const
Returns number of elements in buffer.
Definition circular_buffer.hpp:114
void reset()
Empties internal buffer.
Definition circular_buffer.hpp:306
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:190
T & front()
Returns value at front of buffer.
Definition circular_buffer.hpp:119
iterator end()
Definition circular_buffer.hpp:99
T pop_back()
Pop value at back of buffer.
Definition circular_buffer.hpp:245
T & operator[](size_t index)
Definition circular_buffer.hpp:314
iterator begin()
Definition circular_buffer.hpp:98
const T & front() const
Returns value at front of buffer.
Definition circular_buffer.hpp:124
const T & back() const
Returns value at back of buffer.
Definition circular_buffer.hpp:140
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:211
T & back()
Returns value at back of buffer.
Definition circular_buffer.hpp:132
void push_back(T value)
Push a new value onto the back of the buffer.
Definition circular_buffer.hpp:168
Definition motor_constants.h:14