diff --git a/AK/Vector.h b/AK/Vector.h index d192e87273..be96967b7a 100644 --- a/AK/Vector.h +++ b/AK/Vector.h @@ -503,6 +503,44 @@ public: update_metadata(); } + /// The iterator pair identify a set of indices *in ascending order* to be removed. + template EndIt, typename ToIndex = decltype (*declval()) (*)(It const&)> + void remove_all(It&& it, EndIt const& end, ToIndex const& to_index = [](It const& it) -> decltype(auto) { return *it; }) + { + if (!(it != end)) + return; + size_t write_index = to_index(it); + VERIFY(write_index < m_size); + raw_at(write_index).~StorageType(); + ++it; + size_t next_remove_index = it != end ? to_index(it) : m_size; + for (auto read_index = write_index + 1; read_index < m_size; ++read_index) { + if (read_index == next_remove_index) { + raw_at(read_index).~StorageType(); + ++it; + next_remove_index = it != end ? to_index(it) : m_size; + } else { + if constexpr (Traits::is_trivial()) { + __builtin_memcpy(slot(write_index), slot(read_index), sizeof(StorageType)); + } else { + new (slot(write_index)) StorageType(move(raw_at(read_index))); + raw_at(read_index).~StorageType(); + } + ++write_index; + } + } + + VERIFY(!(it != end)); + m_size = write_index; + update_metadata(); + } + + template().begin()) (*)(decltype(declval().begin()) const&)> + void remove_all(Is&& is, ToIndex const& to_index = [](auto const& it) -> decltype(auto) { return *it; }) + { + remove_all(is.begin(), is.end(), to_index); + } + template bool remove_first_matching(TUnaryPredicate const& predicate) { diff --git a/Tests/AK/TestVector.cpp b/Tests/AK/TestVector.cpp index bd5aba021d..c779d673cc 100644 --- a/Tests/AK/TestVector.cpp +++ b/Tests/AK/TestVector.cpp @@ -643,6 +643,57 @@ static bool is_inline_element(auto& el, auto& vector) \ for (auto& el : v) \ EXPECT(is_inline_element(el, v)); \ + } \ + TEST_CASE(Vector##_remove_all) \ + { \ + { \ + Vector v0 { 1, 2, 3, 2, 4, 2, 5 }; \ + Array indices { 1, 4, 6 }; \ + v0.remove_all(indices); \ + EXPECT_EQ(v0.size(), 4u); \ + EXPECT_EQ(v0[0], 1); \ + EXPECT_EQ(v0[1], 3); \ + EXPECT_EQ(v0[2], 2); \ + EXPECT_EQ(v0[3], 2); \ + } \ + { \ + Vector v1 { 1, 2, 3, 4, 5 }; \ + Array indices { 0, 1, 2, 3, 4 }; \ + v1.remove_all(indices); \ + EXPECT_EQ(v1.size(), 0u); \ + } \ + { \ + Vector v2 { 1, 2, 3, 4, 5 }; \ + Array indices; \ + v2.remove_all(indices); \ + EXPECT_EQ(v2.size(), 5u); \ + EXPECT_EQ(v2[0], 1); \ + EXPECT_EQ(v2[1], 2); \ + EXPECT_EQ(v2[2], 3); \ + EXPECT_EQ(v2[3], 4); \ + EXPECT_EQ(v2[4], 5); \ + } \ + { \ + Vector v3; \ + Array indices; \ + v3.remove_all(indices); \ + EXPECT_EQ(v3.size(), 0u); \ + } \ + /* One more test with a nonstandard iterator deref function */ \ + { \ + Vector v4 { 1, 2, 3, 2, 4, 2, 5 }; \ + struct IndexWrapper { \ + size_t index; \ + size_t const& operator*() const { return index; } \ + }; \ + Array indices { IndexWrapper { 1 }, IndexWrapper { 4 }, IndexWrapper { 6 } }; \ + v4.remove_all(indices, [](auto const& it) -> size_t { return **it; }); \ + EXPECT_EQ(v4.size(), 4u); \ + EXPECT_EQ(v4[0], 1); \ + EXPECT_EQ(v4[1], 3); \ + EXPECT_EQ(v4[2], 2); \ + EXPECT_EQ(v4[3], 2); \ + } \ } DECLARE_TESTS_FOR_VEC(Vector)