From 0c634edef5467fcc7721f13e44c8f538954169dc Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 19 Aug 2023 13:47:14 +0100 Subject: [PATCH] Ring buffer: Add erase --- src/core/ring_buffer.hpp | 63 ++++++++++++++++++++++++++ src/tests/ring_buffer.cpp | 94 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) diff --git a/src/core/ring_buffer.hpp b/src/core/ring_buffer.hpp index 253bd362d2..b6db39dac3 100644 --- a/src/core/ring_buffer.hpp +++ b/src/core/ring_buffer.hpp @@ -652,6 +652,69 @@ public: return this->insert(pos, values.begin(), values.end()); } +private: + uint32 do_erase(uint32 pos, uint32 num) + { + if (pos == this->head) { + /* erase from beginning */ + for (uint32 i = 0; i < num; i++) { + this->ptr_at_pos(pos + i)->~T(); + } + this->head += num; + this->count -= num; + return this->head; + } else if (pos + num == this->head + this->count) { + /* erase from end */ + for (uint32 i = 0; i < num; i++) { + this->ptr_at_pos(pos + i)->~T(); + } + this->count -= num; + return pos; + } else if (pos - this->head < this->head + this->count - (pos + num)) { + /* closer to the beginning, shuffle beginning forwards to fill gap */ + const uint32 new_head = this->head + num; + const uint32 erase_end = pos + num; + for (uint32 idx = erase_end - 1; idx != new_head - 1; idx--) { + *this->ptr_at_pos(idx) = std::move(*this->ptr_at_pos(idx - num)); + } + for (uint32 idx = new_head - 1; idx != this->head - 1; idx--) { + this->ptr_at_pos(idx)->~T(); + } + this->head = new_head; + this->count -= num; + return pos + num; + } else { + /* closer to the end, shuffle end backwards to fill gap */ + const uint32 current_end = this->head + this->count; + const uint32 new_end = current_end - num; + for (uint32 idx = pos; idx != new_end; idx++) { + *this->ptr_at_pos(idx) = std::move(*this->ptr_at_pos(idx + num)); + } + for (uint32 idx = new_end; idx != current_end; idx++) { + this->ptr_at_pos(idx)->~T(); + } + this->count -= num; + return pos; + } + } + +public: + iterator erase(ring_buffer_iterator_base pos) + { + dbg_assert(pos.ring == this); + + return iterator(this, this->do_erase(pos.pos, 1)); + } + + iterator erase(ring_buffer_iterator_base first, ring_buffer_iterator_base last) + { + if (first.ring == last.ring && first.pos == last.pos) return last; + + dbg_assert(first.ring == this && last.ring == this); + + return iterator(this, this->do_erase(first.pos, last.pos - first.pos)); + } + void reserve(size_t new_cap) { if (new_cap <= this->capacity()) return; diff --git a/src/tests/ring_buffer.cpp b/src/tests/ring_buffer.cpp index 3a1d35e104..5075af538b 100644 --- a/src/tests/ring_buffer.cpp +++ b/src/tests/ring_buffer.cpp @@ -353,6 +353,100 @@ TEST_CASE("RingBuffer - insert multi in middle") CHECK(iter == ring.begin() + 2); } +TEST_CASE("RingBuffer - erase") +{ + ring_buffer ring; + auto setup_ring = [&]() { + ring = ring_buffer({ 3, 4, 5, 6, 7, 8 }); + ring.push_front(2); + ring.push_front(1); + CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 8 })); + CHECK(ring.capacity() == 8); + }; + + setup_ring(); + uint32 *expect_front = &ring[1]; + auto iter = ring.erase(ring.begin()); + CHECK(Matches(ring, { 2, 3, 4, 5, 6, 7, 8 })); + CHECK(ring.capacity() == 8); + CHECK(iter == ring.begin()); + CHECK(expect_front == &ring[0]); + + setup_ring(); + uint32 *expect_back = &ring[ring.size() - 2]; + iter = ring.erase(ring.end() - 1); + CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7 })); + CHECK(ring.capacity() == 8); + CHECK(iter == ring.end()); + CHECK(expect_back == &ring[ring.size() - 1]); + + setup_ring(); + expect_front = &ring[1]; + iter = ring.erase(ring.begin() + 2); + CHECK(Matches(ring, { 1, 2, 4, 5, 6, 7, 8 })); + CHECK(ring.capacity() == 8); + CHECK(iter == ring.begin() + 2); + CHECK(expect_front == &ring[0]); + + setup_ring(); + expect_back = &ring[ring.size() - 2]; + iter = ring.erase(ring.end() - 3); + CHECK(Matches(ring, { 1, 2, 3, 4, 5, 7, 8 })); + CHECK(ring.capacity() == 8); + CHECK(iter == ring.end() - 2); + CHECK(expect_back == &ring[ring.size() - 1]); +} + +TEST_CASE("RingBuffer - erase multi") +{ + ring_buffer ring; + auto setup_ring = [&]() { + ring = ring_buffer({ 3, 4, 5, 6, 7, 8 }); + ring.push_front(2); + ring.push_front(1); + CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 8 })); + CHECK(ring.capacity() == 8); + }; + + setup_ring(); + uint32 *expect_front = &ring[2]; + auto iter = ring.erase(ring.begin(), ring.begin() + 2); + CHECK(Matches(ring, { 3, 4, 5, 6, 7, 8 })); + CHECK(ring.capacity() == 8); + CHECK(iter == ring.begin()); + CHECK(expect_front == &ring[0]); + + setup_ring(); + uint32 *expect_back = &ring[ring.size() - 3]; + iter = ring.erase(ring.end() - 2, ring.end()); + CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6 })); + CHECK(ring.capacity() == 8); + CHECK(iter == ring.end()); + CHECK(expect_back == &ring[ring.size() - 1]); + + setup_ring(); + expect_front = &ring[2]; + iter = ring.erase(ring.begin() + 2, ring.begin() + 4); + CHECK(Matches(ring, { 1, 2, 5, 6, 7, 8 })); + CHECK(ring.capacity() == 8); + CHECK(iter == ring.begin() + 2); + CHECK(expect_front == &ring[0]); + + setup_ring(); + expect_back = &ring[ring.size() - 3]; + iter = ring.erase(ring.end() - 4, ring.end() - 2); + CHECK(Matches(ring, { 1, 2, 3, 4, 7, 8 })); + CHECK(ring.capacity() == 8); + CHECK(iter == ring.end() - 2); + CHECK(expect_back == &ring[ring.size() - 1]); + + setup_ring(); + iter = ring.erase(ring.begin() + 1, ring.end() - 1); + CHECK(Matches(ring, { 1, 8 })); + CHECK(ring.capacity() == 8); + CHECK(iter == ring.begin() + 1); +} + TEST_CASE("RingBuffer - shrink to fit") { ring_buffer ring({ 3, 4, 5, 6, 7, 8 });