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,197 @@ 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
+ MBED_ASSERT (len > 0 );
118
+
119
+ core_util_critical_section_enter ();
120
+
121
+ /* if we try to write more bytes than the buffer can hold we only bother writing the last bytes */
122
+ if (len > BufferSize) {
123
+ _tail = 0 ;
103
124
_head = 0 ;
104
- }
105
- if (_head == _tail) {
106
125
_full = true ;
126
+ std::copy (src + len - BufferSize, src + len, _buffer);
127
+ } else {
128
+ /* we need to adjust the tail at the end if we're filling the buffer of overflowing */
129
+ bool adjust_tail = ((BufferSize - non_critical_size ()) <= len);
130
+
131
+ CounterType written = len;
132
+
133
+ /* on first pass we write as much as we can to the right of head */
134
+ if ((_head + written) > BufferSize) {
135
+ written = BufferSize - _head;
136
+ }
137
+
138
+ std::copy (src, src + written, _buffer + _head);
139
+ _head = incrementCounter (_head, written);
140
+
141
+ CounterType left_to_write = len - written;
142
+
143
+ /* we might need to continue to write from the start of the buffer */
144
+ if (left_to_write) {
145
+ std::copy (src + written, src + written + left_to_write, _buffer);
146
+ _head = left_to_write;
147
+ }
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.
159
+ *
160
+ * @param src Data to be pushed to the buffer.
161
+ */
162
+ void push (mbed::Span<const T> src)
163
+ {
164
+ push (src.data (), src.size ());
165
+ }
166
+
167
+ /* * Pop from the buffer.
112
168
*
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
169
+ * @param data Container to store the data to be popped from the buffer.
170
+ * @return True if data popped.
115
171
*/
116
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
+ MBED_ASSERT (len > 0 );
202
+
203
+ if (len == 0 ) {
204
+ return 0 ;
205
+ }
206
+
207
+ CounterType data_popped = 0 ;
208
+
209
+ core_util_critical_section_enter ();
210
+
211
+ if (!non_critical_empty ()) {
212
+ /* make sure we only try to read as much as we have items present */
213
+ if (len > non_critical_size ()) {
214
+ len = non_critical_size ();
215
+ }
216
+ data_popped = len;
217
+
218
+ /* items may be split by overlap, take only the number we have to the right of tail */
219
+ if ((_tail + data_popped) > BufferSize) {
220
+ data_popped = BufferSize - _tail;
124
221
}
222
+
223
+ std::copy (_buffer + _tail, _buffer + _tail + data_popped, dest);
224
+ _tail = incrementCounter (_tail, data_popped);
225
+
226
+ /* if we looped over the end we may need to pop again */
227
+ CounterType left_to_pop = len - data_popped;
228
+
229
+ if (left_to_pop) {
230
+ std::copy (_buffer, _buffer + left_to_pop, dest + data_popped);
231
+ _tail = left_to_pop;
232
+
233
+ data_popped += left_to_pop;
234
+ }
235
+
125
236
_full = false ;
126
- data_popped = true ;
127
237
}
238
+
128
239
core_util_critical_section_exit ();
240
+
129
241
return data_popped;
130
242
}
131
243
132
- /* * Check if the buffer is empty
244
+ /* *
245
+ * Pop multiple elements from the buffer.
246
+ *
247
+ * @param dest The span that contains the buffer that will be used to store the elements.
248
+ *
249
+ * @return The span with the size set to number of elements popped using the buffer passed in as the parameter.
250
+ */
251
+ mbed::Span<T> pop (mbed::Span<T> dest)
252
+ {
253
+ CounterType popped = pop (dest.data (), dest.size ());
254
+ return mbed::make_Span (dest.data (), popped);
255
+ }
256
+
257
+ /* * Check if the buffer is empty.
133
258
*
134
- * @return True if the buffer is empty, false if not
259
+ * @return True if the buffer is empty, false if not.
135
260
*/
136
261
bool empty () const
137
262
{
138
263
core_util_critical_section_enter ();
139
- bool is_empty = (_head == _tail) && !_full ;
264
+ bool is_empty = non_critical_empty () ;
140
265
core_util_critical_section_exit ();
141
266
return is_empty;
142
267
}
143
268
144
- /* * Check if the buffer is full
269
+ /* * Check if the buffer is full.
145
270
*
146
271
* @return True if the buffer is full, false if not
147
272
*/
148
273
bool full () const
149
274
{
150
- core_util_critical_section_enter ();
151
- bool full = _full;
152
- core_util_critical_section_exit ();
153
- return full;
275
+ return core_util_atomic_load_bool (&_full);
154
276
}
155
277
156
- /* * Reset the buffer
157
- *
278
+ /* *
279
+ * Reset the buffer.
158
280
*/
159
281
void reset ()
160
282
{
@@ -165,10 +287,43 @@ class CircularBuffer {
165
287
core_util_critical_section_exit ();
166
288
}
167
289
168
- /* * Get the number of elements currently stored in the circular_buffer */
290
+ /* *
291
+ * Get the number of elements currently stored in the circular_buffer.
292
+ */
169
293
CounterType size () const
170
294
{
171
295
core_util_critical_section_enter ();
296
+ CounterType elements = non_critical_size ();
297
+ core_util_critical_section_exit ();
298
+ return elements;
299
+ }
300
+
301
+ /* * Peek into circular buffer without popping.
302
+ *
303
+ * @param data Data to be peeked from the buffer.
304
+ * @return True if the buffer is not empty and data contains a transaction, false otherwise.
305
+ */
306
+ bool peek (T &data) const
307
+ {
308
+ bool data_updated = false ;
309
+ core_util_critical_section_enter ();
310
+ if (!empty ()) {
311
+ data = _buffer[_tail];
312
+ data_updated = true ;
313
+ }
314
+ core_util_critical_section_exit ();
315
+ return data_updated;
316
+ }
317
+
318
+ private:
319
+ bool non_critical_empty () const
320
+ {
321
+ bool is_empty = (_head == _tail) && !_full;
322
+ return is_empty;
323
+ }
324
+
325
+ CounterType non_critical_size () const
326
+ {
172
327
CounterType elements;
173
328
if (!_full) {
174
329
if (_head < _tail) {
@@ -179,29 +334,30 @@ class CircularBuffer {
179
334
} else {
180
335
elements = BufferSize;
181
336
}
182
- core_util_critical_section_exit ();
183
337
return elements;
184
338
}
185
339
186
- /* * Peek into circular buffer without popping
340
+ /* * Used to increment _tail or _head by a given value.
187
341
*
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
342
+ * @param val The value of the counter to be incremented.
343
+ * @param increment The amount to be added, the value after this incremented must not exceed BufferSize.
344
+ * @return The new value of the counter.
190
345
*/
191
- bool peek (T &data) const
346
+ CounterType incrementCounter (CounterType val, CounterType increment = 1 )
192
347
{
193
- bool data_updated = false ;
194
- core_util_critical_section_enter ();
195
- if (!empty ()) {
196
- data = _pool[_tail];
197
- data_updated = true ;
348
+ val += increment;
349
+
350
+ MBED_ASSERT (val <= BufferSize);
351
+
352
+ if (val == BufferSize) {
353
+ val = 0 ;
198
354
}
199
- core_util_critical_section_exit ();
200
- return data_updated ;
355
+
356
+ return val ;
201
357
}
202
358
203
359
private:
204
- T _pool [BufferSize];
360
+ T _buffer [BufferSize];
205
361
CounterType _head;
206
362
CounterType _tail;
207
363
bool _full;
0 commit comments