M5Utility 0.0.6 git rev:8f9da9c
Loading...
Searching...
No Matches
circular_buffer.hpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
3 *
4 * SPDX-License-Identifier: MIT
5 */
10#ifndef M5_UTILITY_CONTAINER_CIRCULAR_BUFFER_HPP
11#define M5_UTILITY_CONTAINER_CIRCULAR_BUFFER_HPP
12
13#include <cstddef>
14#include <vector>
15#include <iterator>
16#include <cassert>
17#if __cplusplus >= 201703L
18#include <optional>
19#else
20#include "../stl/optional.hpp"
21#endif
22
23namespace m5 {
24namespace container {
25
31template <typename T>
33public:
34 using value_type = T;
35 using size_type = size_t;
36 using reference = T&;
37 using const_reference = const T&;
38#if __cplusplus >= 201703L
39 using return_type = std::optional<value_type>;
40#else
42#endif
43 class iterator;
44 class const_iterator;
45 using reverse_iterator = std::reverse_iterator<iterator>;
46 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
47
50 CircularBuffer() = delete;
51 explicit CircularBuffer(const size_t n)
52 {
53 assert(n != 0 && "Illegal size");
54 _cap = n;
55 _buf.resize(n);
56 }
57 CircularBuffer(const size_type n, const_reference value) : CircularBuffer(n)
58 {
59 assign(n, value);
60 }
61 template <class InputIter>
62 CircularBuffer(const size_type n, InputIter first, InputIter last) : CircularBuffer(n)
63 {
64 assign(first, last);
65 }
66 CircularBuffer(const size_type n, std::initializer_list<T> il) : CircularBuffer(n, il.begin(), il.end())
67 {
68 }
69
70 CircularBuffer(const CircularBuffer&) = default;
71
72 CircularBuffer(CircularBuffer&&) noexcept = default;
74
77
86 template <class InputIterator>
87 void assign(InputIterator first, InputIterator last)
88 {
89 clear();
90 size_type sz = last - first;
91 if (sz > _cap) {
92 first += (sz - _cap);
93 }
94 auto n = std::min(_cap, sz);
95 while (n--) {
96 push_back(*first++);
97 }
98 }
105 void assign(size_type n, const_reference v)
106 {
107 clear();
108 n = std::min(_cap, n);
109 while (n--) {
110 push_back(v);
111 }
112 }
117 inline void assign(std::initializer_list<T> il)
118 {
119 assign(il.begin(), il.end());
120 }
122
125
129 inline return_type front() const
130 {
131#if __cplusplus >= 201703L
132 return !empty() ? std::make_optional(_buf[_tail]) : std::nullopt;
133#else
134 return !empty() ? m5::stl::make_optional(_buf[_tail]) : m5::stl::nullopt;
135#endif
136 }
141 inline return_type back() const
142 {
143#if __cplusplus >= 201703L
144 return !empty() ? std::make_optional(_buf[(_head - 1 + _cap) % _cap]) : std::nullopt;
145#else
146 return !empty() ? m5::stl::make_optional(_buf[(_head - 1 + _cap) % _cap]) : m5::stl::nullopt;
147#endif
148 }
153 inline const_reference operator[](size_type i) const&
154 {
155 assert(size() > 0 && "container empty");
156 assert(i < size() && "index overflow");
157 return _buf[(_tail + i) % _cap];
158 }
163 inline return_type at(size_type i) const
164 {
165#if __cplusplus >= 201703L
166 return (!empty() && i < size()) ? std::make_optional(_buf[(_tail + i) % _cap]) : std::nullopt;
167#else
168 return (!empty() && i < size()) ? m5::stl::make_optional(_buf[(_tail + i) % _cap]) : m5::stl::nullopt;
169#endif
170 }
177 size_t read(value_type* outbuf, const size_t num)
178 {
179 size_t sz = std::min(num, size());
180 if (sz == 0) {
181 return sz;
182 }
183 auto tail = _tail;
184 auto src = &_buf[tail];
185 size_t elms = std::min(_cap - tail, sz);
186
187 std::copy(src, src + elms, outbuf);
188 tail = (tail + elms) % _cap;
189 size_t ret = elms;
190
191 if (elms < sz) {
192 outbuf += elms;
193 src = &_buf[tail];
194 elms = sz - elms;
195
196 std::copy(src, src + elms, outbuf);
197 ret += elms;
198 }
199 return ret;
200 }
202
205
209 inline bool empty() const
210 {
211 return !full() && (_head == _tail);
212 }
217 inline bool full() const
218 {
219 return _full;
220 }
224 inline size_type size() const
225 {
226 return full() ? _cap : (_head >= _tail ? _head - _tail : _cap + _head - _tail);
227 }
232 inline size_type capacity() const
233 {
234 return _cap;
235 }
237
240
241 void clear()
242 {
243 _full = false;
244 _head = _tail = 0U;
245 }
247 void push_front(const value_type& v)
248 {
249 _tail = (_tail - 1 + _cap) % _cap;
250 _buf[_tail] = v;
251 if (_full) {
252 _head = (_head - 1 + _cap) % _cap;
253 }
254 _full = (_head == _tail);
255 }
257 void push_back(const value_type& v)
258 {
259 _buf[_head] = v;
260 _head = (_head + 1) % _cap;
261 if (_full) {
262 _tail = (_tail + 1) % _cap;
263 }
264 _full = (_head == _tail);
265 }
267 inline void pop_front()
268 {
269 if (!empty()) {
270 _tail = (_tail + 1) % _cap;
271 _full = false;
272 }
273 }
275 inline void pop_back()
276 {
277 if (!empty()) {
278 _head = (_head - 1 + _cap) % _cap;
279 _full = false;
280 }
281 }
283
286
290 void fill(const value_type& v)
291 {
292 clear();
293 std::fill(_buf.begin(), _buf.end(), v);
294 _full = true;
295 }
301 {
302 if (this != &o) {
303 std::swap(_buf, o._buf);
304 std::swap(_cap, o._cap);
305 std::swap(_head, o._head);
306 std::swap(_tail, o._tail);
307 std::swap(_full, o._full);
308 }
309 }
311
313 class iterator {
314 public:
315 using iterator_category = std::bidirectional_iterator_tag;
316 using difference_type = std::ptrdiff_t;
317 using value_type = CircularBuffer::value_type;
318 using pointer = CircularBuffer::value_type*;
319 using reference = CircularBuffer::reference;
320
321 iterator() : _buffer(nullptr), _pos(0)
322 {
323 }
324 iterator(CircularBuffer* buf, size_t pos) : _buffer(buf), _pos(pos)
325 {
326 }
327
328 inline reference operator*() const
329 {
330 return _buffer->_buf[_pos % _buffer->capacity()];
331 }
332 inline pointer operator->() const
333 {
334 return &(_buffer->_buf[_pos % _buffer->capacity()]);
335 }
336 inline iterator& operator++()
337 {
338 ++_pos;
339 return *this;
340 }
341 inline iterator& operator--()
342 {
343 --_pos;
344 return *this;
345 }
346 inline iterator operator++(int)
347 {
348 iterator tmp = *this;
349 ++(*this);
350 return tmp;
351 }
352 inline iterator operator--(int)
353 {
354 iterator tmp = *this;
355 --(*this);
356 return tmp;
357 }
358
359 friend inline bool operator==(const iterator& a, const iterator& b)
360 {
361 return a._buffer == b._buffer && a._pos == b._pos;
362 }
363 friend inline bool operator!=(const iterator& a, const iterator& b)
364 {
365 return !(a == b);
366 }
367
368 private:
369 CircularBuffer* _buffer;
370 size_t _pos;
371 };
372
373 class const_iterator {
374 public:
375 using iterator_category = std::bidirectional_iterator_tag;
376 using difference_type = std::ptrdiff_t;
377 using value_type = CircularBuffer::value_type;
378 using pointer = const CircularBuffer::value_type*;
379 using reference = CircularBuffer::const_reference;
380
381 const_iterator() : _buffer(nullptr), _pos(0)
382 {
383 }
384 const_iterator(const CircularBuffer* buf, size_t pos) : _buffer(buf), _pos(pos)
385 {
386 }
387
388 inline reference operator*() const
389 {
390 return _buffer->_buf[_pos % _buffer->capacity()];
391 }
392 inline pointer operator->() const
393 {
394 return &(_buffer->_buf[_pos % _buffer->capacity()]);
395 }
396 inline const_iterator& operator++()
397 {
398 ++_pos;
399 return *this;
400 }
401 inline const_iterator& operator--()
402 {
403 --_pos;
404 return *this;
405 }
406 inline const_iterator operator++(int)
407 {
408 const_iterator tmp = *this;
409 ++(*this);
410 return tmp;
411 }
412 inline const_iterator operator--(int)
413 {
414 const_iterator tmp = *this;
415 --(*this);
416 return tmp;
417 }
418
419 friend inline bool operator==(const const_iterator& a, const const_iterator& b)
420 {
421 return a._buffer == b._buffer && a._pos == b._pos;
422 }
423 friend inline bool operator!=(const const_iterator& a, const const_iterator& b)
424 {
425 return !(a == b);
426 }
427
428 private:
429 const CircularBuffer* _buffer;
430 size_t _pos;
431 };
433
437 inline iterator begin() noexcept
438 {
439 return iterator(this, _tail);
440 }
441 inline iterator end() noexcept
442 {
443 return iterator(this, _tail + size());
444 }
445 inline const_iterator cbegin() const noexcept
446 {
447 return const_iterator(this, _tail);
448 }
449 inline const_iterator cend() const noexcept
450 {
451 return const_iterator(this, _tail + size());
452 }
453 inline reverse_iterator rbegin() noexcept
454 {
455 return std::reverse_iterator<iterator>(end());
456 }
457 inline reverse_iterator rend() noexcept
458 {
459 return std::reverse_iterator<iterator>(begin());
460 }
461 inline const_reverse_iterator crbegin() const noexcept
462 {
463 return std::reverse_iterator<const_iterator>(cend());
464 }
465 inline const_reverse_iterator crend() const noexcept
466 {
467 return std::reverse_iterator<const_iterator>(cbegin());
468 }
470
471private:
472 std::vector<T> _buf{};
473 size_t _cap{}, _head{}, _tail{};
474 bool _full{};
475};
476
483template <typename T, size_t N>
485public:
486 using value_type = T;
487 using size_type = size_t;
488 using reference = T&;
489 using const_reference = const T&;
490#if __cplusplus >= 201703L
491 using return_type = std::optional<value_type>;
492#else
494#endif
495
497 {
498 }
499 FixedCircularBuffer(const size_type n, const_reference value) : CircularBuffer<T>(N)
500 {
502 }
503 template <class InputIter>
504 FixedCircularBuffer(InputIter first, InputIter last) : CircularBuffer<T>(N, first, last)
505 {
506 }
507 FixedCircularBuffer(std::initializer_list<T> il) : CircularBuffer<T>(N, il)
508 {
509 }
510
513
514 FixedCircularBuffer& operator=(const FixedCircularBuffer&) = default;
515
516 FixedCircularBuffer& operator=(FixedCircularBuffer&&) noexcept = default;
517};
518
519} // namespace container
520} // namespace m5
521
522namespace std {
528template <typename T>
533} // namespace std
534
535#endif
Type CircularBuffer giving size in constructor.
Definition circular_buffer.hpp:32
void clear()
Clears the contents.
Definition circular_buffer.hpp:241
void push_front(const value_type &v)
Adds an element to the top.
Definition circular_buffer.hpp:247
void assign(std::initializer_list< T > il)
assigns values to the container
Definition circular_buffer.hpp:117
bool empty() const
checks whether the container is empty
Definition circular_buffer.hpp:209
void assign(size_type n, const_reference v)
assigns values to the container
Definition circular_buffer.hpp:105
void swap(CircularBuffer &o)
Swaps the contents.
Definition circular_buffer.hpp:300
void push_back(const value_type &v)
Adds an element to the end.
Definition circular_buffer.hpp:257
return_type front() const
Access the first element.
Definition circular_buffer.hpp:129
return_type at(size_type i) const
Access specified element with bounds checking.
Definition circular_buffer.hpp:163
return_type back() const
Access the last element.
Definition circular_buffer.hpp:141
const_reference operator[](size_type i) const &
Access specified element.
Definition circular_buffer.hpp:153
CircularBuffer & operator=(CircularBuffer &&)=default
Move.
CircularBuffer & operator=(const CircularBuffer &)=default
Copy.
void swap(m5::container::CircularBuffer< T > &a, m5::container::CircularBuffer< T > &b)
Specializes the std::swap algorithm.
Definition circular_buffer.hpp:529
void fill(const value_type &v)
Assigns the value to all elements in the container.
Definition circular_buffer.hpp:290
void pop_front()
removes the top element
Definition circular_buffer.hpp:267
void pop_back()
removes the end element
Definition circular_buffer.hpp:275
bool full() const
checks whether the container is full
Definition circular_buffer.hpp:217
void assign(InputIterator first, InputIterator last)
Replaces the contents with copies of those in the range [first, last)
Definition circular_buffer.hpp:87
size_type size() const
returns the number of elements
Definition circular_buffer.hpp:224
size_t read(value_type *outbuf, const size_t num)
Read from buffer.
Definition circular_buffer.hpp:177
size_type capacity() const
Returns the number of elements that can be held in currently storage.
Definition circular_buffer.hpp:232
Type CircularBuffer giving size in template parameter.
Definition circular_buffer.hpp:484
Definition optional.hpp:679
Container classes.
Top level namespace of M5.
Definition bit_segment.hpp:17