diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-10-02 16:50:47 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-10-02 16:50:47 +0200 |
| commit | 7087754a7360424518774cbb0fdf4b577edc167b (patch) | |
| tree | 932a1be851c0477dad5a8e483a990a2057e70b02 | |
| parent | bf59e881f3d4069d01e1dfad7c2cd8db54975763 (diff) | |
| download | kernel-7087754a7360424518774cbb0fdf4b577edc167b.tar.xz kernel-7087754a7360424518774cbb0fdf4b577edc167b.zip | |
kstd: ring_buffer: handle zero-capacity instantiations
| -rw-r--r-- | libs/kstd/kstd/ring_buffer.hpp | 33 | ||||
| -rw-r--r-- | libs/kstd/kstd/ring_buffer.tests.cpp | 224 |
2 files changed, 249 insertions, 8 deletions
diff --git a/libs/kstd/kstd/ring_buffer.hpp b/libs/kstd/kstd/ring_buffer.hpp index ce737a81..10f45d72 100644 --- a/libs/kstd/kstd/ring_buffer.hpp +++ b/libs/kstd/kstd/ring_buffer.hpp @@ -626,6 +626,7 @@ namespace kstd //! //! @param value The value to add to the end of this ring buffer. constexpr auto push_back(value_type const & value) -> void + requires(Capacity > 0) { if (m_size < capacity()) { @@ -645,6 +646,7 @@ namespace kstd //! //! @param value The value to add to the end of this ring buffer. constexpr auto push_back(value_type && value) -> void + requires(Capacity > 0) { if (m_size < capacity()) { @@ -669,19 +671,27 @@ namespace kstd requires(std::same_as<ValueType, std::remove_cvref_t<PushedType>>) [[nodiscard]] constexpr auto try_push_back(PushedType && value) -> bool { - if (m_size >= capacity()) + if constexpr (Capacity > 0) + { + if (m_size >= capacity()) + { + return false; + } + + push_back(std::forward<PushedType>(value)); + return true; + } + else { return false; } - - push_back(std::forward<PushedType>(value)); - return true; } //! Remove the first element in the buffer. //! //! @warning This function will panic if the buffer is empty. constexpr auto pop_front() -> void + requires(Capacity > 0) { if (empty()) { @@ -698,13 +708,20 @@ namespace kstd //! @return @p true iff. an element was removed, @p false otherwise [[nodiscard]] constexpr auto try_pop_front() noexcept(std::is_nothrow_destructible_v<value_type>) -> bool { - if (empty()) + if constexpr (Capacity > 0) + { + if (empty()) + { + return false; + } + + pop_front(); + return true; + } + else { return false; } - - pop_front(); - return true; } //! @} diff --git a/libs/kstd/kstd/ring_buffer.tests.cpp b/libs/kstd/kstd/ring_buffer.tests.cpp index 99dbe3eb..453713ee 100644 --- a/libs/kstd/kstd/ring_buffer.tests.cpp +++ b/libs/kstd/kstd/ring_buffer.tests.cpp @@ -17,6 +17,16 @@ #include <type_traits> #include <utility> +namespace +{ + template<typename BufferType> + concept has_push_back = requires(BufferType buffer) { buffer.push_back(typename BufferType::value_type{}); }; + + template<typename BufferType> + concept has_pop_front = requires(BufferType buffer) { buffer.pop_front(); }; + +} // namespace + SCENARIO("Ring Buffer interface types", "[kstd][ring_buffer]") { GIVEN("A ring buffer of float") @@ -1606,4 +1616,218 @@ SCENARIO("Ring Buffer modifiers", "[kstd][ring_buffer]") } } } +} + +SCENARIO("Ring Buffer zero capacity", "[kstd][rin_buffer]") +{ + GIVEN("A ring buffer with a capacity of 0") + { + auto buffer = kstd::ring_buffer<int, 0>{}; + + THEN("the buffer is always empty") + { + REQUIRE(buffer.empty()); + } + + THEN("the capacity is 0") + { + REQUIRE(buffer.capacity() == 0); + } + + THEN("the maximum size is 0") + { + REQUIRE(buffer.max_size() == 0); + } + + THEN("trying to push an element fails") + { + REQUIRE_FALSE(buffer.try_push_back(1)); + } + + THEN("trying to pop an element fails") + { + REQUIRE_FALSE(buffer.try_pop_front()); + } + + THEN("push_back cannot be called") + { + STATIC_REQUIRE_FALSE(has_push_back<decltype(buffer)>); + } + + THEN("pop_front cannot be called") + { + STATIC_REQUIRE_FALSE(has_pop_front<decltype(buffer)>); + } + + THEN("accessing element 0 using at() panics") + { + REQUIRE_THROWS_MATCHES(buffer.at(0), kstd::tests::os_panic, + Catch::Matchers::Message("[KSTD] Index out-of-bounds in ring_buffer element access!")); + } + + THEN("calling front() panics") + { + REQUIRE_THROWS_MATCHES(buffer.front(), kstd::tests::os_panic, + Catch::Matchers::Message("[KSTD] Tried to access an element from an empty ring_buffer!")); + } + + THEN("calling back() panics") + { + REQUIRE_THROWS_MATCHES(buffer.back(), kstd::tests::os_panic, + Catch::Matchers::Message("[KSTD] Tried to access an element from an empty ring_buffer!")); + } + + THEN("begin() equals end()") + { + REQUIRE(buffer.begin() == buffer.end()); + } + + THEN("cbegin() equals cend()") + { + REQUIRE(buffer.cbegin() == buffer.cend()); + } + + THEN("rbegin() equals rend()") + { + REQUIRE(buffer.rbegin() == buffer.rend()); + } + + THEN("the distance between begin() and end() is 0") + { + REQUIRE(std::ranges::distance(buffer) == 0); + } + + WHEN("clearing the buffer") + { + buffer.clear(); + + THEN("the buffer is still empty") + { + REQUIRE(buffer.empty()); + } + } + + WHEN("constructing a copy") + { + auto copy = buffer; + + THEN("the copy is empty") + { + REQUIRE(copy.empty()); + } + } + + WHEN("moving from it") + { + auto moved = std::move(buffer); + + THEN("the new buffer is empty") + { + REQUIRE(moved.empty()); + } + } + + WHEN("copy assigning to another zero-capacity buffer") + { + auto other = kstd::ring_buffer<int, 0>{}; + other = buffer; + + THEN("the target is still empty") + { + REQUIRE(other.empty()); + } + } + + WHEN("move assigning to another zero-capacity buffer") + { + auto other = kstd::ring_buffer<int, 0>{}; + other = std::move(buffer); + + THEN("the target is still empty") + { + REQUIRE(other.empty()); + } + } + } + + WHEN("constructing by n-value-initialized constructor with a count of 0") + { + auto buffer = kstd::ring_buffer<int, 0>{0}; + + THEN("the buffer is empty") + { + REQUIRE(buffer.empty()); + } + } + + WHEN("constructing by n-value-initialized constructor with a count of 1") + { + THEN("the constructor panics") + { + REQUIRE_THROWS_MATCHES( + (kstd::ring_buffer<int, 0>{1}), kstd::tests::os_panic, + Catch::Matchers::Message("[KSTD] Tried to construct a ring buffer with more elements than it can support.")); + } + } + + WHEN("constructing by n-copies-of-value constructor with a count of 0") + { + auto buffer = kstd::ring_buffer<int, 0>{0, 10}; + + THEN("the buffer is empty") + { + REQUIRE(buffer.empty()); + } + } + + WHEN("constructing by n-copies-of-value constructor with a count of 1") + { + THEN("the constructor panics") + { + REQUIRE_THROWS_MATCHES( + (kstd::ring_buffer<int, 0>{1, 42}), kstd::tests::os_panic, + Catch::Matchers::Message("[KSTD] Tried to construct a ring buffer with more elements than it can support.")); + } + } + + GIVEN("A zero-capacity buffer of static operation trackers") + { + auto buffer = kstd::ring_buffer<kstd::tests::static_copy_move_tracker, 0>{}; + + WHEN("trying to push an element from a temporary") + { + kstd::tests::static_copy_move_tracker::reset(); + auto const pushed = buffer.try_push_back(kstd::tests::static_copy_move_tracker{}); + + THEN("the push fails") + { + REQUIRE_FALSE(pushed); + } + + THEN("no special operation occurs other than the temporary's own destruction") + { + REQUIRE(kstd::tests::static_copy_move_tracker::copy_ctor_call_count == 0); + REQUIRE(kstd::tests::static_copy_move_tracker::move_ctor_call_count == 0); + REQUIRE(kstd::tests::static_copy_move_tracker::copy_assignment_call_count == 0); + REQUIRE(kstd::tests::static_copy_move_tracker::move_assignment_call_count == 0); + REQUIRE(kstd::tests::static_copy_move_tracker::dtor_call_count == 1); + } + } + + WHEN("trying to pop an element") + { + kstd::tests::static_copy_move_tracker::reset(); + auto const popped = buffer.try_pop_front(); + + THEN("the pop fails") + { + REQUIRE_FALSE(popped); + } + + THEN("no destructor is invoked") + { + REQUIRE(kstd::tests::static_copy_move_tracker::dtor_call_count == 0); + } + } + } }
\ No newline at end of file |
