Bug 1539322 - Add Vector::eraseIf and Vector::eraseIfEqual - r=froydnj

Convenience functions to erase elements based on a predicate, or by comparing to
a value. They are optimized to use the minimal amount of moves necessary.

Differential Revision: https://phabricator.services.mozilla.com/D25016

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Gerald Squelart 2019-03-28 01:00:35 +00:00
Родитель d2abc5343d
Коммит d8e4582b6c
2 изменённых файлов: 129 добавлений и 0 удалений

Просмотреть файл

@ -800,6 +800,20 @@ class MOZ_NON_PARAM Vector final : private AllocPolicy {
*/
void erase(T* aBegin, T* aEnd);
/**
* Removes all elements that satisfy the predicate, shifting existing elements
* lower to fill erased gaps.
*/
template <typename Pred>
void eraseIf(Pred aPred);
/**
* Removes all elements that compare equal to |aU|, shifting existing elements
* lower to fill erased gaps.
*/
template <typename U>
void eraseIfEqual(const U& aU);
/**
* Measure the size of the vector's heap-allocated storage.
*/
@ -1285,6 +1299,26 @@ inline void Vector<T, N, AP>::erase(T* aBegin, T* aEnd) {
shrinkBy(aEnd - aBegin);
}
template <typename T, size_t N, class AP>
template <typename Pred>
void Vector<T, N, AP>::eraseIf(Pred aPred) {
// remove_if finds the first element to be erased, and then efficiently move-
// assigns elements to effectively overwrite elements that satisfy the
// predicate. It returns the new end pointer, after which there are only
// moved-from elements ready to be destroyed, so we just need to shrink the
// vector accordingly.
T* newEnd = std::remove_if(begin(), end(),
[&aPred](const T& aT) { return aPred(aT); });
MOZ_ASSERT(newEnd <= end());
shrinkBy(end() - newEnd);
}
template <typename T, size_t N, class AP>
template <typename U>
void Vector<T, N, AP>::eraseIfEqual(const U& aU) {
return eraseIf([&aU](const T& aT) { return aT == aU; });
}
template <typename T, size_t N, class AP>
template <typename U>
MOZ_ALWAYS_INLINE bool Vector<T, N, AP>::append(const U* aInsBegin,

Просмотреть файл

@ -22,6 +22,7 @@ struct mozilla::detail::VectorTesting {
static void testExtractOrCopyRawBuffer();
static void testReplaceRawBuffer();
static void testInsert();
static void testErase();
static void testPodResizeToFit();
};
@ -136,6 +137,8 @@ struct S {
return *this;
}
bool operator==(const S& rhs) const { return j == rhs.j && *k == *rhs.k; }
S(const S&) = delete;
S& operator=(const S&) = delete;
};
@ -437,6 +440,97 @@ void mozilla::detail::VectorTesting::testInsert() {
MOZ_RELEASE_ASSERT(S::destructCount == 1);
}
void mozilla::detail::VectorTesting::testErase() {
S::resetCounts();
Vector<S, 8> vec;
MOZ_RELEASE_ASSERT(vec.reserve(8));
for (size_t i = 0; i < 7; i++) {
vec.infallibleEmplaceBack(i, i * i);
}
// vec: [0, 1, 2, 3, 4, 5, 6]
MOZ_RELEASE_ASSERT(vec.length() == 7);
MOZ_ASSERT(vec.reserved() == 8);
MOZ_RELEASE_ASSERT(S::constructCount == 7);
MOZ_RELEASE_ASSERT(S::moveCount == 0);
MOZ_RELEASE_ASSERT(S::destructCount == 0);
S::resetCounts();
vec.erase(&vec[4]);
// vec: [0, 1, 2, 3, 5, 6]
MOZ_RELEASE_ASSERT(vec.length() == 6);
MOZ_ASSERT(vec.reserved() == 8);
MOZ_RELEASE_ASSERT(S::constructCount == 0);
// 5 and 6 should have been moved into 4 and 5.
MOZ_RELEASE_ASSERT(S::moveCount == 2);
MOZ_RELEASE_ASSERT(S::destructCount == 1);
MOZ_RELEASE_ASSERT(vec[4] == S(5, 5 * 5));
MOZ_RELEASE_ASSERT(vec[5] == S(6, 6 * 6));
S::resetCounts();
vec.erase(&vec[3], &vec[5]);
// vec: [0, 1, 2, 6]
MOZ_RELEASE_ASSERT(vec.length() == 4);
MOZ_ASSERT(vec.reserved() == 8);
MOZ_RELEASE_ASSERT(S::constructCount == 0);
// 6 should have been moved into 3.
MOZ_RELEASE_ASSERT(S::moveCount == 1);
MOZ_RELEASE_ASSERT(S::destructCount == 2);
MOZ_RELEASE_ASSERT(vec[3] == S(6, 6 * 6));
S s2(2, 2 * 2);
S::resetCounts();
vec.eraseIfEqual(s2);
// vec: [0, 1, 6]
MOZ_RELEASE_ASSERT(vec.length() == 3);
MOZ_ASSERT(vec.reserved() == 8);
MOZ_RELEASE_ASSERT(S::constructCount == 0);
// 6 should have been moved into 2.
MOZ_RELEASE_ASSERT(S::moveCount == 1);
MOZ_RELEASE_ASSERT(S::destructCount == 1);
MOZ_RELEASE_ASSERT(vec[2] == S(6, 6 * 6));
S::resetCounts();
// Predicate to find one element.
vec.eraseIf([](const S& s) { return s.j == 1; });
// vec: [0, 6]
MOZ_RELEASE_ASSERT(vec.length() == 2);
MOZ_ASSERT(vec.reserved() == 8);
MOZ_RELEASE_ASSERT(S::constructCount == 0);
// 6 should have been moved into 1.
MOZ_RELEASE_ASSERT(S::moveCount == 1);
MOZ_RELEASE_ASSERT(S::destructCount == 1);
MOZ_RELEASE_ASSERT(vec[1] == S(6, 6 * 6));
S::resetCounts();
// Generic predicate that flags everything.
vec.eraseIf([](auto&&) { return true; });
// vec: []
MOZ_RELEASE_ASSERT(vec.length() == 0);
MOZ_ASSERT(vec.reserved() == 8);
MOZ_RELEASE_ASSERT(S::constructCount == 0);
MOZ_RELEASE_ASSERT(S::moveCount == 0);
MOZ_RELEASE_ASSERT(S::destructCount == 2);
for (size_t i = 0; i < 7; i++) {
vec.infallibleEmplaceBack(i, i * i);
}
// vec: [0, 1, 2, 3, 4, 5, 6]
MOZ_RELEASE_ASSERT(vec.length() == 7);
S::resetCounts();
// Predicate that flags all even numbers.
vec.eraseIf([](const S& s) { return s.j % 2 == 0; });
// vec: [1 (was 0), 3 (was 1), 5 (was 2)]
MOZ_RELEASE_ASSERT(vec.length() == 3);
MOZ_ASSERT(vec.reserved() == 8);
MOZ_RELEASE_ASSERT(S::constructCount == 0);
MOZ_RELEASE_ASSERT(S::moveCount == 3);
MOZ_RELEASE_ASSERT(S::destructCount == 4);
}
void mozilla::detail::VectorTesting::testPodResizeToFit() {
// Vectors not using inline storage realloc capacity to exact length.
Vector<int, 0> v1;
@ -576,6 +670,7 @@ int main() {
VectorTesting::testExtractOrCopyRawBuffer();
VectorTesting::testReplaceRawBuffer();
VectorTesting::testInsert();
VectorTesting::testErase();
VectorTesting::testPodResizeToFit();
TestVectorBeginNonNull();
}