/* -*- 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** A doubly-linked list with flexible next/prev naming. */
/** * Where mozilla::LinkedList strives for ease of use above all other * considerations, mozilla::DoublyLinkedList strives for flexibility. The * following are things that can be done with mozilla::DoublyLinkedList that * cannot be done with mozilla::LinkedList: * * * Arbitrary next/prev placement and naming. With the tools provided here, * the next and previous pointers can be at the end of the structure, in a * sub-structure, stored with a tag, in a union, wherever, as long as you * can look them up and set them on demand. * * Can be used without deriving from a new base and, thus, does not require * use of constructors. * * Example: * * class Observer : public DoublyLinkedListElement<Observer> * { * public: * void observe(char* aTopic) { ... } * }; * * class ObserverContainer * { * private: * DoublyLinkedList<Observer> mList; * * public: * void addObserver(Observer* aObserver) * { * // Will assert if |aObserver| is part of another list. * mList.pushBack(aObserver); * } * * void removeObserver(Observer* aObserver) * { * // Will assert if |aObserver| is not part of |list|. * mList.remove(aObserver); * } * * void notifyObservers(char* aTopic) * { * for (Observer* o : mList) { * o->observe(aTopic); * } * } * };
*/
namespace mozilla {
/** * Deriving from this will allow T to be inserted into and removed from a * DoublyLinkedList.
*/ template <typename T> class DoublyLinkedListElement { template <typename U, typename E> friendclass DoublyLinkedList; friend T;
T* mNext;
T* mPrev;
/** * Provides access to a DoublyLinkedListElement within T. * * The default implementation of this template works for types that derive * from DoublyLinkedListElement, but one can specialize for their class so * that some appropriate DoublyLinkedListElement reference is returned. * * For more complex cases (multiple DoublyLinkedListElements, for example), * one can define their own trait class and use that as ElementAccess for * DoublyLinkedList. See TestDoublyLinkedList.cpp for an example.
*/ template <typename T> struct GetDoublyLinkedListElement {
static_assert(std::is_base_of<DoublyLinkedListElement<T>, T>::value, "You need your own specialization of GetDoublyLinkedListElement" " or use a separate Trait."); static DoublyLinkedListElement<T>& Get(T* aThis) { return *aThis; }
};
/** * A doubly linked list. |T| is the type of element stored in this list. |T| * must contain or have access to unique next and previous element pointers. * The template argument |ElementAccess| provides code to tell this list how to * get a reference to a DoublyLinkedListElement that may reside anywhere.
*/ template <typename T, typename ElementAccess = GetDoublyLinkedListElement<T>> class DoublyLinkedList final {
T* mHead;
T* mTail;
/** * Checks that either the list is empty and both mHead and mTail are nullptr * or the list has entries and both mHead and mTail are non-null.
*/ bool isStateValid() const { return (mHead != nullptr) == (mTail != nullptr); }
bool ElementNotInList(T* aElm) { if (!ElementAccess::Get(aElm).mNext && !ElementAccess::Get(aElm).mPrev) { // Both mNext and mPrev being NULL can mean two things: // - the element is not in the list. // - the element is the first and only element in the list. // So check for the latter. return mHead != aElm;
} returnfalse;
}
public: using iterator_category = std::forward_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; using pointer = T*; using reference = T&;
/** * Returns true if the list contains no elements.
*/ bool isEmpty() const {
MOZ_ASSERT(isStateValid()); return mHead == nullptr;
}
/** * Inserts aElm into the list at the head position. |aElm| must not already * be in a list.
*/ void pushFront(T* aElm) {
MOZ_ASSERT(aElm);
MOZ_ASSERT(ElementNotInList(aElm));
MOZ_ASSERT(isStateValid());
/** * Remove the head of the list and return it. Calling this on an empty list * will assert.
*/
T* popFront() {
MOZ_ASSERT(!isEmpty());
MOZ_ASSERT(isStateValid());
T* result = mHead;
mHead = result ? ElementAccess::Get(result).mNext : nullptr; if (mHead) {
ElementAccess::Get(mHead).mPrev = nullptr;
}
if (mTail == result) {
mTail = nullptr;
}
if (result) {
ElementAccess::Get(result).mNext = nullptr;
ElementAccess::Get(result).mPrev = nullptr;
}
return result;
}
/** * Inserts aElm into the list at the tail position. |aElm| must not already * be in a list.
*/ void pushBack(T* aElm) {
MOZ_ASSERT(aElm);
MOZ_ASSERT(ElementNotInList(aElm));
MOZ_ASSERT(isStateValid());
/** * Remove the tail of the list and return it. Calling this on an empty list * will assert.
*/
T* popBack() {
MOZ_ASSERT(!isEmpty());
MOZ_ASSERT(isStateValid());
T* result = mTail;
mTail = result ? ElementAccess::Get(result).mPrev : nullptr; if (mTail) {
ElementAccess::Get(mTail).mNext = nullptr;
}
if (mHead == result) {
mHead = nullptr;
}
if (result) {
ElementAccess::Get(result).mNext = nullptr;
ElementAccess::Get(result).mPrev = nullptr;
}
return result;
}
/** * Insert the given |aElm| *before* |aIter|.
*/ void insertBefore(const Iterator& aIter, T* aElm) {
MOZ_ASSERT(aElm);
MOZ_ASSERT(ElementNotInList(aElm));
MOZ_ASSERT(isStateValid());
/** * Removes the given element from the list. The element must be in this list.
*/ void remove(T* aElm) {
MOZ_ASSERT(aElm);
MOZ_ASSERT(ElementAccess::Get(aElm).mNext ||
ElementAccess::Get(aElm).mPrev ||
(aElm == mHead && aElm == mTail), "Attempted to remove element not in this list");
/** * Returns an iterator referencing the first found element whose value matches * the given element according to operator==.
*/
Iterator find(const T& aElm) { return std::find(begin(), end(), aElm); }
/** * Returns whether the given element is in the list. Note that this uses * T::operator==, not pointer comparison.
*/ bool contains(const T& aElm) { return find(aElm) != Iterator(); }
/** * Returns whether the given element might be in the list. Note that this * assumes the element is either in the list or not in the list, and ignores * the case where the element might be in another list in order to make the * check fast.
*/ bool ElementProbablyInList(T* aElm) { if (isEmpty()) { returnfalse;
} return !ElementNotInList(aElm);
}
};
/** * @brief Double linked list that allows insertion/removal during iteration. * * This class uses the mozilla::DoublyLinkedList internally and keeps * track of created iterator instances by putting them on a simple list on stack * (compare nsTAutoObserverArray). * This allows insertion or removal operations to adjust iterators and therefore * keeping them valid during iteration.
*/ template <typename T, typename ElementAccess = GetDoublyLinkedListElement<T>> class SafeDoublyLinkedList { public: /** * @brief Iterator class for SafeDoublyLinkedList. * * The iterator contains two iterators of the underlying list: * - mCurrent points to the current list element of the iterator. * - mNext points to the next element of the list. * * When removing an element from the list, mCurrent and mNext may * be adjusted: * - If mCurrent is the element to be deleted, it is set to empty. mNext can * still be used to advance to the next element. * - If mNext is the element to be deleted, it is set to its next element * (or to empty if mNext is the last element of the list).
*/ class SafeIterator { using BaseIterator = typename DoublyLinkedList<T, ElementAccess>::Iterator; friendclass SafeDoublyLinkedList<T, ElementAccess>;
public: using iterator_category = std::forward_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&;
SafeIterator(BaseIterator aBaseIter,
SafeDoublyLinkedList<T, ElementAccess>* aList)
: mCurrent(aBaseIter),
mNext(aBaseIter ? ++aBaseIter : BaseIterator()),
mList(aList) { if (mList) {
mNextIterator = mList->mIter;
mList->mIter = this;
}
}
~SafeIterator() { if (mList) {
MOZ_ASSERT(mList->mIter == this, "Iterators must currently be destroyed in opposite order " "from the construction order. It is suggested that you " "simply put them on the stack");
mList->mIter = mNextIterator;
}
}
BaseIterator& next() { return mNext; } // mainly needed for unittests. private: /** * Base list iterator pointing to the current list element of the iteration. * If element mCurrent points to gets removed, the iterator will be set to * empty. mNext keeps the iterator valid.
*/
BaseIterator mCurrent{nullptr}; /** * Base list iterator pointing to the next list element of the iteration. * If element mCurrent points to gets removed, mNext is still valid. * If element mNext points to gets removed, mNext advances, keeping this * iterator valid.
*/
BaseIterator mNext{nullptr};
/** * Next element in the stack-allocated list of iterators stored in the * SafeLinkedList object.
*/
SafeIterator* mNextIterator{nullptr};
SafeDoublyLinkedList<T, ElementAccess>* mList{nullptr};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.