MLIB
Loading...
Searching...
No Matches
ringbuf.h
Go to the documentation of this file.
1/*
2 Copyright (c) Mircea Neacsu (2014-2025) Licensed under MIT License.
3 This file is part of MLIB project. See LICENSE file for full license terms.
4*/
5
7
8#pragma once
9
10#include <memory>
11#include <vector>
12#include <stdexcept>
13#include <assert.h>
14
15#if __has_include("defs.h")
16#include "defs.h"
17#endif
18
19namespace mlib {
20
22template <class T>
24{
25public:
28 {
29 public:
30 // Following typedefs must exist to allow instantiation of std::iterator_traits
31 using difference_type = std::ptrdiff_t;
32 using value_type = T;
33 using reference = T const&;
34 using pointer = T const*;
35 using iterator_category = std::bidirectional_iterator_tag;
36
39 : ring (nullptr)
40 , pos (0)
41 {}
42
44 reference operator* ()
45 {
46 if (pos == (size_t)(-1))
47 throw std::range_error ("Ring buffer iterator at end!");
48 return ring->buf[pos];
49 }
50
52 const reference operator* () const
53 {
54 if (pos == (size_t)(-1))
55 throw std::range_error ("Ring buffer iterator at end!");
56 return ring->buf[pos];
57 }
58
60 pointer operator->()
61 {
62 if (pos == (size_t)(-1))
63 throw std::range_error ("Ring buffer iterator at end!");
64 return &ring->buf[pos];
65 }
66
68 const pointer operator->() const
69 {
70 if (pos == (size_t)(-1))
71 throw std::range_error ("Ring buffer iterator at end!");
72 return &ring->buf[pos];
73 }
74
77 {
78 const_iterator tmp = *this;
79 pos = ring->increment (pos);
80 return tmp;
81 }
82
85 {
86 pos = ring->increment (pos);
87 return *this;
88 }
89
92 {
93 const_iterator tmp = *this;
94 pos = ring->decrement (pos);
95 return tmp;
96 }
97
100 {
101 pos = ring->decrement (pos);
102 return *this;
103 }
104
106 bool operator== (const const_iterator& it) const
107 {
108 return (ring == it.ring) && (pos == it.pos);
109 }
110
112 bool operator!= (const const_iterator& it) const
113 {
114 return !operator== (it);
115 }
116
119 {
120 if (this != &rhs)
121 {
122 ring = rhs.ring;
123 pos = rhs.pos;
124 }
125 return *this;
126 }
127
129 const_iterator operator+ (size_t inc) const
130 {
131 const_iterator tmp = *this;
132 tmp.pos = ring->add (pos, inc);
133 return tmp;
134 }
135
138 {
139 pos = ring->add (pos, inc);
140 return *this;
141 }
142
144 const_iterator operator- (size_t dec) const
145 {
146 const_iterator tmp = *this;
147 tmp.pos = ring->subtract (pos, dec);
148 return tmp;
149 }
150
153 {
154 pos = ring->subtract (pos, dec);
155 return *this;
156 }
157
159 ptrdiff_t operator- (const const_iterator& other) const
160 {
161 assert (ring == other.ring);
162 size_t p1 = (pos != -1) ? pos
163 : (ring->back_idx == ring->front_idx) ? ring->sz
164 : ring->back_idx;
165 size_t p2 = (other.pos != -1) ? other.pos
166 : (ring->back_idx == ring->front_idx) ? ring->sz
167 : ring->back_idx;
168 if (p1 >= p2)
169 return p1 - p2;
170 else
171 return ring->cap - p2 + p1;
172 }
173
174 protected:
175 const_iterator (const ring_buffer* ring_, size_t pos_)
176 : ring (ring_)
177 , pos (ring->sz?pos_: -1)
178 {
179 }
180
181 const ring_buffer* ring;
182 size_t pos;
183 friend class ring_buffer;
184 };
185
186 class iterator final : public const_iterator
187 {
188 public:
189 // Following typedefs must exist to allow instantiation of std::iterator_traits
190 using difference_type = std::ptrdiff_t;
191 using value_type = T;
192 using reference = T&;
193 using pointer = T*;
194 using iterator_category = std::bidirectional_iterator_tag;
195 using my_base = const_iterator;
196
199 : const_iterator ()
200 {}
201
202 iterator (const const_iterator& ci)
203 : const_iterator (ci)
204 {}
205
207 reference operator* ()
208 {
209 return const_cast<reference> (my_base::operator* ());
210 }
211
213 pointer operator->()
214 {
215 return const_cast<pointer> (my_base::operator-> ());
216 }
217
220 {
221 return my_base::operator++ (x);
222 }
223
226 {
228 return *this;
229 }
230
233 {
234 return my_base::operator-- (x);
235 }
236
239 {
241 return *this;
242 }
243
246 {
248 return *this;
249 }
250
252 iterator operator+ (size_t inc) const
253 {
254 return my_base::operator+ (inc);
255 }
256
259 {
261 return *this;
262 }
263
265 iterator operator- (size_t dec) const
266 {
267 return my_base::operator- (dec);
268 }
269
272 {
274 return *this;
275 }
276
278 ptrdiff_t operator- (const const_iterator& other) const
279 {
280 return my_base::operator- (other);
281 }
282
283 private:
284 iterator (const ring_buffer* ring_, size_t pos_)
285 : const_iterator (ring_, pos_)
286 {}
287
288 friend class ring_buffer;
289 };
290
293 : buf (std::unique_ptr<T[]> (new T[size]))
294 , cap (size)
295 , front_idx (0)
296 , back_idx (0)
297 , sz (0)
298 {}
299
302 : cap (0)
303 , front_idx (0)
304 , back_idx (0)
305 , sz (0)
306 {}
307
310 : front_idx (other.front_idx)
311 , back_idx (other.back_idx)
312 , cap (other.cap)
313 , sz (other.sz)
314 {
315 if (other.cap)
316 {
317 buf = std::unique_ptr<T[]> (new T[other.cap]);
318 for (size_t i = 0; i < sz; i++)
319 {
320 size_t idx = (front_idx + i) % cap;
321 buf[idx] = other.buf[idx];
322 }
323 }
324 }
325
327 ring_buffer (std::initializer_list<T> il)
328 : buf (std::unique_ptr<T[]> (new T[il.size ()]))
329 , cap (il.size ())
330 , front_idx (0)
331 , back_idx (0)
332 , sz (il.size ())
333 {
334 size_t i = 0;
335 for (const auto v : il)
336 buf[i++] = std::move (v);
337 }
338
340 ring_buffer& operator= (const ring_buffer& rhs)
341 {
342 if (&rhs != this)
343 {
344 front_idx = rhs.front_idx;
345 back_idx = rhs.back_idx;
346 cap = rhs.cap;
347 sz = rhs.sz;
348 if (rhs.cap)
349 {
350 buf.reset (new T[rhs.cap]);
351 for (size_t i = 0; i < sz; i++)
352 {
353 size_t idx = (front_idx + i) % cap;
354 buf[idx] = rhs.buf[idx];
355 }
356 }
357 else
358 buf.reset ();
359 }
360 return *this;
361 }
362
364 bool operator== (const ring_buffer<T>& other) const
365 {
366 if (cap == other.cap && sz == other.sz)
367 {
368 for (size_t i = 0; i < sz; i++)
369 {
370 size_t idx1 = (front_idx + i) % cap;
371 size_t idx2 = (other.front_idx + i) % other.cap;
372 if (buf[idx1] != other.buf[idx2])
373 return false;
374 }
375 return true;
376 }
377 return false;
378 }
379
381 bool operator!= (const ring_buffer<T>& other) const
382 {
383 return !operator== (other);
384 }
385
387 operator std::vector<T> () const
388 {
389 std::vector<T> v (sz);
390 for (size_t i = 0; i < sz; i++)
391 v[i] = buf[(front_idx + i) % cap];
392
393 return v;
394 }
395
397 void push_back (const T& item)
398 {
399 if (!cap)
400 return; // container not allocated
401
402 if (sz && back_idx == front_idx)
403 front_idx = (front_idx + 1) % cap;
404 else
405 sz++;
406 buf[back_idx] = item;
407 back_idx = (back_idx + 1) % cap;
408 }
409
412 {
413 return iterator (this, front_idx);
414 }
415
418 {
419 return const_iterator (this, front_idx);
420 }
421
424 {
425 return const_iterator (this, front_idx);
426 }
427
430 {
431 return iterator (this, (size_t)(-1));
432 }
433
436 {
437 return const_iterator (this, (size_t)(-1));
438 }
439
442 {
443 return const_iterator (this, (size_t)(-1));
444 }
445
447 void pop_front ()
448 {
449 #ifdef _DEBUG
450 if (!sz)
451 throw std::range_error ("ring_buffer::pop_front - empty container");
452 #endif
453 front_idx = (front_idx + 1) % cap;
454 sz--;
455 }
456
458 T& front ()
459 {
460#ifdef _DEBUG
461 if (!sz)
462 throw std::range_error ("ring_buffer::front - empty container");
463#endif
464 return buf[front_idx];
465 }
466
468 const T& front () const
469 {
470#ifdef _DEBUG
471 if (!sz)
472 throw std::range_error ("ring_buffer::front - empty container");
473#endif
474 return buf[front_idx];
475 }
476
478 T& back ()
479 {
480#ifdef _DEBUG
481 if (!sz)
482 throw std::range_error ("ring_buffer::back - empty container");
483#endif
484 return buf[(back_idx + cap - 1) % cap];
485 }
486
488 const T& back () const
489 {
490#ifdef _DEBUG
491 if (!sz)
492 throw std::range_error ("ring_buffer::back - empty container");
493#endif
494 return buf[(back_idx + cap - 1) % cap];
495 }
496
500 {
501#ifdef _DEBUG
502 if (ptr.ring != this)
503 throw std::runtime_error ("ring_buffer<>::erase - Invalid iterator");
504#endif
505 if (sz == 0)
506 return end (); //erasing from an empty buffer is a no-op
507 size_t pos = ptr.pos == (size_t)-1 ? back_idx : ptr.pos;
508#ifdef _DEBUG
509 if ((front_idx < back_idx && (pos < front_idx || pos >= back_idx))
510 || (front_idx > back_idx && (pos < back_idx || pos >= front_idx)))
511 throw std::range_error ("ring_buffer<>::erase - Bad iterator position");
512#endif
513 for (auto p = pos; p != front_idx; )
514 {
515 auto pp = decrement (p);
516 assert (pp != (size_t)-1);
517 buf[p] = buf[pp];
518 p = pp;
519 }
520 --sz;
521 if (pos == front_idx)
522 pos = increment (pos);
523 front_idx = (front_idx + 1) % cap;
524 return iterator(this, pos);
525 }
526
528 void clear (void)
529 {
530 front_idx = back_idx;
531 sz = 0;
532 }
533
535 bool empty (void)
536 {
537 return (sz == 0);
538 }
539
541 bool full (void) const
542 {
543 return (sz == cap);
544 }
545
547 size_t capacity (void) const
548 {
549 return cap;
550 };
551
553 void resize (size_t new_cap)
554 {
555 std::unique_ptr<T[]> newbuf (new_cap ? new T[new_cap] : nullptr);
556 if (new_cap)
557 {
558 if (sz >= new_cap)
559 sz = new_cap;
560 for (size_t i = 0; i < sz; i++)
561 newbuf[i] = buf[(front_idx + i) % cap];
562 }
563 else
564 sz = 0;
565 cap = new_cap;
566 buf.swap (newbuf);
567 front_idx = 0;
568 back_idx = cap ? (front_idx + sz) % cap : 0;
569 }
570
572 size_t size () const
573 {
574 return sz;
575 }
576
577private:
579 size_t increment (size_t pos) const
580 {
581 if (cap && pos != (size_t)-1)
582 pos = (pos + 1) % cap;
583 if (pos == back_idx)
584 pos = (size_t)-1;
585 return pos;
586 }
587
589 size_t decrement (size_t pos) const
590 {
591 if (cap)
592 {
593 if (pos == (size_t)-1)
594 pos = (back_idx + cap - 1) % cap;
595 else if (pos != front_idx)
596 pos = (pos + cap - 1) % cap;
597 }
598 return pos;
599 }
600
602 size_t add (size_t oldpos, size_t delta) const
603 {
604 if (cap && oldpos != -1)
605 {
606 size_t np = oldpos + (delta % cap);
607 if (np >= cap && np >= back_idx + cap)
608 np = (size_t)(-1);
609 else
610 np %= cap;
611 return np;
612 }
613 return oldpos;
614 }
615
617 size_t subtract (size_t oldpos, size_t delta) const
618 {
619 if (cap)
620 {
621 size_t np = (oldpos == (size_t)-1 ? back_idx : oldpos) - (delta % cap) + cap;
622 if (np < front_idx)
623 np = front_idx;
624 else
625 np %= cap;
626 return np;
627 }
628 return oldpos;
629 }
630
631 std::unique_ptr<T[]> buf; //the ring buffer
632 size_t front_idx; // start (oldest) element
633 size_t back_idx; // end (newest) element
634 size_t cap; // capacity
635 size_t sz; // size
636};
637
638} // namespace mlib
Iterator through the circular buffer.
Definition ringbuf.h:28
bool operator!=(const const_iterator &it) const
Inequality comparison.
Definition ringbuf.h:112
const_iterator()
Default constructor.
Definition ringbuf.h:38
const_iterator & operator+=(size_t inc)
Addition assignment operator.
Definition ringbuf.h:137
const_iterator & operator--()
Decrement operator (prefix)
Definition ringbuf.h:99
const_iterator & operator=(const const_iterator &rhs)
Assignment operator.
Definition ringbuf.h:118
reference operator*()
Dereference operator.
Definition ringbuf.h:44
const_iterator operator-(size_t dec) const
Subtraction operator.
Definition ringbuf.h:144
const pointer operator->() const
Object pointer (const version)
Definition ringbuf.h:68
pointer operator->()
Object pointer.
Definition ringbuf.h:60
const_iterator & operator-=(size_t dec)
Subtraction assignment operator.
Definition ringbuf.h:152
const_iterator operator+(size_t inc) const
Addition operator.
Definition ringbuf.h:129
bool operator==(const const_iterator &it) const
Equality comparison.
Definition ringbuf.h:106
const_iterator & operator++()
Increment operator (prefix)
Definition ringbuf.h:84
Definition ringbuf.h:187
iterator()
Default constructor.
Definition ringbuf.h:198
reference operator*()
Dereference operator.
Definition ringbuf.h:207
iterator & operator++()
Increment operator (prefix)
Definition ringbuf.h:225
iterator & operator--()
Decrement operator (prefix)
Definition ringbuf.h:238
iterator operator+(size_t inc) const
Addition operator.
Definition ringbuf.h:252
iterator & operator=(const const_iterator &rhs)
Assignment operator.
Definition ringbuf.h:245
iterator & operator-=(size_t dec)
Subtraction assignment operator.
Definition ringbuf.h:271
iterator operator-(size_t dec) const
Subtraction operator.
Definition ringbuf.h:265
iterator & operator+=(size_t inc)
Addition assignment operator.
Definition ringbuf.h:258
pointer operator->()
Object pointer.
Definition ringbuf.h:213
Circular buffer.
Definition ringbuf.h:24
void pop_front()
Remove oldest element from buffer.
Definition ringbuf.h:447
void clear(void)
Remove all elements from buffer.
Definition ringbuf.h:528
size_t size() const
Return number of elements in buffer.
Definition ringbuf.h:572
ring_buffer(const ring_buffer &other)
Copy constructor.
Definition ringbuf.h:309
T & front()
Return a reference to first (oldest) element in buffer.
Definition ringbuf.h:458
void push_back(const T &item)
Inserts new element in buffer.
Definition ringbuf.h:397
ring_buffer()
Default constructor.
Definition ringbuf.h:301
bool empty(void)
Return true if buffer is empty.
Definition ringbuf.h:535
const T & front() const
Return a reference to first (oldest) element in buffer.
Definition ringbuf.h:468
ring_buffer(std::initializer_list< T > il)
Initializer list constructor.
Definition ringbuf.h:327
bool full(void) const
Return true if buffer is full.
Definition ringbuf.h:541
const_iterator begin() const
Return a const iterator pointing to first (oldest) element in buffer.
Definition ringbuf.h:417
void resize(size_t new_cap)
(Re)allocate buffer with a different capacity
Definition ringbuf.h:553
size_t capacity(void) const
Return maximum buffer size.
Definition ringbuf.h:547
const T & back() const
Return reference to last (newest) element in buffer.
Definition ringbuf.h:488
const_iterator cbegin()
Return a const iterator pointing to first (oldest) element in buffer.
Definition ringbuf.h:423
iterator end()
Return an iterator pointing past the last (newest) element in buffer.
Definition ringbuf.h:429
T & back()
Return reference to last (newest) element in buffer.
Definition ringbuf.h:478
const_iterator end() const
Return a const iterator pointing past the last (newest) element in buffer.
Definition ringbuf.h:435
iterator erase(const_iterator &ptr)
Definition ringbuf.h:499
ring_buffer(size_t size)
Constructor.
Definition ringbuf.h:292
const_iterator cend()
Return an iterator pointing past the last (newest) element in buffer.
Definition ringbuf.h:441
iterator begin()
Return an iterator pointing to first (oldest) element in buffer.
Definition ringbuf.h:411
bool operator!=(const timeval &t1, const timeval &t2)
"Not equal" operator
Definition tvops.h:73
bool operator==(const timeval &t1, const timeval &t2)
Equality operator.
Definition tvops.h:31