Bug 1431122 - Part 1: Add UnorderedRemoveElement[s]At to nsTArray, r=froydnj

MozReview-Commit-ID: H0oqG7H7299
This commit is contained in:
Nika Layzell 2018-01-19 18:18:15 -05:00
parent 281f61ed94
commit 0c171ce388
2 changed files with 140 additions and 0 deletions

View File

@ -273,6 +273,61 @@ nsTArray_base<Alloc, Copy>::ShiftData(index_type aStart,
}
}
template<class Alloc, class Copy>
template<typename ActualAlloc>
void
nsTArray_base<Alloc, Copy>::SwapFromEnd(index_type aStart,
size_type aCount,
size_type aElemSize,
size_t aElemAlign)
{
// This method is part of the implementation of
// nsTArray::SwapRemoveElement{s,}At. For more information, read the
// documentation on that method.
if (aCount == 0) {
return;
}
// We are going to be removing aCount elements. Update our length to point to
// the new end of the array.
size_type oldLength = mHdr->mLength;
mHdr->mLength -= aCount;
if (mHdr->mLength == 0) {
// If we have no elements remaining in the array, we can free our buffer.
ShrinkCapacity(aElemSize, aElemAlign);
return;
}
// Determine how many elements we need to move from the end of the array into
// the now-removed section. This will either be the number of elements which
// were removed (if there are more elements in the tail of the array), or the
// entire tail of the array, whichever is smaller.
size_type relocCount = std::min(aCount, mHdr->mLength - aStart);
if (relocCount == 0) {
return;
}
// Move the elements which are now stranded after the end of the array back
// into the now-vacated memory.
index_type sourceBytes = (oldLength - relocCount) * aElemSize;
index_type destBytes = aStart * aElemSize;
// Perform the final copy. This is guaranteed to be a non-overlapping copy
// as our source contains only still-valid entries, and the destination
// contains only invalid entries which need to be overwritten.
MOZ_ASSERT(sourceBytes >= destBytes,
"The source should be after the destination.");
MOZ_ASSERT(sourceBytes - destBytes >= relocCount * aElemSize,
"The range should be nonoverlapping");
char* baseAddr = reinterpret_cast<char*>(mHdr + 1);
Copy::MoveNonOverlappingRegion(baseAddr + destBytes,
baseAddr + sourceBytes,
relocCount,
aElemSize);
}
template<class Alloc, class Copy>
template<typename ActualAlloc>
bool

View File

@ -410,6 +410,17 @@ protected:
void ShiftData(index_type aStart, size_type aOldLen, size_type aNewLen,
size_type aElemSize, size_t aElemAlign);
// This method may be called to swap elements from the end of the array to
// fill a "gap" in the array. If the resulting array has zero elements, then
// the array's memory is free'd.
// @param aStart The starting index of the gap.
// @param aCount The length of the gap.
// @param aElemSize The size of an array element.
// @param aElemAlign The alignment in bytes of an array element.
template<typename ActualAlloc>
void SwapFromEnd(index_type aStart, size_type aCount,
size_type aElemSize, size_t aElemAlign);
// This method increments the length member of the array's header.
// Note that mHdr may actually be sEmptyHdr in the case where a
// zero-length array is inserted into our array. But then aNum should
@ -1717,6 +1728,58 @@ public:
// A variation on the RemoveElementsAt method defined above.
void RemoveElementAt(index_type aIndex) { RemoveElementsAt(aIndex, 1); }
// This method performs index-based removals from an array without preserving
// the order of the array. This is useful if you are using the array as a
// set-like data structure.
//
// These removals are efficient, as they move as few elements as possible. At
// most N elements, where N is the number of removed elements, will have to
// be relocated.
//
// ## Examples
//
// When removing an element from the end of the array, it can be removed in
// place, by destroying it and decrementing the length.
//
// [ 1, 2, 3 ] => [ 1, 2 ]
// ^
//
// When removing any other single element, it is removed by swapping it with
// the last element, and then decrementing the length as before.
//
// [ 1, 2, 3, 4, 5, 6 ] => [ 1, 6, 3, 4, 5 ]
// ^
//
// This method also supports efficiently removing a range of elements. If they
// are at the end, then they can all be removed like in the one element case.
//
// [ 1, 2, 3, 4, 5, 6 ] => [ 1, 2 ]
// ^--------^
//
// If more elements are removed than exist after the removed section, the
// remaining elements will be shifted down like in a normal removal.
//
// [ 1, 2, 3, 4, 5, 6, 7, 8 ] => [ 1, 2, 7, 8 ]
// ^--------^
//
// And if fewer elements are removed than exist after the removed section,
// elements will be moved from the end of the array to fill the vacated space.
//
// [ 1, 2, 3, 4, 5, 6, 7, 8 ] => [ 1, 7, 8, 4, 5, 6 ]
// ^--^
//
// @param aStart The starting index of the elements to remove. @param aCount
// The number of elements to remove.
void UnorderedRemoveElementsAt(index_type aStart, size_type aCount);
// A variation on the UnorderedRemoveElementsAt method defined above to remove
// a single element. This operation is sometimes called `SwapRemove`.
//
// This method is O(1), but does not preserve the order of the elements.
void UnorderedRemoveElementAt(index_type aIndex) {
UnorderedRemoveElementsAt(aIndex, 1);
}
void Clear() {
ClearAndRetainStorage();
Compact();
@ -2053,6 +2116,28 @@ nsTArray_Impl<E, Alloc>::RemoveElementsAt(index_type aStart, size_type aCount)
MOZ_ALIGNOF(elem_type));
}
template<typename E, class Alloc>
void
nsTArray_Impl<E, Alloc>::UnorderedRemoveElementsAt(index_type aStart, size_type aCount)
{
MOZ_ASSERT(aCount == 0 || aStart < Length(), "Invalid aStart index");
mozilla::CheckedInt<index_type> rangeEnd = aStart;
rangeEnd += aCount;
if (MOZ_UNLIKELY(!rangeEnd.isValid() || rangeEnd.value() > Length())) {
InvalidArrayIndex_CRASH(aStart, Length());
}
// Destroy the elements which are being removed, and then swap elements in to
// replace them from the end. See the docs on the declaration of this
// function.
DestructRange(aStart, aCount);
this->template SwapFromEnd<InfallibleAlloc>(aStart, aCount,
sizeof(elem_type),
MOZ_ALIGNOF(elem_type));
}
template<typename E, class Alloc>
template<typename Predicate>
void