20
20
#include < stdint.h>
21
21
#include " platform/mbed_critical.h"
22
22
#include " platform/mbed_assert.h"
23
+ #include " platform/Span.h"
24
+ #include " platform/mbed_atomic.h"
23
25
24
26
namespace mbed {
25
27
@@ -60,8 +62,8 @@ struct is_unsigned<unsigned long long> {
60
62
61
63
/* * Templated Circular buffer class
62
64
*
63
- * @note Synchronization level: Interrupt safe
64
- * @note CounterType must be unsigned and consistent with BufferSize
65
+ * @note Synchronization level: Interrupt safe.
66
+ * @note CounterType must be unsigned and consistent with BufferSize.
65
67
*/
66
68
template <typename T, uint32_t BufferSize, typename CounterType = uint32_t >
67
69
class CircularBuffer {
@@ -84,77 +86,195 @@ class CircularBuffer {
84
86
{
85
87
}
86
88
87
- /* * Push the transaction to the buffer. This overwrites the buffer if it's
88
- * full
89
+ /* * Push the transaction to the buffer. This overwrites the buffer if it's full.
89
90
*
90
- * @param data Data to be pushed to the buffer
91
+ * @param data Data to be pushed to the buffer.
91
92
*/
92
93
void push (const T &data)
93
94
{
94
95
core_util_critical_section_enter ();
95
- if (full ()) {
96
- _tail++;
97
- if (_tail == BufferSize) {
98
- _tail = 0 ;
99
- }
96
+
97
+ _buffer[_head] = data;
98
+
99
+ _head = incrementCounter (_head);
100
+
101
+ if (_full) {
102
+ _tail = _head;
103
+ } else if (_head == _tail) {
104
+ _full = true ;
100
105
}
101
- _pool[_head++] = data;
102
- if (_head == BufferSize) {
106
+
107
+ core_util_critical_section_exit ();
108
+ }
109
+
110
+ /* * Push the transaction to the buffer. This overwrites the buffer if it's full.
111
+ *
112
+ * @param src Data to be pushed to the buffer.
113
+ * @param len Number of items to be pushed to the buffer.
114
+ */
115
+ void push (const T* src, CounterType len)
116
+ {
117
+ core_util_critical_section_enter ();
118
+
119
+ /* if we try to write more bytes than the buffer can hold we only bother writing the last bytes */
120
+ if (len > BufferSize) {
121
+ _tail = 0 ;
103
122
_head = 0 ;
104
- }
105
- if (_head == _tail) {
106
123
_full = true ;
124
+ std::copy (src + len - BufferSize, src + len, _buffer);
125
+ } else {
126
+ /* we need to adjust the tail at the end if we're filling the buffer of overflowing */
127
+ bool adjust_tail = ((BufferSize - non_critical_size ()) <= len);
128
+
129
+ CounterType written = len;
130
+
131
+ /* on first pass we write as much as we can to the right of head */
132
+ if ((_head + len) > BufferSize) {
133
+ written = BufferSize - _head;
134
+ }
135
+
136
+ std::copy (src, src + written, _buffer + _head);
137
+
138
+ CounterType left_to_write = len - written;
139
+
140
+ /* we might need to continue to write from the start of the buffer */
141
+ if (left_to_write) {
142
+ std::copy (src + written, src + written + left_to_write, _buffer);
143
+ written += left_to_write;
144
+ }
145
+
146
+ _head += written;
147
+ _head %= BufferSize;
148
+
149
+ if (adjust_tail) {
150
+ _tail = _head;
151
+ _full = true ;
152
+ }
107
153
}
154
+
108
155
core_util_critical_section_exit ();
109
156
}
110
157
111
- /* * Pop the transaction from the buffer
158
+ /* * Push the transaction to the buffer. This overwrites the buffer if it's full.
112
159
*
113
- * @param data Data to be popped from the buffer
114
- * @return True if the buffer is not empty and data contains a transaction, false otherwise
160
+ * @param src Data to be pushed to the buffer.
115
161
*/
116
- bool pop (T &data)
162
+ void push (mbed::Span<const T> src)
163
+ {
164
+ push (src.data (), src.size ());
165
+ }
166
+
167
+ /* * Pop from the buffer.
168
+ *
169
+ * @param data Container to store the data to be popped from the buffer.
170
+ * @return True if data popped.
171
+ */
172
+ bool pop (T& data)
117
173
{
118
174
bool data_popped = false ;
175
+
119
176
core_util_critical_section_enter ();
120
- if (!empty ()) {
121
- data = _pool[_tail++];
122
- if (_tail == BufferSize) {
123
- _tail = 0 ;
177
+
178
+ if (!non_critical_empty ()) {
179
+ data_popped = true ;
180
+
181
+ data = _buffer[_tail];
182
+ _tail = incrementCounter (_tail);
183
+ _full = false ;
184
+ }
185
+
186
+ core_util_critical_section_exit ();
187
+
188
+ return data_popped;
189
+ }
190
+
191
+ /* *
192
+ * Pop multiple elements from the buffer.
193
+ *
194
+ * @param dest The array which will receive the elements.
195
+ * @param len The number of elements to pop.
196
+ *
197
+ * @return The number of elements popped.
198
+ */
199
+ CounterType pop (T* dest, CounterType len)
200
+ {
201
+ if (len == 0 ) {
202
+ return 0 ;
203
+ }
204
+
205
+ CounterType data_popped = 0 ;
206
+
207
+ core_util_critical_section_enter ();
208
+
209
+ if (!non_critical_empty ()) {
210
+ /* make sure we only try to read as much as we have items present */
211
+ if (len > non_critical_size ()) {
212
+ len = non_critical_size ();
124
213
}
214
+ data_popped = len;
215
+
216
+ /* items may be split by overlap, take only the number we have to the right of tail */
217
+ if ((_tail + data_popped) > BufferSize) {
218
+ data_popped = BufferSize - _tail;
219
+ }
220
+
221
+ std::copy (_buffer + _tail, _buffer + _tail + data_popped, dest);
222
+
223
+ /* if we looped over the end we may need to pop again */
224
+ CounterType left_to_pop = len - data_popped;
225
+
226
+ if (left_to_pop) {
227
+ std::copy (_buffer, _buffer + left_to_pop, dest + data_popped);
228
+
229
+ data_popped += left_to_pop;
230
+ }
231
+
232
+ _tail += data_popped;
233
+ _tail %= BufferSize;
125
234
_full = false ;
126
- data_popped = true ;
127
235
}
236
+
128
237
core_util_critical_section_exit ();
238
+
129
239
return data_popped;
130
240
}
131
241
132
- /* * Check if the buffer is empty
242
+ /* *
243
+ * Pop multiple elements from the buffer.
244
+ *
245
+ * @param dest The span that contains the buffer that will be used to store the elements.
133
246
*
134
- * @return True if the buffer is empty, false if not
247
+ * @return The span with the size set to number of elements popped using the buffer passed in as the parameter.
248
+ */
249
+ mbed::Span<T> pop (mbed::Span<T> dest)
250
+ {
251
+ CounterType popped = pop (dest.data (), dest.size ());
252
+ return mbed::make_Span (dest.data (), popped);
253
+ }
254
+
255
+ /* * Check if the buffer is empty.
256
+ *
257
+ * @return True if the buffer is empty, false if not.
135
258
*/
136
259
bool empty () const
137
260
{
138
261
core_util_critical_section_enter ();
139
- bool is_empty = (_head == _tail) && !_full ;
262
+ bool is_empty = non_critical_empty () ;
140
263
core_util_critical_section_exit ();
141
264
return is_empty;
142
265
}
143
266
144
- /* * Check if the buffer is full
267
+ /* * Check if the buffer is full.
145
268
*
146
269
* @return True if the buffer is full, false if not
147
270
*/
148
271
bool full () const
149
272
{
150
- core_util_critical_section_enter ();
151
- bool full = _full;
152
- core_util_critical_section_exit ();
153
- return full;
273
+ return core_util_atomic_load_bool (&_full);
154
274
}
155
275
156
- /* * Reset the buffer
157
- *
276
+ /* *
277
+ * Reset the buffer.
158
278
*/
159
279
void reset ()
160
280
{
@@ -165,43 +285,63 @@ class CircularBuffer {
165
285
core_util_critical_section_exit ();
166
286
}
167
287
168
- /* * Get the number of elements currently stored in the circular_buffer */
288
+ /* *
289
+ * Get the number of elements currently stored in the circular_buffer.
290
+ */
169
291
CounterType size () const
170
292
{
171
293
core_util_critical_section_enter ();
172
- CounterType elements;
173
- if (!_full) {
174
- if (_head < _tail) {
175
- elements = BufferSize + _head - _tail;
176
- } else {
177
- elements = _head - _tail;
178
- }
179
- } else {
180
- elements = BufferSize;
181
- }
294
+ CounterType elements = non_critical_size ();
182
295
core_util_critical_section_exit ();
183
296
return elements;
184
297
}
185
298
186
- /* * Peek into circular buffer without popping
299
+ /* * Peek into circular buffer without popping.
187
300
*
188
- * @param data Data to be peeked from the buffer
189
- * @return True if the buffer is not empty and data contains a transaction, false otherwise
301
+ * @param data Data to be peeked from the buffer.
302
+ * @return True if the buffer is not empty and data contains a transaction, false otherwise.
190
303
*/
191
304
bool peek (T &data) const
192
305
{
193
306
bool data_updated = false ;
194
307
core_util_critical_section_enter ();
195
308
if (!empty ()) {
196
- data = _pool [_tail];
309
+ data = _buffer [_tail];
197
310
data_updated = true ;
198
311
}
199
312
core_util_critical_section_exit ();
200
313
return data_updated;
201
314
}
202
315
203
316
private:
204
- T _pool[BufferSize];
317
+ bool non_critical_empty () const
318
+ {
319
+ bool is_empty = (_head == _tail) && !_full;
320
+ return is_empty;
321
+ }
322
+
323
+ CounterType non_critical_size () const
324
+ {
325
+ CounterType elements;
326
+ if (!_full) {
327
+ if (_head < _tail) {
328
+ elements = BufferSize + _head - _tail;
329
+ } else {
330
+ elements = _head - _tail;
331
+ }
332
+ } else {
333
+ elements = BufferSize;
334
+ }
335
+ return elements;
336
+ }
337
+
338
+ CounterType incrementCounter (CounterType val)
339
+ {
340
+ return (++val) % BufferSize;
341
+ }
342
+
343
+ private:
344
+ T _buffer[BufferSize];
205
345
CounterType _head;
206
346
CounterType _tail;
207
347
bool _full;
0 commit comments