Mistake on this page?
Report an issue in GitHub or email us
CircularBuffer.h
1 /* mbed Microcontroller Library
2  * Copyright (c) 2015-2019 ARM Limited
3  * SPDX-License-Identifier: Apache-2.0
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #ifndef MBED_CIRCULARBUFFER_H
18 #define MBED_CIRCULARBUFFER_H
19 
20 #include <stdint.h>
21 #include "platform/mbed_critical.h"
22 #include "platform/mbed_assert.h"
23 #include "platform/Span.h"
24 #include "platform/mbed_atomic.h"
25 
26 namespace mbed {
27 
28 namespace internal {
29 /* Detect if CounterType of the Circular buffer is of unsigned type. */
30 template<typename T>
31 struct is_unsigned {
32  static const bool value = false;
33 };
34 template<>
35 struct is_unsigned<unsigned char> {
36  static const bool value = true;
37 };
38 template<>
39 struct is_unsigned<unsigned short> {
40  static const bool value = true;
41 };
42 template<>
43 struct is_unsigned<unsigned int> {
44  static const bool value = true;
45 };
46 template<>
47 struct is_unsigned<unsigned long> {
48  static const bool value = true;
49 };
50 template<>
51 struct is_unsigned<unsigned long long> {
52  static const bool value = true;
53 };
54 }
55 
56 /** \addtogroup platform-public-api */
57 /** @{*/
58 /**
59  * \defgroup platform_CircularBuffer CircularBuffer functions
60  * @{
61  */
62 
63 /** Templated Circular buffer class
64  *
65  * @note Synchronization level: Interrupt safe.
66  * @note CounterType must be unsigned and consistent with BufferSize.
67  */
68 template<typename T, uint32_t BufferSize, typename CounterType = uint32_t>
70 public:
71  CircularBuffer() : _head(0), _tail(0), _full(false)
72  {
73  static_assert(
75  "CounterType must be unsigned"
76  );
77 
78  static_assert(
79  (sizeof(CounterType) >= sizeof(uint32_t)) ||
80  (BufferSize < (((uint64_t) 1) << (sizeof(CounterType) * 8))),
81  "Invalid BufferSize for the CounterType"
82  );
83  }
84 
86  {
87  }
88 
89  /** Push the transaction to the buffer. This overwrites the buffer if it's full.
90  *
91  * @param data Data to be pushed to the buffer.
92  */
93  void push(const T &data)
94  {
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;
105  }
106 
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 
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;
124  _head = 0;
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  }
153  }
154 
156  }
157 
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  */
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)
173  {
174  bool data_popped = false;
175 
177 
178  if (!non_critical_empty()) {
179  data_popped = true;
180 
181  data = _buffer[_tail];
182  _tail = incrementCounter(_tail);
183  _full = false;
184  }
185 
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 
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;
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 
236  _full = false;
237  }
238 
240 
241  return data_popped;
242  }
243 
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  */
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.
258  *
259  * @return True if the buffer is empty, false if not.
260  */
261  bool empty() const
262  {
264  bool is_empty = non_critical_empty();
266  return is_empty;
267  }
268 
269  /** Check if the buffer is full.
270  *
271  * @return True if the buffer is full, false if not
272  */
273  bool full() const
274  {
275  return core_util_atomic_load_bool(&_full);
276  }
277 
278  /**
279  * Reset the buffer.
280  */
281  void reset()
282  {
284  _head = 0;
285  _tail = 0;
286  _full = false;
288  }
289 
290  /**
291  * Get the number of elements currently stored in the circular_buffer.
292  */
293  CounterType size() const
294  {
296  CounterType elements = non_critical_size();
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;
310  if (!empty()) {
311  data = _buffer[_tail];
312  data_updated = true;
313  }
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  {
327  CounterType elements;
328  if (!_full) {
329  if (_head < _tail) {
330  elements = BufferSize + _head - _tail;
331  } else {
332  elements = _head - _tail;
333  }
334  } else {
335  elements = BufferSize;
336  }
337  return elements;
338  }
339 
340  /** Used to increment _tail or _head by a given value.
341  *
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.
345  */
346  CounterType incrementCounter(CounterType val, CounterType increment = 1)
347  {
348  val += increment;
349 
350  MBED_ASSERT(val <= BufferSize);
351 
352  if (val == BufferSize) {
353  val = 0;
354  }
355 
356  return val;
357  }
358 
359 private:
360  T _buffer[BufferSize];
361  CounterType _head;
362  CounterType _tail;
363  bool _full;
364 };
365 
366 /**@}*/
367 
368 /**@}*/
369 
370 }
371 
372 #endif
bool full() const
Check if the buffer is full.
index_type size() const
Return the size of the sequence viewed.
Definition: Span.h:348
Templated Circular buffer class.
void push(const T *src, CounterType len)
Push the transaction to the buffer.
Span< T, Extent > make_Span(T *elements)
Generate a Span from a pointer to a C/C++ array.
Definition: Span.h:1034
void core_util_critical_section_exit(void)
Mark the end of a critical section.
mbed::Span< T > pop(mbed::Span< T > dest)
Pop multiple elements from the buffer.
void push(mbed::Span< const T > src)
Push the transaction to the buffer.
void push(const T &data)
Push the transaction to the buffer.
CounterType size() const
Get the number of elements currently stored in the circular_buffer.
MBED_FORCEINLINE bool core_util_atomic_load_bool(const volatile bool *valuePtr)
Atomic load.
Nonowning view to a sequence of contiguous elements.
Definition: Span.h:215
void core_util_critical_section_enter(void)
Mark the start of a critical section.
#define MBED_ASSERT(expr)
MBED_ASSERT Declare runtime assertions: results in runtime error if condition is false.
Definition: mbed_assert.h:66
bool pop(T &data)
Pop from the buffer.
CounterType pop(T *dest, CounterType len)
Pop multiple elements from the buffer.
pointer data() const
Return a pointer to the first element of the sequence or NULL if the Span is empty().
Definition: Span.h:426
bool peek(T &data) const
Peek into circular buffer without popping.
Definition: ATHandler.h:46
bool empty() const
Check if the buffer is empty.
void reset()
Reset the buffer.
Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.