diff options
| -rw-r--r-- | libs/kstd/kstd/ring_buffer.hpp | 41 | ||||
| -rw-r--r-- | libs/kstd/kstd/ring_buffer.tests.cpp | 232 |
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 |
