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,195 @@ 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+ 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 ;
103122 _head = 0 ;
104- }
105- if (_head == _tail) {
106123 _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+ }
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.
112159 *
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.
115161 */
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)
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+ 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 ();
124213 }
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;
125234 _full = false ;
126- data_popped = true ;
127235 }
236+
128237 core_util_critical_section_exit ();
238+
129239 return data_popped;
130240 }
131241
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.
133246 *
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.
135258 */
136259 bool empty () const
137260 {
138261 core_util_critical_section_enter ();
139- bool is_empty = (_head == _tail) && !_full ;
262+ bool is_empty = non_critical_empty () ;
140263 core_util_critical_section_exit ();
141264 return is_empty;
142265 }
143266
144- /* * Check if the buffer is full
267+ /* * Check if the buffer is full.
145268 *
146269 * @return True if the buffer is full, false if not
147270 */
148271 bool full () const
149272 {
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);
154274 }
155275
156- /* * Reset the buffer
157- *
276+ /* *
277+ * Reset the buffer.
158278 */
159279 void reset ()
160280 {
@@ -165,43 +285,63 @@ class CircularBuffer {
165285 core_util_critical_section_exit ();
166286 }
167287
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+ */
169291 CounterType size () const
170292 {
171293 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 ();
182295 core_util_critical_section_exit ();
183296 return elements;
184297 }
185298
186- /* * Peek into circular buffer without popping
299+ /* * Peek into circular buffer without popping.
187300 *
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.
190303 */
191304 bool peek (T &data) const
192305 {
193306 bool data_updated = false ;
194307 core_util_critical_section_enter ();
195308 if (!empty ()) {
196- data = _pool [_tail];
309+ data = _buffer [_tail];
197310 data_updated = true ;
198311 }
199312 core_util_critical_section_exit ();
200313 return data_updated;
201314 }
202315
203316private:
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];
205345 CounterType _head;
206346 CounterType _tail;
207347 bool _full;
0 commit comments