aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/kstd/kstd/ring_buffer.hpp33
-rw-r--r--libs/kstd/kstd/ring_buffer.tests.cpp224
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