Bug 1750791 - Part 1: Add LinkedList::mergeBack and LinkedList::splice. r=glandium

Differential Revision: https://phabricator.services.mozilla.com/D137244
This commit is contained in:
Toshihito Kikuchi 2022-02-24 15:00:27 +00:00
Родитель a7164ba192
Коммит 076c992786
2 изменённых файлов: 138 добавлений и 0 удалений

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

@ -340,6 +340,28 @@ class LinkedListElement {
Traits::enterList(aElem);
}
/*
* Transfers the elements [aBegin, aEnd) before the "this" list element.
*/
void transferBeforeUnsafe(LinkedListElement<T>& aBegin,
LinkedListElement<T>& aEnd) {
MOZ_RELEASE_ASSERT(!aBegin.mIsSentinel);
if (!aBegin.isInList() || !aEnd.isInList()) {
return;
}
auto otherPrev = aBegin.mPrev;
aBegin.mPrev = this->mPrev;
this->mPrev->mNext = &aBegin;
this->mPrev = aEnd.mPrev;
aEnd.mPrev->mNext = this;
// Patch the gap in the source list
otherPrev->mNext = &aEnd;
aEnd.mPrev = otherPrev;
}
/*
* Adjust mNext and mPrev for implementing move constructor and move
* assignment.
@ -459,6 +481,50 @@ class LinkedList {
*/
void insertBack(RawType aElem) { sentinel.setPreviousUnsafe(aElem); }
/*
* Move all elements from another list to the back
*/
void extendBack(LinkedList<T>&& aOther) {
MOZ_RELEASE_ASSERT(this != &aOther);
if (aOther.isEmpty()) {
return;
}
sentinel.transferBeforeUnsafe(**aOther.begin(), aOther.sentinel);
}
/*
* Move elements from another list to the specified position
*/
void splice(size_t aDestinationPos, LinkedList<T>& aListFrom,
size_t aSourceStart, size_t aSourceLen) {
MOZ_RELEASE_ASSERT(this != &aListFrom);
if (aListFrom.isEmpty() || !aSourceLen) {
return;
}
const auto safeForward = [](LinkedList<T>& aList,
LinkedListElement<T>& aBegin,
size_t aPos) -> LinkedListElement<T>& {
auto* iter = &aBegin;
for (size_t i = 0; i < aPos; ++i, (iter = iter->mNext)) {
if (iter->mIsSentinel) {
break;
}
}
return *iter;
};
auto& sourceBegin =
safeForward(aListFrom, *aListFrom.sentinel.mNext, aSourceStart);
if (sourceBegin.mIsSentinel) {
return;
}
auto& sourceEnd = safeForward(aListFrom, sourceBegin, aSourceLen);
auto& destination = safeForward(*this, *sentinel.mNext, aDestinationPos);
destination.transferBeforeUnsafe(sourceBegin, sourceEnd);
}
/*
* Get the first element of the list, or nullptr if the list is empty.
*/
@ -662,6 +728,8 @@ class AutoCleanLinkedList : public LinkedList<T> {
using ClientType = typename detail::LinkedListElementTraits<T>::ClientType;
public:
AutoCleanLinkedList() = default;
AutoCleanLinkedList(AutoCleanLinkedList&&) = default;
~AutoCleanLinkedList() { clear(); }
AutoCleanLinkedList& operator=(AutoCleanLinkedList&& aOther) = default;

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

@ -1,3 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
@ -7,12 +8,15 @@
#include "mozilla/Assertions.h"
#include "mozilla/LinkedList.h"
using mozilla::AutoCleanLinkedList;
using mozilla::LinkedList;
using mozilla::LinkedListElement;
struct SomeClass : public LinkedListElement<SomeClass> {
unsigned int mValue;
explicit SomeClass(int aValue = 0) : mValue(aValue) {}
SomeClass(SomeClass&&) = default;
SomeClass& operator=(SomeClass&&) = default;
void incr() { ++mValue; }
};
@ -173,6 +177,70 @@ static void TestList() {
}
}
static void TestExtendLists() {
AutoCleanLinkedList<SomeClass> list1, list2;
constexpr unsigned int N = 5;
for (unsigned int i = 0; i < N; ++i) {
list1.insertBack(new SomeClass(static_cast<int>(i)));
AutoCleanLinkedList<SomeClass> singleItemList;
singleItemList.insertFront(new SomeClass(static_cast<int>(i + N)));
list2.extendBack(std::move(singleItemList));
}
// list1 = { 0, 1, 2, 3, 4 }
// list2 = { 5, 6, 7, 8, 9 }
list1.extendBack(AutoCleanLinkedList<SomeClass>());
list1.extendBack(std::move(list2));
// Make sure the line above has properly emptied |list2|.
MOZ_RELEASE_ASSERT(list2.isEmpty()); // NOLINT(bugprone-use-after-move)
size_t i = 0;
for (SomeClass* x : list1) {
MOZ_RELEASE_ASSERT(x->mValue == i++);
}
MOZ_RELEASE_ASSERT(i == N * 2);
}
void TestSplice() {
AutoCleanLinkedList<SomeClass> list1, list2;
for (unsigned int i = 1; i <= 5; ++i) {
list1.insertBack(new SomeClass(static_cast<int>(i)));
AutoCleanLinkedList<SomeClass> singleItemList;
singleItemList.insertFront(new SomeClass(static_cast<int>(i * 10)));
list2.extendBack(std::move(singleItemList));
}
// list1 = { 1, 2, 3, 4, 5 }
// list2 = { 10, 20, 30, 40, 50 }
list1.splice(2, list2, 0, 5);
MOZ_RELEASE_ASSERT(list2.isEmpty());
unsigned int kExpected1[]{1, 2, 10, 20, 30, 40, 50, 3, 4, 5};
CheckListValues(list1, kExpected1);
// Since aSourceLen=100 exceeds list1's end, the function transfers
// three items [3, 4, 5].
list2.splice(0, list1, 7, 100);
unsigned int kExpected2[]{1, 2, 10, 20, 30, 40, 50};
unsigned int kExpected3[]{3, 4, 5};
CheckListValues(list1, kExpected2);
CheckListValues(list2, kExpected3);
// Since aDestinationPos=100 exceeds list2's end, the function transfers
// items to list2's end.
list2.splice(100, list1, 1, 1);
unsigned int kExpected4[]{1, 10, 20, 30, 40, 50};
unsigned int kExpected5[]{3, 4, 5, 2};
CheckListValues(list1, kExpected4);
CheckListValues(list2, kExpected5);
}
static void TestMove() {
auto MakeSomeClass = [](unsigned int aValue) -> SomeClass {
return SomeClass(aValue);
@ -321,6 +389,8 @@ static void TestRefPtrList() {
int main() {
TestList();
TestExtendLists();
TestSplice();
TestPrivate();
TestMove();
TestRemoveAndGet();