diff options
| -rw-r--r-- | libs/kstd/kstd/ring_buffer.hpp | 92 |
1 files changed, 74 insertions, 18 deletions
diff --git a/libs/kstd/kstd/ring_buffer.hpp b/libs/kstd/kstd/ring_buffer.hpp index 48711cd3..99584a90 100644 --- a/libs/kstd/kstd/ring_buffer.hpp +++ b/libs/kstd/kstd/ring_buffer.hpp @@ -265,24 +265,29 @@ namespace kstd //! Construct a ring buffer by copying from an existing one. constexpr ring_buffer(ring_buffer const & other) noexcept(std::is_nothrow_copy_constructible_v<ValueType>) - : m_size{other.m_size} - , m_read_index{} { - std::ranges::for_each(std::views::iota(0uz, other.size()), - [&](auto const i) { m_storage.construct(i, other[i]); }); + auto guard = construction_guard{*this}; + std::ranges::for_each(std::views::iota(0uz, other.size()), [&](auto const i) { + m_storage.construct(i, other[i]); + ++m_size; + }); + guard.disarm(); } //! Construct a ring buffer by moving from an existing one. constexpr ring_buffer(ring_buffer && other) noexcept(std::is_nothrow_move_constructible_v<ValueType> && std::is_nothrow_destructible_v<ValueType>) - : m_size{other.m_size} - , m_read_index{} { - std::ranges::for_each(std::views::iota(0uz, other.size()), [&](auto const i) { - m_storage.construct(i, std::move(other[i])); - std::destroy_at(other.element_at(i)); + auto guard = construction_guard{*this}; + std::ranges::for_each(std::views::iota(0uz, other.size()), [&](auto) { + m_storage.construct(m_size, std::move(other[0])); + ++m_size; + auto * to_destroy = other.element_at(0); + other.m_read_index = (other.m_read_index + 1) % other.capacity(); + --other.m_size; + std::destroy_at(to_destroy); }); - other.m_size = 0; + guard.disarm(); } //! Destroy this ring buffer. @@ -301,8 +306,12 @@ namespace kstd os::panic("[KSTD] Tried to construct a ring buffer with more elements than it can support."); } - std::ranges::for_each(std::views::iota(0uz, count), [&](auto i) { m_storage.construct(i); }); - m_size = count; + auto guard = construction_guard{*this}; + std::ranges::for_each(std::views::iota(0uz, count), [&](auto i) { + m_storage.construct(i); + ++m_size; + }); + guard.disarm(); } //! Construct a ring buffer with a given number of copies of a given value. @@ -316,8 +325,12 @@ namespace kstd os::panic("[KSTD] Tried to construct a ring buffer with more elements than it can support."); } - std::ranges::for_each(std::views::iota(0uz, count), [&](auto i) { m_storage.construct(i, value); }); - m_size = count; + auto guard = construction_guard{*this}; + std::ranges::for_each(std::views::iota(0uz, count), [&](auto i) { + m_storage.construct(i, value); + ++m_size; + }); + guard.disarm(); } //! Construct a ring buffer with the elements of an input range. @@ -330,16 +343,21 @@ namespace kstd template<std::input_iterator InputIterator> constexpr ring_buffer(InputIterator first, InputIterator last) { - for (; first != last && m_size < capacity(); ++first) + auto guard = construction_guard{*this}; + if constexpr (Capacity > 0) { - push_back(std::ranges::iter_move(first)); + for (; first != last && m_size < capacity(); ++first) + { + push_back(std::ranges::iter_move(first)); + } } if (first != last) { - clear(); os::panic("[KSTD] Tried to construct a ring buffer with more elements than it can support."); } + + guard.disarm(); } //! Construct a ring buffer with the elements of a forward range. @@ -358,7 +376,12 @@ namespace kstd os::panic("[KSTD] Tried to construct a ring buffer with more elements than it can support."); } - std::ranges::for_each(first, last, [this](auto const & element) { this->push_back(element); }); + if constexpr (Capacity > 0) + { + auto guard = construction_guard{*this}; + std::ranges::for_each(first, last, [this](auto const & element) { this->push_back(element); }); + guard.disarm(); + } } //! Replace the content of this ring buffer with a copy of the content of another one. @@ -879,6 +902,39 @@ namespace kstd //! @} private: + //! An RAII guard to ensure partially constructed buffers are properly cleaned up. + struct construction_guard + { + //! Construct a new construction guard for the given ring buffer. + constexpr explicit construction_guard(ring_buffer & buf) + : m_buffer{&buf} + {} + + constexpr construction_guard(construction_guard const &) = delete; + constexpr construction_guard(construction_guard &&) = delete; + + //! Destroy the construction guard, clearing the buffer if it is still engaged. + constexpr ~construction_guard() + { + if (m_buffer) + { + m_buffer->clear(); + } + } + + constexpr auto operator=(construction_guard const &) -> construction_guard & = delete; + constexpr auto operator=(construction_guard &&) -> construction_guard & = delete; + + //! Disarm the construction guard, preventing it from clearing the buffer. + constexpr auto disarm() noexcept -> void + { + m_buffer = nullptr; + } + + private: + ring_buffer * m_buffer; + }; + //! Get a pointer to the element at the given read-index relative position. //! //! @param position The logical (read-index relative) index of the element. |
