Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_ring_buffer.H
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
58#ifndef TPL_RING_BUFFER_H
59#define TPL_RING_BUFFER_H
60
61#include <cstddef>
62#include <iterator>
63#include <memory>
64#include <type_traits>
65#include <utility>
66
67#include <ah-errors.H>
68
69namespace Aleph {
70
113template <typename T>
115{
116 static_assert(std::is_move_constructible_v<T>,
117 "RingBuffer requires a move-constructible element type");
118 static_assert(std::is_move_assignable_v<T>, "RingBuffer requires a move-assignable element type");
119
120 T *buf_ = nullptr;
121 size_t cap_ = 0;
122 size_t head_ = 0; // physical index of the oldest element
123 size_t n_ = 0;
124
125 static T *allocate(size_t m)
126 {
127 return std::allocator<T>().allocate(m);
128 }
129
130 static void deallocate(T *p, size_t m) noexcept
131 {
132 std::allocator<T>().deallocate(p, m);
133 }
134
135 // Physical index of the i-th logical element.
136 size_t phys(const size_t i) const noexcept
137 {
138 return (head_ + i) % cap_;
139 }
140
141 // Destroys all elements without releasing the storage.
143 {
144 for (size_t i = 0; i < n_; ++i)
145 std::destroy_at(buf_ + phys(i));
146 head_ = 0;
147 n_ = 0;
148 }
149
150public:
151 using Item_Type = T;
152 using value_type = T;
153 using size_type = size_t;
154
162 template <bool IsConst>
164 {
165 using BufPtr = std::conditional_t<IsConst, const RingBuffer *, RingBuffer *>;
166
167 BufPtr rb_ = nullptr;
168 size_t idx_ = 0; // logical index
169
171 static size_t negative_magnitude(const std::ptrdiff_t i) noexcept
172 {
173 return static_cast<size_t>(-(i + 1)) + 1;
174 }
175
177 static size_t add_offset(const size_t idx, const std::ptrdiff_t i) noexcept
178 {
179 return i < 0 ? idx - negative_magnitude(i) : idx + static_cast<size_t>(i);
180 }
181
183 static size_t sub_offset(const size_t idx, const std::ptrdiff_t i) noexcept
184 {
185 return i < 0 ? idx + negative_magnitude(i) : idx - static_cast<size_t>(i);
186 }
187
188 public:
189 using iterator_category = std::random_access_iterator_tag;
190 using value_type = T;
191 using difference_type = std::ptrdiff_t;
192 using reference = std::conditional_t<IsConst, const T &, T &>;
193 using pointer = std::conditional_t<IsConst, const T *, T *>;
194
196 basic_iterator() = default;
197
199 basic_iterator(BufPtr rb, const size_t idx) noexcept : rb_(rb), idx_(idx) {}
200
202 template <bool B> requires (IsConst and not B)
204 {}
205
208 {
209 return rb_;
210 }
211
214 {
215 return idx_;
216 }
217
220 {
221 return (*rb_)(idx_);
222 }
223
226 {
227 return &(*rb_)(idx_);
228 }
229
231 reference operator [] (const difference_type i) const noexcept
232 {
233 return (*rb_)(add_offset(idx_, i));
234 }
235
238 {
239 ++idx_;
240 return *this;
241 }
242
245 {
246 basic_iterator ret = *this;
247 ++idx_;
248 return ret;
249 }
250
253 {
254 --idx_;
255 return *this;
256 }
257
260 {
261 basic_iterator ret = *this;
262 --idx_;
263 return ret;
264 }
265
268 {
269 idx_ = add_offset(idx_, i);
270 return *this;
271 }
272
275 {
276 idx_ = sub_offset(idx_, i);
277 return *this;
278 }
279
282 {
283 basic_iterator ret = *this;
284 return ret += i;
285 }
286
289 {
290 basic_iterator ret = *this;
291 return ret -= i;
292 }
293
296 {
297 return static_cast<difference_type>(idx_) - static_cast<difference_type>(it.index());
298 }
299
301 bool operator == (const basic_iterator &it) const noexcept
302 {
303 return rb_ == it.buffer() and idx_ == it.index();
304 }
305
307 bool operator != (const basic_iterator &it) const noexcept
308 {
309 return not (*this == it);
310 }
311
313 bool operator < (const basic_iterator &it) const noexcept
314 {
315 return idx_ < it.index();
316 }
317
319 bool operator <= (const basic_iterator &it) const noexcept
320 {
321 return idx_ <= it.index();
322 }
323
325 bool operator > (const basic_iterator &it) const noexcept
326 {
327 return idx_ > it.index();
328 }
329
331 bool operator >= (const basic_iterator &it) const noexcept
332 {
333 return idx_ >= it.index();
334 }
335 };
336
339
345 explicit RingBuffer(const size_t cap) : cap_(cap)
346 {
347 ah_invalid_argument_if(cap == 0) << "RingBuffer: capacity must be positive";
348 buf_ = allocate(cap_);
349 }
350
354 requires std::is_copy_constructible_v<T>
355 : cap_(rb.cap_)
356 {
357 buf_ = allocate(cap_);
358 try
359 {
360 for (; n_ < rb.n_; ++n_)
361 ::new (static_cast<void *>(buf_ + n_)) T(rb(n_));
362 }
363 catch (...)
364 {
365 destroy_all();
367 throw;
368 }
369 }
370
375 {
376 swap(rb);
377 }
378
381 requires std::is_copy_constructible_v<T>
382 {
383 if (this == &rb)
384 return *this;
385 RingBuffer tmp(rb); // strong guarantee
386 swap(tmp);
387 return *this;
388 }
389
392 {
393 swap(rb);
394 return *this;
395 }
396
398 {
399 if (buf_ != nullptr)
400 {
401 destroy_all();
403 }
404 }
405
409 void swap(RingBuffer &rb) noexcept
410 {
411 std::swap(buf_, rb.buf_);
412 std::swap(cap_, rb.cap_);
413 std::swap(head_, rb.head_);
414 std::swap(n_, rb.n_);
415 }
416
417 // -- capacity -------------------------------------------------------------
418
421 {
422 return n_;
423 }
424
427 {
428 return cap_;
429 }
430
433 {
434 return cap_ - n_;
435 }
436
439 {
440 return n_ == 0;
441 }
442
445 {
446 return n_ == cap_;
447 }
448
454 {
455 destroy_all();
456 }
457
460 {
461 destroy_all();
462 }
463
464 // -- element access -------------------------------------------------------
465
471 [[nodiscard]] T &operator [] (const size_t i)
472 {
473 ah_out_of_range_error_if(i >= n_) << "RingBuffer: index out of range";
474 return buf_[phys(i)];
475 }
476
478 [[nodiscard]] const T &operator [] (const size_t i) const
479 {
480 ah_out_of_range_error_if(i >= n_) << "RingBuffer: index out of range";
481 return buf_[phys(i)];
482 }
483
485 [[nodiscard]] T &operator () (const size_t i) noexcept
486 {
487 return buf_[phys(i)];
488 }
489
491 [[nodiscard]] const T &operator () (const size_t i) const noexcept
492 {
493 return buf_[phys(i)];
494 }
495
501 {
502 ah_underflow_error_if(n_ == 0) << "RingBuffer::get_first(): empty";
503 return buf_[head_];
504 }
505
507 [[nodiscard]] const T &get_first() const
508 {
509 ah_underflow_error_if(n_ == 0) << "RingBuffer::get_first(): empty";
510 return buf_[head_];
511 }
512
518 {
519 ah_underflow_error_if(n_ == 0) << "RingBuffer::get_last(): empty";
520 return buf_[phys(n_ - 1)];
521 }
522
524 [[nodiscard]] const T &get_last() const
525 {
526 ah_underflow_error_if(n_ == 0) << "RingBuffer::get_last(): empty";
527 return buf_[phys(n_ - 1)];
528 }
529
532 {
533 return get_first();
534 }
535
537 [[nodiscard]] const T &front() const
538 {
539 return get_first();
540 }
541
544 {
545 return get_last();
546 }
547
549 [[nodiscard]] const T &back() const
550 {
551 return get_last();
552 }
553
554 // -- modifiers ------------------------------------------------------------
555
563 template <class... Args>
565 {
566 ah_overflow_error_if(is_full()) << "RingBuffer::emplace(): buffer full";
567 T *slot = buf_ + phys(n_);
568 ::new (static_cast<void *>(slot)) T(std::forward<Args>(args)...);
569 ++n_;
570 return *slot;
571 }
572
579 T &put(const T &item)
580 requires std::is_copy_constructible_v<T>
581 {
582 return emplace(item);
583 }
584
591 T &put(T &&item)
592 {
593 return emplace(std::move(item));
594 }
595
597 T &push(const T &item)
598 requires std::is_copy_constructible_v<T>
599 {
600 return put(item);
601 }
602
604 T &push(T &&item)
605 {
606 return put(std::move(item));
607 }
608
619 bool put_overwrite(const T &item)
620 requires(std::is_copy_constructible_v<T> and std::is_copy_assignable_v<T>)
621 {
622 if (not is_full())
623 {
624 put(item);
625 return false;
626 }
627 buf_[head_] = item;
628 head_ = (head_ + 1) % cap_;
629 return true;
630 }
631
633 bool put_overwrite(T &&item)
634 requires std::is_move_assignable_v<T>
635 {
636 if (not is_full())
637 {
638 put(std::move(item));
639 return false;
640 }
641 buf_[head_] = std::move(item);
642 head_ = (head_ + 1) % cap_;
643 return true;
644 }
645
652 {
653 ah_underflow_error_if(n_ == 0) << "RingBuffer::get(): empty buffer";
654 T *slot = buf_ + head_;
655 T ret = std::move(*slot);
656 std::destroy_at(slot);
657 head_ = (head_ + 1) % cap_;
658 --n_;
659 return ret;
660 }
661
664 {
665 return get();
666 }
667
668 // -- iteration ------------------------------------------------------------
669
672 {
673 return iterator(this, 0);
674 }
675
678 {
679 return iterator(this, n_);
680 }
681
684 {
685 return const_iterator(this, 0);
686 }
687
690 {
691 return const_iterator(this, n_);
692 }
693
696 {
697 return begin();
698 }
699
702 {
703 return end();
704 }
705
715 template <class Operation>
717 {
718 for (size_t i = 0; i < n_; ++i)
719 if (not operation(buf_[phys(i)]))
720 return false;
721 return true;
722 }
723
729 template <class Operation>
731 {
732 for (size_t i = 0; i < n_; ++i)
733 if (not operation(buf_[phys(i)]))
734 return false;
735 return true;
736 }
737
738 // -- comparison -----------------------------------------------------------
739
745 bool operator == (const RingBuffer &rb) const
746 {
747 if (n_ != rb.n_)
748 return false;
749 for (size_t i = 0; i < n_; ++i)
750 if (not ((*this)(i) == rb(i)))
751 return false;
752 return true;
753 }
754
756 bool operator != (const RingBuffer &rb) const
757 {
758 return not (*this == rb);
759 }
760};
761
762} // end namespace Aleph
763
764#endif // TPL_RING_BUFFER_H
Exception handling system with formatted messages for Aleph-w.
#define ah_out_of_range_error_if(C)
Throws std::out_of_range if condition holds.
Definition ah-errors.H:584
#define ah_underflow_error_if(C)
Throws std::underflow_error if condition holds.
Definition ah-errors.H:373
#define ah_overflow_error_if(C)
Throws std::overflow_error if condition holds.
Definition ah-errors.H:468
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:644
Random-access iterator over the logical window.
basic_iterator(BufPtr rb, const size_t idx) noexcept
Construct from a buffer and a logical index (internal use).
std::conditional_t< IsConst, const RingBuffer *, RingBuffer * > BufPtr
size_t index() const noexcept
Current logical index (internal use).
bool operator==(const basic_iterator &it) const noexcept
Equality: same buffer and logical position.
static size_t negative_magnitude(const std::ptrdiff_t i) noexcept
Return abs(i) for negative signed offsets without signed overflow.
basic_iterator & operator-=(const difference_type i) noexcept
Retreat by i logical positions.
basic_iterator & operator--() noexcept
Pre-decrement.
bool operator>(const basic_iterator &it) const noexcept
Strict ordering by logical position.
static size_t sub_offset(const size_t idx, const std::ptrdiff_t i) noexcept
Subtract signed offset i from logical index idx.
std::conditional_t< IsConst, const T &, T & > reference
Ref.
std::conditional_t< IsConst, const T *, T * > pointer
Ptr.
std::ptrdiff_t difference_type
Signed distance type.
bool operator>=(const basic_iterator &it) const noexcept
Ordering by logical position.
bool operator!=(const basic_iterator &it) const noexcept
Inequality: different buffer or logical position.
bool operator<=(const basic_iterator &it) const noexcept
Ordering by logical position.
bool operator<(const basic_iterator &it) const noexcept
Strict ordering by logical position.
std::random_access_iterator_tag iterator_category
Category.
basic_iterator & operator++() noexcept
Pre-increment.
basic_iterator(const basic_iterator< B > &it) noexcept
Convert a mutable iterator into a const iterator.
basic_iterator operator+(difference_type i) const noexcept
Iterator i positions forward.
basic_iterator & operator+=(const difference_type i) noexcept
Advance by i logical positions.
pointer operator->() const noexcept
Member access on the current element.
reference operator[](const difference_type i) const noexcept
Element i logical positions away.
BufPtr buffer() const noexcept
Buffer this iterator walks (internal use).
basic_iterator()=default
Construct a singular iterator.
static size_t add_offset(const size_t idx, const std::ptrdiff_t i) noexcept
Add signed offset i to logical index idx.
reference operator*() const noexcept
Dereference to the current element.
basic_iterator operator-(difference_type i) const noexcept
Iterator i positions backward.
Fixed-capacity circular FIFO buffer over contiguous storage.
bool operator!=(const RingBuffer &rb) const
Inequality: negation of operator==.
const_iterator end() const noexcept
Const iterator past the newest element. O(1).
void empty() noexcept
Destroy all elements (Aleph convention).
T & front()
Oldest element (checked). Alias of get_first().
const_iterator begin() const noexcept
Const iterator on the oldest element. O(1).
const T & get_first() const
size_t size() const noexcept
Return the number of stored elements. O(1).
basic_iterator< false > iterator
Mutable iterator.
iterator begin() noexcept
Iterator on the oldest element. O(1).
T & push(const T &item)
Queue-style alias of put(const T &).
T & emplace(Args &&...args)
Construct an element in place at the tail.
RingBuffer & operator=(const RingBuffer &rb)
Copy assignment (requires copyable T).
T & put(const T &item)
Append a copy of item at the tail.
T & put(T &&item)
Append item at the tail by moving.
size_t phys(const size_t i) const noexcept
bool is_empty() const noexcept
Return true if no elements are stored. O(1).
const_iterator cend() const noexcept
Const iterator past the newest element. O(1).
T & get_last()
Newest element — the last one inserted (checked).
RingBuffer(RingBuffer &&rb) noexcept
Move constructor: steals the storage in O(1).
void clear() noexcept
Destroy all elements. Alias of empty(). Capacity is kept.
bool put_overwrite(T &&item)
T & get_first()
Oldest element — the next to leave (checked).
void swap(RingBuffer &rb) noexcept
Swap contents with rb in O(1).
bool is_full() const noexcept
Return true if the buffer holds capacity() elements. O(1).
bool traverse(Operation operation) const
Const traversal from oldest to newest.
iterator end() noexcept
Iterator past the newest element. O(1).
const_iterator cbegin() const noexcept
Const iterator on the oldest element. O(1).
T & back()
Newest element (checked). Alias of get_last().
T get()
Extract the oldest element from the head.
T & push(T &&item)
Queue-style alias of put(T &&).
const T & get_last() const
T pop()
Queue-style alias of get().
bool operator==(const RingBuffer &rb) const
Equality: same logical contents (capacity is not compared).
const T & front() const
static T * allocate(size_t m)
RingBuffer(const RingBuffer &rb)
Copy constructor (requires copyable T).
T value_type
STL convention: value type.
T & operator[](const size_t i)
Checked access to the i-th logical element (0 = oldest).
T Item_Type
Aleph convention: element type.
RingBuffer(const size_t cap)
Construct a buffer with an exact capacity.
const T & back() const
static void deallocate(T *p, size_t m) noexcept
bool traverse(Operation operation)
Traverse from oldest to newest while operation returns true.
size_t size_type
STL convention: size type.
void destroy_all() noexcept
size_t capacity() const noexcept
Return the fixed capacity chosen at construction. O(1).
T & operator()(const size_t i) noexcept
Unchecked access to the i-th logical element (must be < size()).
basic_iterator< true > const_iterator
Const iterator.
bool put_overwrite(const T &item)
Append at the tail, evicting the oldest element when full.
size_t available() const noexcept
Return the number of free slots. O(1).
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)