2020#include < stdint.h>
2121#include " platform/mbed_critical.h"
2222#include " platform/mbed_assert.h"
23+ #include " platform/Span.h"
24+ #include " platform/mbed_atomic.h"
2325
2426namespace mbed {
2527
@@ -60,8 +62,8 @@ struct is_unsigned<unsigned long long> {
6062
6163/* * Templated Circular buffer class
6264 *
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.
6567 */
6668template <typename T, uint32_t BufferSize, typename CounterType = uint32_t >
6769class CircularBuffer {
@@ -84,77 +86,197 @@ class CircularBuffer {
8486 {
8587 }
8688
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.
8990 *
90- * @param data Data to be pushed to the buffer
91+ * @param data Data to be pushed to the buffer.
9192 */
9293 void push (const T &data)
9394 {
9495 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 ;
100105 }
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 ;
103124 _head = 0 ;
104- }
105- if (_head == _tail) {
106125 _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+ }
107153 }
154+
108155 core_util_critical_section_exit ();
109156 }
110157
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.
112168 *
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.
115171 */
116172 bool pop (T &data)
117173 {
118174 bool data_popped = false ;
175+
119176 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;
124221 }
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+
125236 _full = false ;
126- data_popped = true ;
127237 }
238+
128239 core_util_critical_section_exit ();
240+
129241 return data_popped;
130242 }
131243
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.
133258 *
134- * @return True if the buffer is empty, false if not
259+ * @return True if the buffer is empty, false if not.
135260 */
136261 bool empty () const
137262 {
138263 core_util_critical_section_enter ();
139- bool is_empty = (_head == _tail) && !_full ;
264+ bool is_empty = non_critical_empty () ;
140265 core_util_critical_section_exit ();
141266 return is_empty;
142267 }
143268
144- /* * Check if the buffer is full
269+ /* * Check if the buffer is full.
145270 *
146271 * @return True if the buffer is full, false if not
147272 */
148273 bool full () const
149274 {
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);
154276 }
155277
156- /* * Reset the buffer
157- *
278+ /* *
279+ * Reset the buffer.
158280 */
159281 void reset ()
160282 {
@@ -165,10 +287,43 @@ class CircularBuffer {
165287 core_util_critical_section_exit ();
166288 }
167289
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+ */
169293 CounterType size () const
170294 {
171295 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+ {
172327 CounterType elements;
173328 if (!_full) {
174329 if (_head < _tail) {
@@ -179,29 +334,30 @@ class CircularBuffer {
179334 } else {
180335 elements = BufferSize;
181336 }
182- core_util_critical_section_exit ();
183337 return elements;
184338 }
185339
186- /* * Peek into circular buffer without popping
340+ /* * Used to increment _tail or _head by a given value.
187341 *
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.
190345 */
191- bool peek (T &data) const
346+ CounterType incrementCounter (CounterType val, CounterType increment = 1 )
192347 {
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 ;
198354 }
199- core_util_critical_section_exit ();
200- return data_updated ;
355+
356+ return val ;
201357 }
202358
203359private:
204- T _pool [BufferSize];
360+ T _buffer [BufferSize];
205361 CounterType _head;
206362 CounterType _tail;
207363 bool _full;
0 commit comments