diff --git a/src/xrt/auxiliary/util/u_id_ringbuffer.cpp b/src/xrt/auxiliary/util/u_id_ringbuffer.cpp index 1e726db5e..719851db6 100644 --- a/src/xrt/auxiliary/util/u_id_ringbuffer.cpp +++ b/src/xrt/auxiliary/util/u_id_ringbuffer.cpp @@ -165,6 +165,15 @@ u_id_ringbuffer_pop_front(struct u_id_ringbuffer *uirb) DEFAULT_CATCH() } +void +u_id_ringbuffer_pop_back(struct u_id_ringbuffer *uirb) +{ + try { + uirb->helper.pop_back(); + } + DEFAULT_CATCH() +} + int32_t u_id_ringbuffer_get_back(struct u_id_ringbuffer *uirb, uint64_t *out_id) diff --git a/src/xrt/auxiliary/util/u_id_ringbuffer.h b/src/xrt/auxiliary/util/u_id_ringbuffer.h index d1fee2b47..90f6e06d8 100644 --- a/src/xrt/auxiliary/util/u_id_ringbuffer.h +++ b/src/xrt/auxiliary/util/u_id_ringbuffer.h @@ -65,6 +65,16 @@ u_id_ringbuffer_push_back(struct u_id_ringbuffer *uirb, uint64_t id); void u_id_ringbuffer_pop_front(struct u_id_ringbuffer *uirb); +/** + * Pop an element from the back, if any + * + * @param uirb self pointer. + * + * @public @memberof u_id_ringbuffer + */ +void +u_id_ringbuffer_pop_back(struct u_id_ringbuffer *uirb); + /** * Get the back (most recent) of the buffer * diff --git a/src/xrt/auxiliary/util/u_template_historybuf.hpp b/src/xrt/auxiliary/util/u_template_historybuf.hpp index f56a9f406..8fb9be270 100644 --- a/src/xrt/auxiliary/util/u_template_historybuf.hpp +++ b/src/xrt/auxiliary/util/u_template_historybuf.hpp @@ -52,6 +52,20 @@ public: void push_back(const T &element); + /*! + * @brief Logically remove the newest element from the buffer. + * + * This is permitted to invalidate iterators. They won't be poisoned, + * but they will return something you don't expect. + * + * @return true if there was something to pop. + */ + bool + pop_back() noexcept + { + return helper_.pop_back(); + } + /*! * @brief Logically remove the oldest element from the buffer. * diff --git a/src/xrt/auxiliary/util/u_template_historybuf_impl_helpers.hpp b/src/xrt/auxiliary/util/u_template_historybuf_impl_helpers.hpp index ea6f09e89..be8ccead6 100644 --- a/src/xrt/auxiliary/util/u_template_historybuf_impl_helpers.hpp +++ b/src/xrt/auxiliary/util/u_template_historybuf_impl_helpers.hpp @@ -96,6 +96,15 @@ public: void pop_front() noexcept; + /*! + * @brief Record the logical removal of the back element, if any. + * + * Returns false if the buffer is empty. Does not actually modify the + * value stored in the backing array. + */ + bool + pop_back() noexcept; + //! Get the inner index of the front (oldest) value, or capacity_ if empty. size_t front_inner_index() const noexcept; @@ -192,7 +201,17 @@ RingBufferHelper::pop_front() noexcept length_--; } } - +inline bool +RingBufferHelper::pop_back() noexcept +{ + if (empty()) { + return false; + } + // adding capacity before -1 to avoid overflow + latest_inner_idx_ = (latest_inner_idx_ + capacity_ - 1) % capacity_; + length_--; + return true; +} inline size_t RingBufferHelper::front_inner_index() const noexcept { diff --git a/tests/tests_history_buf.cpp b/tests/tests_history_buf.cpp index 570c68385..7db260017 100644 --- a/tests/tests_history_buf.cpp +++ b/tests/tests_history_buf.cpp @@ -217,6 +217,11 @@ TEST_CASE("u_template_historybuf") CHECK_FALSE(buffer.begin().valid()); CHECK_FALSE(buffer.end().valid()); CHECK(buffer.begin() == buffer.end()); + { + INFO("Check after pop_back"); + REQUIRE_FALSE(buffer.pop_back()); + CHECK(buffer.empty()); + } } SECTION("behavior with one") { @@ -277,6 +282,14 @@ TEST_CASE("u_template_historybuf") CHECK(buffer.back() == 0); CHECK(*buffer.begin() == buffer.front()); + + { + INFO("Check after pop_back"); + REQUIRE(buffer.pop_back()); + CHECK(buffer.size() == 0); + + REQUIRE_FALSE(buffer.pop_back()); + } } SECTION("behavior with two") @@ -352,6 +365,15 @@ TEST_CASE("u_template_historybuf") CHECK(*buffer.begin() == buffer.front()); CHECK(buffer.back() == *(--buffer.end())); } + SECTION("Check after pop_back") + { + REQUIRE(buffer.pop_back()); + CHECK(buffer.size() == 1); + CHECK(buffer.front() == 0); + + REQUIRE(buffer.pop_back()); + CHECK(buffer.size() == 0); + } } SECTION("algorithm behavior with 3") diff --git a/tests/tests_id_ringbuffer.cpp b/tests/tests_id_ringbuffer.cpp index a9ce49eda..630081d57 100644 --- a/tests/tests_id_ringbuffer.cpp +++ b/tests/tests_id_ringbuffer.cpp @@ -23,6 +23,12 @@ TEST_CASE("u_template_historybuf") uint64_t out_id = 0; CHECK(u_id_ringbuffer_get_front(buffer, &out_id) < 0); CHECK(u_id_ringbuffer_get_back(buffer, &out_id) < 0); + + { + INFO("Check after pop_back"); + u_id_ringbuffer_pop_back(buffer); + CHECK(u_id_ringbuffer_is_empty(buffer)); + } } SECTION("behavior with one") { @@ -74,6 +80,16 @@ TEST_CASE("u_template_historybuf") CHECK(u_id_ringbuffer_get_at_clamped_age(buffer, 2, &out_id) == zero_inner_index); CHECK(out_id == 0); } + + { + INFO("Check after pop_back"); + u_id_ringbuffer_pop_back(buffer); + + CHECK(u_id_ringbuffer_is_empty(buffer)); + + u_id_ringbuffer_pop_back(buffer); + CHECK(u_id_ringbuffer_is_empty(buffer)); + } } SECTION("behavior with two") @@ -154,6 +170,19 @@ TEST_CASE("u_template_historybuf") CHECK(out_id == 0); } } + SECTION("Check after pop_back") + { + u_id_ringbuffer_pop_back(buffer); + CHECK(1 == u_id_ringbuffer_get_size(buffer)); + uint64_t out_id_front = 55; + CHECK(u_id_ringbuffer_get_front(buffer, &out_id_front) == zero_inner_index); + CHECK(out_id_front == 0); + + u_id_ringbuffer_pop_back(buffer); + CHECK(0 == u_id_ringbuffer_get_size(buffer)); + uint64_t out_id = 0; + CHECK(u_id_ringbuffer_get_front(buffer, &out_id) < 0); + } } SECTION("algorithm behavior with 3")