-
Notifications
You must be signed in to change notification settings - Fork 0
Enhance ring buffer implementation with additional features and tests #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
002c14f
cdcb97c
4423210
8461f1d
4f3a415
cd69180
c72bd51
40a89ad
cf5f003
0b9d7d7
55aec70
235c9ce
879256e
da18e64
32b99a4
27807fd
d49855f
cc02225
4a8f2ef
b068e63
c52050a
e6cbd21
d06bf03
bd7f23a
2e75f29
c31b7cc
a7c245e
54c8d3b
ac4043d
c1aaaba
dd67dd0
ee52493
64b5492
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,6 @@ | ||
| .idea | ||
| cmake-build-debug | ||
| cmake-build-release | ||
| build | ||
| Testing/Temporary | ||
|
|
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |||||||||||||||||||||||||||||||||||
| #include <type_traits> | ||||||||||||||||||||||||||||||||||||
| #include <algorithm> | ||||||||||||||||||||||||||||||||||||
| #include <cstring> | ||||||||||||||||||||||||||||||||||||
| #include <utility> | ||||||||||||||||||||||||||||||||||||
| #include <vector> | ||||||||||||||||||||||||||||||||||||
| #pragma once | ||||||||||||||||||||||||||||||||||||
| namespace buffers { | ||||||||||||||||||||||||||||||||||||
|
|
@@ -62,12 +63,12 @@ namespace buffers { | |||||||||||||||||||||||||||||||||||
| [[nodiscard]] const_pointer operator->() const noexcept { | ||||||||||||||||||||||||||||||||||||
| return &((*source_)[index_]); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] self_type& operator++() noexcept { | ||||||||||||||||||||||||||||||||||||
| self_type& operator++() noexcept { | ||||||||||||||||||||||||||||||||||||
| index_ = ++index_ % N; | ||||||||||||||||||||||||||||||||||||
| ++count_; | ||||||||||||||||||||||||||||||||||||
| return *this; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] self_type operator++(int) noexcept { | ||||||||||||||||||||||||||||||||||||
| self_type operator++(int) noexcept { | ||||||||||||||||||||||||||||||||||||
|
bugparty marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||||
| auto result = *this; | ||||||||||||||||||||||||||||||||||||
| ++(*this); | ||||||||||||||||||||||||||||||||||||
| return result; | ||||||||||||||||||||||||||||||||||||
|
|
@@ -78,6 +79,9 @@ namespace buffers { | |||||||||||||||||||||||||||||||||||
| [[nodiscard]] size_type count() const noexcept { | ||||||||||||||||||||||||||||||||||||
| return count_; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] buffer_t source() const noexcept { | ||||||||||||||||||||||||||||||||||||
| return source_; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| ~ring_buffer_iterator() = default; | ||||||||||||||||||||||||||||||||||||
| private: | ||||||||||||||||||||||||||||||||||||
| buffer_t source_{}; | ||||||||||||||||||||||||||||||||||||
|
|
@@ -88,13 +92,13 @@ namespace buffers { | |||||||||||||||||||||||||||||||||||
| template<typename T, size_t N, bool C, bool Overwrite> | ||||||||||||||||||||||||||||||||||||
| bool operator==(ring_buffer_iterator<T,N,C,Overwrite> const& l, | ||||||||||||||||||||||||||||||||||||
| ring_buffer_iterator<T,N,C,Overwrite> const& r) noexcept { | ||||||||||||||||||||||||||||||||||||
| return l.count() == r.count(); | ||||||||||||||||||||||||||||||||||||
| return l.source() == r.source() && l.count() == r.count() && l.index() == r.index(); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| template<typename T, size_t N, bool C, bool Overwrite> | ||||||||||||||||||||||||||||||||||||
| bool operator!=(ring_buffer_iterator<T,N,C,Overwrite> const& l, | ||||||||||||||||||||||||||||||||||||
| ring_buffer_iterator<T,N,C,Overwrite> const& r) noexcept { | ||||||||||||||||||||||||||||||||||||
| return l.count() != r.count(); | ||||||||||||||||||||||||||||||||||||
| return l.source() != r.source() || l.count() != r.count() || l.index() != r.index(); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
@@ -134,11 +138,14 @@ using std::bool_constant; | |||||||||||||||||||||||||||||||||||
| using iterator = detail::ring_buffer_iterator<T, N, false, Overwrite>; | ||||||||||||||||||||||||||||||||||||
| using const_iterator = detail::ring_buffer_iterator<T, N, true, Overwrite>; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Create an empty ring buffer. | ||||||||||||||||||||||||||||||||||||
| ring_buffer() noexcept = default; | ||||||||||||||||||||||||||||||||||||
| // Copy contents and state from another buffer. | ||||||||||||||||||||||||||||||||||||
| ring_buffer(ring_buffer const& rhs) noexcept(is_nothrow_copy_constructible_v<value_type>) | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| copy_impl(rhs, bool_constant<is_trivially_copyable_v<T>>{}); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| // Assign from another buffer. | ||||||||||||||||||||||||||||||||||||
| ring_buffer& operator=(ring_buffer const& rhs) noexcept(is_nothrow_copy_constructible_v<value_type>) { | ||||||||||||||||||||||||||||||||||||
| if(this == &rhs) | ||||||||||||||||||||||||||||||||||||
| return *this; | ||||||||||||||||||||||||||||||||||||
|
|
@@ -148,10 +155,31 @@ using std::bool_constant; | |||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return *this; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| // Move contents and state from another buffer. | ||||||||||||||||||||||||||||||||||||
| ring_buffer(ring_buffer&& rhs) noexcept(std::is_nothrow_move_constructible<value_type>::value) | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| move_impl(rhs, bool_constant<is_trivially_copyable_v<T>>{}); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| // Move-assign from another buffer. | ||||||||||||||||||||||||||||||||||||
| ring_buffer& operator=(ring_buffer&& rhs) noexcept(std::is_nothrow_move_constructible<value_type>::value) { | ||||||||||||||||||||||||||||||||||||
| if(this == &rhs) | ||||||||||||||||||||||||||||||||||||
| return *this; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| destroy_all(bool_constant<is_trivially_copyable_v<T>>{}); | ||||||||||||||||||||||||||||||||||||
| move_impl(rhs, bool_constant<is_trivially_copyable_v<T>>{}); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return *this; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| // Swap contents with another buffer. | ||||||||||||||||||||||||||||||||||||
| void swap(self_type& rhs) noexcept(noexcept(swap_impl(rhs, bool_constant<is_trivially_copyable_v<T>>{}))) { | ||||||||||||||||||||||||||||||||||||
| swap_impl(rhs, bool_constant<is_trivially_copyable_v<T>>{}); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| // Append an element, overwriting when configured. | ||||||||||||||||||||||||||||||||||||
| template<typename U> | ||||||||||||||||||||||||||||||||||||
| void push_back(U&& value) { | ||||||||||||||||||||||||||||||||||||
| push_back(std::forward<U>(value), bool_constant<Overwrite>{}); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| // Remove the oldest element if present. | ||||||||||||||||||||||||||||||||||||
| void pop_front() noexcept{ | ||||||||||||||||||||||||||||||||||||
| if(empty()) | ||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||
|
|
@@ -161,36 +189,52 @@ using std::bool_constant; | |||||||||||||||||||||||||||||||||||
| --size_; | ||||||||||||||||||||||||||||||||||||
| tail_ = ++tail_ %N; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| // Access the newest element. | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] reference back() noexcept { | ||||||||||||||||||||||||||||||||||||
| return reinterpret_cast<reference>(elements_[(head_ + N - 1) % N]); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| // Access the newest element (const). | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] const_reference back() const noexcept { | ||||||||||||||||||||||||||||||||||||
| return const_cast<self_type*>(this)->back(); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| // Access the oldest element. | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] reference front() noexcept { return reinterpret_cast<reference >(elements_[tail_]); } | ||||||||||||||||||||||||||||||||||||
| // Access the oldest element (const). | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] const_reference front() const noexcept { | ||||||||||||||||||||||||||||||||||||
| return const_cast<self_type*>(this)->front(); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| // Direct access by internal storage index. | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] reference operator[](size_type index) noexcept { | ||||||||||||||||||||||||||||||||||||
| return reinterpret_cast<reference >(elements_[index]); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| // Direct access by internal storage index (const). | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] const_reference operator[](size_type index) const noexcept { | ||||||||||||||||||||||||||||||||||||
| return const_cast<self_type *>(this)->operator[](index); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| // Iterator to oldest element. | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] iterator begin() noexcept { return iterator{this, tail_, 0};} | ||||||||||||||||||||||||||||||||||||
| // Iterator to one past newest element. | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] iterator end() noexcept { return iterator{this, head_, size_};} | ||||||||||||||||||||||||||||||||||||
| // Const iterator to oldest element. | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] const_iterator cbegin() const noexcept { return const_iterator{this, tail_, 0};} | ||||||||||||||||||||||||||||||||||||
| // Const iterator to one past newest element. | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] const_iterator cend() const noexcept { return const_iterator{this, head_, size_};} | ||||||||||||||||||||||||||||||||||||
| // Check if buffer has no elements. | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] bool empty() const noexcept { return size_ == 0; } | ||||||||||||||||||||||||||||||||||||
| // Check if buffer is at capacity. | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] bool full() const noexcept { return size_ == N; } | ||||||||||||||||||||||||||||||||||||
| // Current element count. | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] size_type size() const noexcept { return size_; } | ||||||||||||||||||||||||||||||||||||
| // Maximum element count. | ||||||||||||||||||||||||||||||||||||
| [[nodiscard]] size_type capacity() const noexcept { return N; } | ||||||||||||||||||||||||||||||||||||
| // Remove all elements and reset indices. | ||||||||||||||||||||||||||||||||||||
| void clear() noexcept { | ||||||||||||||||||||||||||||||||||||
| destroy_all(bool_constant<is_trivially_destructible_v<value_type>>{}); | ||||||||||||||||||||||||||||||||||||
| size_ = 0; | ||||||||||||||||||||||||||||||||||||
| head_ = 0; | ||||||||||||||||||||||||||||||||||||
| tail_ = 0; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| // Destroy elements on teardown. | ||||||||||||||||||||||||||||||||||||
| ~ring_buffer() { | ||||||||||||||||||||||||||||||||||||
| clear(); | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
|
@@ -204,7 +248,7 @@ using std::bool_constant; | |||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| void copy_impl(self_type const& rhs, std::true_type) { | ||||||||||||||||||||||||||||||||||||
| std::memcpy(elements_, rhs.elements_, rhs.size_ * sizeof(T)); | ||||||||||||||||||||||||||||||||||||
| std::memcpy(elements_, rhs.elements_, N * sizeof(T)); | ||||||||||||||||||||||||||||||||||||
| size_ = rhs.size_; | ||||||||||||||||||||||||||||||||||||
| tail_ = rhs.tail_; | ||||||||||||||||||||||||||||||||||||
| head_ = rhs.head_; | ||||||||||||||||||||||||||||||||||||
|
|
@@ -248,6 +292,59 @@ using std::bool_constant; | |||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| void move_impl(self_type& rhs, std::true_type) { | ||||||||||||||||||||||||||||||||||||
| std::memcpy(elements_, rhs.elements_, N * sizeof(T)); | ||||||||||||||||||||||||||||||||||||
| size_ = rhs.size_; | ||||||||||||||||||||||||||||||||||||
| tail_ = rhs.tail_; | ||||||||||||||||||||||||||||||||||||
| head_ = rhs.head_; | ||||||||||||||||||||||||||||||||||||
| rhs.clear(); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| void move_impl(self_type& rhs, std::false_type) { | ||||||||||||||||||||||||||||||||||||
| tail_ = rhs.tail_; | ||||||||||||||||||||||||||||||||||||
| head_ = rhs.head_; | ||||||||||||||||||||||||||||||||||||
| size_ = rhs.size_; | ||||||||||||||||||||||||||||||||||||
| #ifdef __cpp_exceptions | ||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||
| for (auto i = 0; i < size_; ++i) | ||||||||||||||||||||||||||||||||||||
| new( elements_ + ((tail_ + i) % N)) T(std::move(rhs[(tail_ + i) % N])); | ||||||||||||||||||||||||||||||||||||
| }catch(...) { | ||||||||||||||||||||||||||||||||||||
| while(!empty()) { | ||||||||||||||||||||||||||||||||||||
| destroy(tail_, bool_constant<std::is_trivially_destructible_v<value_type>>{}); | ||||||||||||||||||||||||||||||||||||
| tail_ = ++tail_ % N; | ||||||||||||||||||||||||||||||||||||
| --size_; | ||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If Useful? React with 👍 / 👎. |
||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| throw; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| #else | ||||||||||||||||||||||||||||||||||||
| storage_type *p = nullptr; | ||||||||||||||||||||||||||||||||||||
| for (auto i = 0; i < size_; ++i) { | ||||||||||||||||||||||||||||||||||||
| p =reinterpret_cast<storage_type *>(new(elements_ + ((tail_ + i) % N)) T(std::move(rhs[(tail_ + i) % N]))); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
| if (!p) { | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| if (!p) { | ||||||||||||||||||||||||||||||||||||
| while(!empty()) { | ||||||||||||||||||||||||||||||||||||
| destroy(tail_, bool_constant<is_trivially_destructible_v<value_type>>{}); | ||||||||||||||||||||||||||||||||||||
| tail_ = ++tail_ % N; | ||||||||||||||||||||||||||||||||||||
| --size_; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
| storage_type *p = nullptr; | |
| for (auto i = 0; i < size_; ++i) { | |
| p =reinterpret_cast<storage_type *>(new(elements_ + ((tail_ + i) % N)) T(std::move(rhs[(tail_ + i) % N]))); | |
| if (!p) { | |
| break; | |
| } | |
| } | |
| if (!p) { | |
| while(!empty()) { | |
| destroy(tail_, bool_constant<is_trivially_destructible_v<value_type>>{}); | |
| tail_ = ++tail_ % N; | |
| --size_; | |
| } | |
| } | |
| for (auto i = 0; i < size_; ++i) { | |
| new(elements_ + ((tail_ + i) % N)) T(std::move(rhs[(tail_ + i) % N])); | |
| } |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The swap_impl for trivially copyable types attempts to swap C-style arrays (elements_), which cannot be done with std::swap. C-style arrays are not assignable. This implementation will not compile. The correct approach is to swap element-by-element or use std::swap_ranges for the arrays.
Uh oh!
There was an error while loading. Please reload this page.