blob: 6f1e2c32d8ca5ddf9a992090de8112f22d1fda0e [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2015-2017. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#pragma once
9
10#include <algorithm>
11
12namespace frc {
13
14template <class T>
15CircularBuffer<T>::CircularBuffer(size_t size) : m_data(size, 0) {}
16
17/**
18 * Push new value onto front of the buffer. The value at the back is overwritten
19 * if the buffer is full.
20 */
21template <class T>
22void CircularBuffer<T>::PushFront(T value) {
23 if (m_data.size() == 0) {
24 return;
25 }
26
27 m_front = ModuloDec(m_front);
28
29 m_data[m_front] = value;
30
31 if (m_length < m_data.size()) {
32 m_length++;
33 }
34}
35
36/**
37 * Push new value onto back of the buffer. The value at the front is overwritten
38 * if the buffer is full.
39 */
40template <class T>
41void CircularBuffer<T>::PushBack(T value) {
42 if (m_data.size() == 0) {
43 return;
44 }
45
46 m_data[(m_front + m_length) % m_data.size()] = value;
47
48 if (m_length < m_data.size()) {
49 m_length++;
50 } else {
51 // Increment front if buffer is full to maintain size
52 m_front = ModuloInc(m_front);
53 }
54}
55
56/**
57 * Pop value at front of buffer.
58 */
59template <class T>
60T CircularBuffer<T>::PopFront() {
61 // If there are no elements in the buffer, do nothing
62 if (m_length == 0) {
63 return 0;
64 }
65
66 T& temp = m_data[m_front];
67 m_front = ModuloInc(m_front);
68 m_length--;
69 return temp;
70}
71
72/**
73 * Pop value at back of buffer.
74 */
75template <class T>
76T CircularBuffer<T>::PopBack() {
77 // If there are no elements in the buffer, do nothing
78 if (m_length == 0) {
79 return 0;
80 }
81
82 m_length--;
83 return m_data[(m_front + m_length) % m_data.size()];
84}
85
86/**
87 * Resizes internal buffer to given size.
88 */
89template <class T>
90void CircularBuffer<T>::Resize(size_t size) {
91 if (size > m_data.size()) {
92 // Find end of buffer
93 size_t insertLocation = (m_front + m_length) % m_data.size();
94
95 // If insertion location precedes front of buffer, push front index back
96 if (insertLocation <= m_front) {
97 m_front += size - m_data.size();
98 }
99
100 // Add elements to end of buffer
101 m_data.insert(m_data.begin() + insertLocation, size - m_data.size(), 0);
102 } else if (size < m_data.size()) {
103 /* 1) Shift element block start at "front" left as many blocks as were
104 * removed up to but not exceeding buffer[0]
105 * 2) Shrink buffer, which will remove even more elements automatically if
106 * necessary
107 */
108 size_t elemsToRemove = m_data.size() - size;
109 auto frontIter = m_data.begin() + m_front;
110 if (m_front < elemsToRemove) {
111 /* Remove elements from end of buffer before shifting start of element
112 * block. Doing so saves a few copies.
113 */
114 m_data.erase(frontIter + size, m_data.end());
115
116 // Shift start of element block to left
117 m_data.erase(m_data.begin(), frontIter);
118
119 // Update metadata
120 m_front = 0;
121 } else {
122 // Shift start of element block to left
123 m_data.erase(frontIter - elemsToRemove, frontIter);
124
125 // Update metadata
126 m_front -= elemsToRemove;
127 }
128
129 /* Length only changes during a shrink if all unused spaces have been
130 * removed. Length decreases as used spaces are removed to meet the
131 * required size.
132 */
133 if (m_length > size) {
134 m_length = size;
135 }
136 }
137}
138
139/**
140 * Sets internal buffer contents to zero.
141 */
142template <class T>
143void CircularBuffer<T>::Reset() {
144 std::fill(m_data.begin(), m_data.end(), 0);
145 m_front = 0;
146 m_length = 0;
147}
148
149/**
150 * @return Element at index starting from front of buffer.
151 */
152template <class T>
153T& CircularBuffer<T>::operator[](size_t index) {
154 return m_data[(m_front + index) % m_data.size()];
155}
156
157/**
158 * @return Element at index starting from front of buffer.
159 */
160template <class T>
161const T& CircularBuffer<T>::operator[](size_t index) const {
162 return m_data[(m_front + index) % m_data.size()];
163}
164
165/**
166 * Increment an index modulo the length of the buffer.
167 *
168 * @return The result of the modulo operation.
169 */
170template <class T>
171size_t CircularBuffer<T>::ModuloInc(size_t index) {
172 return (index + 1) % m_data.size();
173}
174
175/**
176 * Decrement an index modulo the length of the buffer.
177 *
178 * @return The result of the modulo operation.
179 */
180template <class T>
181size_t CircularBuffer<T>::ModuloDec(size_t index) {
182 if (index == 0) {
183 return m_data.size() - 1;
184 } else {
185 return index - 1;
186 }
187}
188
189} // namespace frc