aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-10-02 11:36:01 +0200
committerFelix Morgner <felix.morgner@ost.ch>2026-10-02 11:51:42 +0200
commit555c181c042be054fb9574d8fc77d2663bd39d4f (patch)
treeae7748c7661e8071b7aa710160a13e6d397a72f7
parentb95beb4597dec47fee3a3d3134eea138e72d27a8 (diff)
downloadkernel-555c181c042be054fb9574d8fc77d2663bd39d4f.tar.xz
kernel-555c181c042be054fb9574d8fc77d2663bd39d4f.zip
kstd: ring_buffer: implement push_back()
-rw-r--r--libs/kstd/kstd/ring_buffer.hpp41
-rw-r--r--libs/kstd/kstd/ring_buffer.tests.cpp232
2 files changed, 271 insertions, 2 deletions
diff --git a/libs/kstd/kstd/ring_buffer.hpp b/libs/kstd/kstd/ring_buffer.hpp
index 66d870dd..755ebbf6 100644
--- a/libs/kstd/kstd/ring_buffer.hpp
+++ b/libs/kstd/kstd/ring_buffer.hpp
@@ -475,6 +475,47 @@ namespace kstd
m_read_index = 0;
}
+ //! Add an element to the end of this ring buffer.
+ //!
+ //! If the buffer is full, the oldest element will be overwritten.
+ //!
+ //! @param value The value to add to the end of this ring buffer.
+ constexpr auto push_back(value_type const & value) -> void
+ {
+ if (m_size < capacity())
+ {
+ std::construct_at(element_at(m_size), value);
+ ++m_size;
+ return;
+ }
+
+ (*this)[0] = value;
+ m_read_index = (m_read_index + 1) % capacity();
+ }
+
+ //! Add an element to the end of this ring buffer.
+ //!
+ //! If the buffer is full, the oldest element will be overwritten. If the element would overwrite itself, no
+ //! assignment will be performed.
+ //!
+ //! @param value The value to add to the end of this ring buffer.
+ constexpr auto push_back(value_type && value) -> void
+ {
+ if (m_size < capacity())
+ {
+ std::construct_at(element_at(m_size), std::move(value));
+ ++m_size;
+ return;
+ }
+
+ auto target = element_at(0);
+ if (std::addressof(value) != target)
+ {
+ *target = std::move(value);
+ }
+ m_read_index = (m_read_index + 1) % capacity();
+ }
+
//! @}
private:
diff --git a/libs/kstd/kstd/ring_buffer.tests.cpp b/libs/kstd/kstd/ring_buffer.tests.cpp
index d22633d6..3da3f7ea 100644
--- a/libs/kstd/kstd/ring_buffer.tests.cpp
+++ b/libs/kstd/kstd/ring_buffer.tests.cpp
@@ -6,6 +6,7 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_exception.hpp>
+#include <catch2/matchers/catch_matchers_range_equals.hpp>
#include <algorithm>
#include <cstddef>
@@ -1020,7 +1021,7 @@ SCENARIO("Ring Buffer iterators", "[kstd][ring_buffer]")
SCENARIO("Ring Buffer modifiers", "[kstd][ring_buffer]")
{
- GIVEN("An empty ring buffer")
+ GIVEN("An empty ring buffer with capcity 5")
{
auto buffer = kstd::ring_buffer<int, 5>{};
@@ -1038,9 +1039,95 @@ SCENARIO("Ring Buffer modifiers", "[kstd][ring_buffer]")
REQUIRE(buffer.size() == 0);
}
}
+
+ WHEN("pushing an element into the buffer")
+ {
+ buffer.push_back(10);
+
+ THEN("the buffer is not empty")
+ {
+ REQUIRE_FALSE(buffer.empty());
+ }
+
+ THEN("the size is 1")
+ {
+ REQUIRE(buffer.size() == 1);
+ }
+
+ THEN("the first element is equal to the pushed value")
+ {
+ REQUIRE(buffer.at(0) == 10);
+ }
+ }
+
+ WHEN("pushing six elements into the buffer copy")
+ {
+ for (int i = 0; i < 6; ++i)
+ {
+ buffer.push_back(i);
+ }
+
+ THEN("the buffer is not empty")
+ {
+ REQUIRE_FALSE(buffer.empty());
+ }
+
+ THEN("the size is equal to the capacity")
+ {
+ REQUIRE(buffer.size() == buffer.capacity());
+ }
+
+ THEN("the first element is equal to the second pushed value")
+ {
+ REQUIRE(buffer.at(0) == 1);
+ }
+
+ THEN("the last element is equal to the last pushed value")
+ {
+ REQUIRE(buffer.at(4) == 5);
+ }
+ }
+
+ WHEN("pushing ten element into the buffer by copy")
+ {
+ for (int i = 0; i < 10; ++i)
+ {
+ buffer.push_back(i);
+ }
+
+ THEN("the content is equal to the last 5 pushed values")
+ {
+ REQUIRE_THAT(buffer, Catch::Matchers::RangeEquals({5, 6, 7, 8, 9}));
+ }
+ }
}
- GIVEN("A ring buffer containing static operation trackers")
+ GIVEN("A ring buffer with a capacity of 5 containing 3 elements")
+ {
+ auto buffer = kstd::ring_buffer<int, 5>{3};
+
+ WHEN("pushing a single element into the buffer")
+ {
+ buffer.push_back(10);
+
+ THEN("the buffer is not empty")
+ {
+ REQUIRE_FALSE(buffer.empty());
+ }
+
+ THEN("the size is 4")
+ {
+ REQUIRE(buffer.size() == 4);
+ }
+
+ THEN("the last element is equal to the pushed value")
+ {
+ REQUIRE(buffer.at(3) == 10);
+ }
+ }
+ }
+
+ GIVEN("A partially filled ring buffer of static operation trackers")
{
auto buffer = kstd::ring_buffer<kstd::tests::static_copy_move_tracker, 5>{3};
@@ -1064,5 +1151,146 @@ SCENARIO("Ring Buffer modifiers", "[kstd][ring_buffer]")
REQUIRE(kstd::tests::static_copy_move_tracker::dtor_call_count == 3);
}
}
+
+ WHEN("pushing an element from the buffer into the buffer")
+ {
+ kstd::tests::static_copy_move_tracker::reset();
+ buffer.push_back(buffer.at(0));
+
+ THEN("the buffer is not empty")
+ {
+ REQUIRE_FALSE(buffer.empty());
+ }
+
+ THEN("the size is 4")
+ {
+ REQUIRE(buffer.size() == 4);
+ }
+
+ THEN("1 copy construction occurs")
+ {
+ REQUIRE(kstd::tests::static_copy_move_tracker::dtor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::copy_ctor_call_count == 1);
+ REQUIRE(kstd::tests::static_copy_move_tracker::copy_assignment_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::move_ctor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::move_assignment_call_count == 0);
+ }
+ }
+
+ WHEN("pushing an element from the buffer into the buffer using move semantics")
+ {
+ kstd::tests::static_copy_move_tracker::reset();
+ buffer.push_back(std::move(buffer.at(0)));
+
+ THEN("the buffer is not empty")
+ {
+ REQUIRE_FALSE(buffer.empty());
+ }
+
+ THEN("the size is 4")
+ {
+ REQUIRE(buffer.size() == 4);
+ }
+
+ THEN("1 move construction occurs")
+ {
+ REQUIRE(kstd::tests::static_copy_move_tracker::dtor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::copy_ctor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::copy_assignment_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::move_ctor_call_count == 1);
+ REQUIRE(kstd::tests::static_copy_move_tracker::move_assignment_call_count == 0);
+ }
+ }
+
+ WHEN("the buffer is full")
+ {
+ buffer.push_back({});
+ buffer.push_back({});
+
+ AND_WHEN("the first element is pushed into the buffer by copy")
+ {
+ kstd::tests::static_copy_move_tracker::reset();
+ buffer.push_back(buffer.at(0));
+
+ THEN("1 copy assignment occurs")
+ {
+ REQUIRE(kstd::tests::static_copy_move_tracker::dtor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::copy_ctor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::copy_assignment_call_count == 1);
+ REQUIRE(kstd::tests::static_copy_move_tracker::move_ctor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::move_assignment_call_count == 0);
+ }
+ }
+
+ AND_WHEN("the first element is pushed into the buffer by move")
+ {
+ kstd::tests::static_copy_move_tracker::reset();
+ buffer.push_back(std::move(buffer.at(0)));
+
+ THEN("no special operation occurs")
+ {
+ REQUIRE(kstd::tests::static_copy_move_tracker::dtor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::copy_ctor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::copy_assignment_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::move_ctor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::move_assignment_call_count == 0);
+ }
+ }
+ }
+ }
+
+ GIVEN("A full ring buffer of static operation trackers")
+ {
+ auto buffer = kstd::ring_buffer<kstd::tests::static_copy_move_tracker, 5>{5};
+
+ WHEN("pushing an element from the buffer into the buffer")
+ {
+ kstd::tests::static_copy_move_tracker::reset();
+ buffer.push_back({buffer.at(0)});
+
+ THEN("the buffer is not empty")
+ {
+ REQUIRE_FALSE(buffer.empty());
+ }
+
+ THEN("the size is 5")
+ {
+ REQUIRE(buffer.size() == 5);
+ }
+
+ THEN("1 copy assignment occurs")
+ {
+ REQUIRE(kstd::tests::static_copy_move_tracker::dtor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::copy_ctor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::copy_assignment_call_count == 1);
+ REQUIRE(kstd::tests::static_copy_move_tracker::move_ctor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::move_assignment_call_count == 0);
+ }
+ }
+
+ WHEN("pushing an element into the buffer from a temporary")
+ {
+ kstd::tests::static_copy_move_tracker::reset();
+ buffer.push_back({});
+
+ THEN("the buffer is not empty")
+ {
+ REQUIRE_FALSE(buffer.empty());
+ }
+
+ THEN("the size is 5")
+ {
+ REQUIRE(buffer.size() == 5);
+ }
+
+ THEN("1 move assignment and 1 dtor call occurs")
+ {
+ REQUIRE(kstd::tests::static_copy_move_tracker::dtor_call_count == 1);
+ REQUIRE(kstd::tests::static_copy_move_tracker::copy_ctor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::copy_assignment_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::move_ctor_call_count == 0);
+ REQUIRE(kstd::tests::static_copy_move_tracker::move_assignment_call_count == 1);
+ }
+ }
}
} \ No newline at end of file