1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /** A doubly-linked list with flexible next/prev naming. */
9 #ifndef mozilla_DoublyLinkedList_h
10 #define mozilla_DoublyLinkedList_h
15 #include <type_traits>
17 #include "mozilla/Assertions.h"
20 * Where mozilla::LinkedList strives for ease of use above all other
21 * considerations, mozilla::DoublyLinkedList strives for flexibility. The
22 * following are things that can be done with mozilla::DoublyLinkedList that
23 * cannot be done with mozilla::LinkedList:
25 * * Arbitrary next/prev placement and naming. With the tools provided here,
26 * the next and previous pointers can be at the end of the structure, in a
27 * sub-structure, stored with a tag, in a union, wherever, as long as you
28 * can look them up and set them on demand.
29 * * Can be used without deriving from a new base and, thus, does not require
30 * use of constructors.
34 * class Observer : public DoublyLinkedListElement<Observer>
37 * void observe(char* aTopic) { ... }
40 * class ObserverContainer
43 * DoublyLinkedList<Observer> mList;
46 * void addObserver(Observer* aObserver)
48 * // Will assert if |aObserver| is part of another list.
49 * mList.pushBack(aObserver);
52 * void removeObserver(Observer* aObserver)
54 * // Will assert if |aObserver| is not part of |list|.
55 * mList.remove(aObserver);
58 * void notifyObservers(char* aTopic)
60 * for (Observer* o : mList) {
70 * Deriving from this will allow T to be inserted into and removed from a
74 class DoublyLinkedListElement
{
75 template <typename U
, typename E
>
76 friend class DoublyLinkedList
;
82 DoublyLinkedListElement() : mNext(nullptr), mPrev(nullptr) {}
86 * Provides access to a DoublyLinkedListElement within T.
88 * The default implementation of this template works for types that derive
89 * from DoublyLinkedListElement, but one can specialize for their class so
90 * that some appropriate DoublyLinkedListElement reference is returned.
92 * For more complex cases (multiple DoublyLinkedListElements, for example),
93 * one can define their own trait class and use that as ElementAccess for
94 * DoublyLinkedList. See TestDoublyLinkedList.cpp for an example.
97 struct GetDoublyLinkedListElement
{
98 static_assert(std::is_base_of
<DoublyLinkedListElement
<T
>, T
>::value
,
99 "You need your own specialization of GetDoublyLinkedListElement"
100 " or use a separate Trait.");
101 static DoublyLinkedListElement
<T
>& Get(T
* aThis
) { return *aThis
; }
105 * A doubly linked list. |T| is the type of element stored in this list. |T|
106 * must contain or have access to unique next and previous element pointers.
107 * The template argument |ElementAccess| provides code to tell this list how to
108 * get a reference to a DoublyLinkedListElement that may reside anywhere.
110 template <typename T
, typename ElementAccess
= GetDoublyLinkedListElement
<T
>>
111 class DoublyLinkedList final
{
116 * Checks that either the list is empty and both mHead and mTail are nullptr
117 * or the list has entries and both mHead and mTail are non-null.
119 bool isStateValid() const { return (mHead
!= nullptr) == (mTail
!= nullptr); }
121 bool ElementNotInList(T
* aElm
) {
122 if (!ElementAccess::Get(aElm
).mNext
&& !ElementAccess::Get(aElm
).mPrev
) {
123 // Both mNext and mPrev being NULL can mean two things:
124 // - the element is not in the list.
125 // - the element is the first and only element in the list.
126 // So check for the latter.
127 return mHead
!= aElm
;
133 DoublyLinkedList() : mHead(nullptr), mTail(nullptr) {}
135 class Iterator final
{
139 using iterator_category
= std::forward_iterator_tag
;
140 using value_type
= T
;
141 using difference_type
= std::ptrdiff_t;
143 using reference
= T
&;
145 Iterator() : mCurrent(nullptr) {}
146 explicit Iterator(T
* aCurrent
) : mCurrent(aCurrent
) {}
148 T
& operator*() const { return *mCurrent
; }
149 T
* operator->() const { return mCurrent
; }
151 Iterator
& operator++() {
152 mCurrent
= mCurrent
? ElementAccess::Get(mCurrent
).mNext
: nullptr;
156 Iterator
operator++(int) {
157 Iterator result
= *this;
162 Iterator
& operator--() {
163 mCurrent
= ElementAccess::Get(mCurrent
).mPrev
;
167 Iterator
operator--(int) {
168 Iterator result
= *this;
173 bool operator!=(const Iterator
& aOther
) const {
174 return mCurrent
!= aOther
.mCurrent
;
177 bool operator==(const Iterator
& aOther
) const {
178 return mCurrent
== aOther
.mCurrent
;
181 explicit operator bool() const { return mCurrent
; }
184 Iterator
begin() { return Iterator(mHead
); }
185 const Iterator
begin() const { return Iterator(mHead
); }
186 const Iterator
cbegin() const { return Iterator(mHead
); }
188 Iterator
end() { return Iterator(); }
189 const Iterator
end() const { return Iterator(); }
190 const Iterator
cend() const { return Iterator(); }
193 * Returns true if the list contains no elements.
195 bool isEmpty() const {
196 MOZ_ASSERT(isStateValid());
197 return mHead
== nullptr;
201 * Inserts aElm into the list at the head position. |aElm| must not already
204 void pushFront(T
* aElm
) {
206 MOZ_ASSERT(ElementNotInList(aElm
));
207 MOZ_ASSERT(isStateValid());
209 ElementAccess::Get(aElm
).mNext
= mHead
;
211 MOZ_ASSERT(!ElementAccess::Get(mHead
).mPrev
);
212 ElementAccess::Get(mHead
).mPrev
= aElm
;
222 * Remove the head of the list and return it. Calling this on an empty list
226 MOZ_ASSERT(!isEmpty());
227 MOZ_ASSERT(isStateValid());
230 mHead
= result
? ElementAccess::Get(result
).mNext
: nullptr;
232 ElementAccess::Get(mHead
).mPrev
= nullptr;
235 if (mTail
== result
) {
240 ElementAccess::Get(result
).mNext
= nullptr;
241 ElementAccess::Get(result
).mPrev
= nullptr;
248 * Inserts aElm into the list at the tail position. |aElm| must not already
251 void pushBack(T
* aElm
) {
253 MOZ_ASSERT(ElementNotInList(aElm
));
254 MOZ_ASSERT(isStateValid());
256 ElementAccess::Get(aElm
).mNext
= nullptr;
257 ElementAccess::Get(aElm
).mPrev
= mTail
;
259 MOZ_ASSERT(!ElementAccess::Get(mTail
).mNext
);
260 ElementAccess::Get(mTail
).mNext
= aElm
;
270 * Remove the tail of the list and return it. Calling this on an empty list
274 MOZ_ASSERT(!isEmpty());
275 MOZ_ASSERT(isStateValid());
278 mTail
= result
? ElementAccess::Get(result
).mPrev
: nullptr;
280 ElementAccess::Get(mTail
).mNext
= nullptr;
283 if (mHead
== result
) {
288 ElementAccess::Get(result
).mNext
= nullptr;
289 ElementAccess::Get(result
).mPrev
= nullptr;
296 * Insert the given |aElm| *before* |aIter|.
298 void insertBefore(const Iterator
& aIter
, T
* aElm
) {
300 MOZ_ASSERT(ElementNotInList(aElm
));
301 MOZ_ASSERT(isStateValid());
304 return pushBack(aElm
);
305 } else if (aIter
== begin()) {
306 return pushFront(aElm
);
309 T
* after
= &(*aIter
);
310 T
* before
= ElementAccess::Get(after
).mPrev
;
313 ElementAccess::Get(before
).mNext
= aElm
;
314 ElementAccess::Get(aElm
).mPrev
= before
;
315 ElementAccess::Get(aElm
).mNext
= after
;
316 ElementAccess::Get(after
).mPrev
= aElm
;
320 * Removes the given element from the list. The element must be in this list.
322 void remove(T
* aElm
) {
324 MOZ_ASSERT(ElementAccess::Get(aElm
).mNext
||
325 ElementAccess::Get(aElm
).mPrev
||
326 (aElm
== mHead
&& aElm
== mTail
),
327 "Attempted to remove element not in this list");
329 if (T
* prev
= ElementAccess::Get(aElm
).mPrev
) {
330 ElementAccess::Get(prev
).mNext
= ElementAccess::Get(aElm
).mNext
;
332 MOZ_ASSERT(mHead
== aElm
);
333 mHead
= ElementAccess::Get(aElm
).mNext
;
336 if (T
* next
= ElementAccess::Get(aElm
).mNext
) {
337 ElementAccess::Get(next
).mPrev
= ElementAccess::Get(aElm
).mPrev
;
339 MOZ_ASSERT(mTail
== aElm
);
340 mTail
= ElementAccess::Get(aElm
).mPrev
;
343 ElementAccess::Get(aElm
).mNext
= nullptr;
344 ElementAccess::Get(aElm
).mPrev
= nullptr;
348 * Returns an iterator referencing the first found element whose value matches
349 * the given element according to operator==.
351 Iterator
find(const T
& aElm
) { return std::find(begin(), end(), aElm
); }
354 * Returns whether the given element is in the list. Note that this uses
355 * T::operator==, not pointer comparison.
357 bool contains(const T
& aElm
) { return find(aElm
) != Iterator(); }
360 * Returns whether the given element might be in the list. Note that this
361 * assumes the element is either in the list or not in the list, and ignores
362 * the case where the element might be in another list in order to make the
365 bool ElementProbablyInList(T
* aElm
) {
369 return !ElementNotInList(aElm
);
374 * @brief Double linked list that allows insertion/removal during iteration.
376 * This class uses the mozilla::DoublyLinkedList internally and keeps
377 * track of created iterator instances by putting them on a simple list on stack
378 * (compare nsTAutoObserverArray).
379 * This allows insertion or removal operations to adjust iterators and therefore
380 * keeping them valid during iteration.
382 template <typename T
, typename ElementAccess
= GetDoublyLinkedListElement
<T
>>
383 class SafeDoublyLinkedList
{
386 * @brief Iterator class for SafeDoublyLinkedList.
388 * The iterator contains two iterators of the underlying list:
389 * - mCurrent points to the current list element of the iterator.
390 * - mNext points to the next element of the list.
392 * When removing an element from the list, mCurrent and mNext may
394 * - If mCurrent is the element to be deleted, it is set to empty. mNext can
395 * still be used to advance to the next element.
396 * - If mNext is the element to be deleted, it is set to its next element
397 * (or to empty if mNext is the last element of the list).
400 using BaseIterator
= typename DoublyLinkedList
<T
, ElementAccess
>::Iterator
;
401 friend class SafeDoublyLinkedList
<T
, ElementAccess
>;
404 using iterator_category
= std::forward_iterator_tag
;
405 using value_type
= T
;
406 using difference_type
= std::ptrdiff_t;
408 using const_pointer
= const T
*;
409 using reference
= T
&;
410 using const_reference
= const T
&;
412 SafeIterator() = default;
413 SafeIterator(SafeIterator
const& aOther
)
414 : SafeIterator(aOther
.mCurrent
, aOther
.mList
) {}
416 SafeIterator(BaseIterator aBaseIter
,
417 SafeDoublyLinkedList
<T
, ElementAccess
>* aList
)
418 : mCurrent(aBaseIter
),
419 mNext(aBaseIter
? ++aBaseIter
: BaseIterator()),
422 mNextIterator
= mList
->mIter
;
428 MOZ_ASSERT(mList
->mIter
== this,
429 "Iterators must currently be destroyed in opposite order "
430 "from the construction order. It is suggested that you "
431 "simply put them on the stack");
432 mList
->mIter
= mNextIterator
;
436 SafeIterator
& operator++() {
444 pointer
operator->() { return &*mCurrent
; }
445 const_pointer
operator->() const { return &*mCurrent
; }
446 reference
operator*() { return *mCurrent
; }
447 const_reference
operator*() const { return *mCurrent
; }
449 pointer
current() { return mCurrent
? &*mCurrent
: nullptr; }
450 const_pointer
current() const { return mCurrent
? &*mCurrent
: nullptr; }
452 explicit operator bool() const { return bool(mCurrent
); }
453 bool operator==(SafeIterator
const& other
) const {
454 return mCurrent
== other
.mCurrent
;
456 bool operator!=(SafeIterator
const& other
) const {
457 return mCurrent
!= other
.mCurrent
;
460 BaseIterator
& next() { return mNext
; } // mainly needed for unittests.
463 * Base list iterator pointing to the current list element of the iteration.
464 * If element mCurrent points to gets removed, the iterator will be set to
465 * empty. mNext keeps the iterator valid.
467 BaseIterator mCurrent
{nullptr};
469 * Base list iterator pointing to the next list element of the iteration.
470 * If element mCurrent points to gets removed, mNext is still valid.
471 * If element mNext points to gets removed, mNext advances, keeping this
474 BaseIterator mNext
{nullptr};
477 * Next element in the stack-allocated list of iterators stored in the
478 * SafeLinkedList object.
480 SafeIterator
* mNextIterator
{nullptr};
481 SafeDoublyLinkedList
<T
, ElementAccess
>* mList
{nullptr};
483 void setNext(T
* aElm
) { mNext
= BaseIterator(aElm
); }
484 void setCurrent(T
* aElm
) { mCurrent
= BaseIterator(aElm
); }
488 using BaseListType
= DoublyLinkedList
<T
, ElementAccess
>;
489 friend class SafeIterator
;
492 SafeDoublyLinkedList() = default;
494 bool isEmpty() const { return mList
.isEmpty(); }
495 bool contains(T
* aElm
) {
496 for (auto iter
= mList
.begin(); iter
!= mList
.end(); ++iter
) {
497 if (&*iter
== aElm
) {
504 SafeIterator
begin() { return SafeIterator(mList
.begin(), this); }
505 SafeIterator
begin() const { return SafeIterator(mList
.begin(), this); }
506 SafeIterator
cbegin() const { return begin(); }
508 SafeIterator
end() { return SafeIterator(); }
509 SafeIterator
end() const { return SafeIterator(); }
510 SafeIterator
cend() const { return SafeIterator(); }
512 void pushFront(T
* aElm
) { mList
.pushFront(aElm
); }
514 void pushBack(T
* aElm
) {
515 mList
.pushBack(aElm
);
521 iter
= iter
->mNextIterator
;
526 T
* firstElm
= mList
.popFront();
529 if (iter
->current() == firstElm
) {
530 iter
->setCurrent(nullptr);
532 iter
= iter
->mNextIterator
;
539 T
* lastElm
= mList
.popBack();
542 if (iter
->current() == lastElm
) {
543 iter
->setCurrent(nullptr);
544 } else if (iter
->mNext
&& &*(iter
->mNext
) == lastElm
) {
545 iter
->setNext(nullptr);
547 iter
= iter
->mNextIterator
;
553 void remove(T
* aElm
) {
554 if (!mList
.ElementProbablyInList(aElm
)) {
559 if (iter
->mNext
&& &*(iter
->mNext
) == aElm
) {
562 if (iter
->current() == aElm
) {
563 iter
->setCurrent(nullptr);
565 iter
= iter
->mNextIterator
;
573 SafeIterator
* mIter
{nullptr};
576 } // namespace mozilla
578 #endif // mozilla_DoublyLinkedList_h