Bug 1845666 - Lock the media.utility-* prefs r=alwu
[gecko.git] / mfbt / LinkedList.h
blob850b8594c751b0301791e958f5cdddbffbd1b845
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 type-safe doubly-linked list class. */
9 /*
10 * The classes LinkedList<T> and LinkedListElement<T> together form a
11 * convenient, type-safe doubly-linked list implementation.
13 * The class T which will be inserted into the linked list must inherit from
14 * LinkedListElement<T>. A given object may be in only one linked list at a
15 * time.
17 * A LinkedListElement automatically removes itself from the list upon
18 * destruction, and a LinkedList will fatally assert in debug builds if it's
19 * non-empty when it's destructed.
21 * For example, you might use LinkedList in a simple observer list class as
22 * follows.
24 * class Observer : public LinkedListElement<Observer>
25 * {
26 * public:
27 * void observe(char* aTopic) { ... }
28 * };
30 * class ObserverContainer
31 * {
32 * private:
33 * LinkedList<Observer> list;
35 * public:
36 * void addObserver(Observer* aObserver)
37 * {
38 * // Will assert if |aObserver| is part of another list.
39 * list.insertBack(aObserver);
40 * }
42 * void removeObserver(Observer* aObserver)
43 * {
44 * // Will assert if |aObserver| is not part of some list.
45 * aObserver.remove();
46 * // Or, will assert if |aObserver| is not part of |list| specifically.
47 * // aObserver.removeFrom(list);
48 * }
50 * void notifyObservers(char* aTopic)
51 * {
52 * for (Observer* o = list.getFirst(); o != nullptr; o = o->getNext()) {
53 * o->observe(aTopic);
54 * }
55 * }
56 * };
58 * Additionally, the class AutoCleanLinkedList<T> is a LinkedList<T> that will
59 * remove and delete each element still within itself upon destruction. Note
60 * that because each element is deleted, elements must have been allocated
61 * using |new|.
64 #ifndef mozilla_LinkedList_h
65 #define mozilla_LinkedList_h
67 #include <algorithm>
68 #include <utility>
70 #include "mozilla/Assertions.h"
71 #include "mozilla/Attributes.h"
72 #include "mozilla/MemoryReporting.h"
73 #include "mozilla/RefPtr.h"
75 #ifdef __cplusplus
77 namespace mozilla {
79 template <typename T>
80 class LinkedListElement;
82 namespace detail {
84 /**
85 * LinkedList supports refcounted elements using this adapter class. Clients
86 * using LinkedList<RefPtr<T>> will get a data structure that holds a strong
87 * reference to T as long as T is in the list.
89 template <typename T>
90 struct LinkedListElementTraits {
91 typedef T* RawType;
92 typedef const T* ConstRawType;
93 typedef T* ClientType;
94 typedef const T* ConstClientType;
96 // These static methods are called when an element is added to or removed from
97 // a linked list. It can be used to keep track ownership in lists that are
98 // supposed to own their elements. If elements are transferred from one list
99 // to another, no enter or exit calls happen since the elements still belong
100 // to a list.
101 static void enterList(LinkedListElement<T>* elt) {}
102 static void exitList(LinkedListElement<T>* elt) {}
104 // This method is called when AutoCleanLinkedList cleans itself
105 // during destruction. It can be used to call delete on elements if
106 // the list is the sole owner.
107 static void cleanElement(LinkedListElement<T>* elt) { delete elt->asT(); }
110 template <typename T>
111 struct LinkedListElementTraits<RefPtr<T>> {
112 typedef T* RawType;
113 typedef const T* ConstRawType;
114 typedef RefPtr<T> ClientType;
115 typedef RefPtr<const T> ConstClientType;
117 static void enterList(LinkedListElement<RefPtr<T>>* elt) {
118 elt->asT()->AddRef();
120 static void exitList(LinkedListElement<RefPtr<T>>* elt) {
121 elt->asT()->Release();
123 static void cleanElement(LinkedListElement<RefPtr<T>>* elt) {}
126 } /* namespace detail */
128 template <typename T>
129 class LinkedList;
131 template <typename T>
132 class LinkedListElement {
133 typedef typename detail::LinkedListElementTraits<T> Traits;
134 typedef typename Traits::RawType RawType;
135 typedef typename Traits::ConstRawType ConstRawType;
136 typedef typename Traits::ClientType ClientType;
137 typedef typename Traits::ConstClientType ConstClientType;
140 * It's convenient that we return nullptr when getNext() or getPrevious()
141 * hits the end of the list, but doing so costs an extra word of storage in
142 * each linked list node (to keep track of whether |this| is the sentinel
143 * node) and a branch on this value in getNext/getPrevious.
145 * We could get rid of the extra word of storage by shoving the "is
146 * sentinel" bit into one of the pointers, although this would, of course,
147 * have performance implications of its own.
149 * But the goal here isn't to win an award for the fastest or slimmest
150 * linked list; rather, we want a *convenient* linked list. So we won't
151 * waste time guessing which micro-optimization strategy is best.
154 * Speaking of unnecessary work, it's worth addressing here why we wrote
155 * mozilla::LinkedList in the first place, instead of using stl::list.
157 * The key difference between mozilla::LinkedList and stl::list is that
158 * mozilla::LinkedList stores the mPrev/mNext pointers in the object itself,
159 * while stl::list stores the mPrev/mNext pointers in a list element which
160 * itself points to the object being stored.
162 * mozilla::LinkedList's approach makes it harder to store an object in more
163 * than one list. But the upside is that you can call next() / prev() /
164 * remove() directly on the object. With stl::list, you'd need to store a
165 * pointer to its iterator in the object in order to accomplish this. Not
166 * only would this waste space, but you'd have to remember to update that
167 * pointer every time you added or removed the object from a list.
169 * In-place, constant-time removal is a killer feature of doubly-linked
170 * lists, and supporting this painlessly was a key design criterion.
173 private:
174 LinkedListElement* mNext;
175 LinkedListElement* mPrev;
176 const bool mIsSentinel;
178 public:
179 LinkedListElement() : mNext(this), mPrev(this), mIsSentinel(false) {}
182 * Moves |aOther| into |*this|. If |aOther| is already in a list, then
183 * |aOther| is removed from the list and replaced by |*this|.
185 LinkedListElement(LinkedListElement<T>&& aOther)
186 : mIsSentinel(aOther.mIsSentinel) {
187 adjustLinkForMove(std::move(aOther));
190 LinkedListElement& operator=(LinkedListElement<T>&& aOther) {
191 MOZ_ASSERT(mIsSentinel == aOther.mIsSentinel, "Mismatch NodeKind!");
192 MOZ_ASSERT(!isInList(),
193 "Assigning to an element in a list messes up that list!");
194 adjustLinkForMove(std::move(aOther));
195 return *this;
198 ~LinkedListElement() {
199 if (!mIsSentinel && isInList()) {
200 remove();
205 * Get the next element in the list, or nullptr if this is the last element
206 * in the list.
208 RawType getNext() { return mNext->asT(); }
209 ConstRawType getNext() const { return mNext->asT(); }
212 * Get the previous element in the list, or nullptr if this is the first
213 * element in the list.
215 RawType getPrevious() { return mPrev->asT(); }
216 ConstRawType getPrevious() const { return mPrev->asT(); }
219 * Insert aElem after this element in the list. |this| must be part of a
220 * linked list when you call setNext(); otherwise, this method will assert.
222 void setNext(RawType aElem) {
223 MOZ_ASSERT(isInList());
224 setNextUnsafe(aElem);
228 * Insert aElem before this element in the list. |this| must be part of a
229 * linked list when you call setPrevious(); otherwise, this method will
230 * assert.
232 void setPrevious(RawType aElem) {
233 MOZ_ASSERT(isInList());
234 setPreviousUnsafe(aElem);
238 * Remove this element from the list which contains it. If this element is
239 * not currently part of a linked list, this method asserts.
241 void remove() {
242 MOZ_ASSERT(isInList());
244 mPrev->mNext = mNext;
245 mNext->mPrev = mPrev;
246 mNext = this;
247 mPrev = this;
249 Traits::exitList(this);
253 * Remove this element from the list containing it. Returns a pointer to the
254 * element that follows this element (before it was removed). This method
255 * asserts if the element does not belong to a list. Note: In a refcounted
256 * list, |this| may be destroyed.
258 RawType removeAndGetNext() {
259 RawType r = getNext();
260 remove();
261 return r;
265 * Remove this element from the list containing it. Returns a pointer to the
266 * previous element in the containing list (before the removal). This method
267 * asserts if the element does not belong to a list. Note: In a refcounted
268 * list, |this| may be destroyed.
270 RawType removeAndGetPrevious() {
271 RawType r = getPrevious();
272 remove();
273 return r;
277 * Identical to remove(), but also asserts in debug builds that this element
278 * is in aList.
280 void removeFrom(const LinkedList<T>& aList) {
281 aList.assertContains(asT());
282 remove();
286 * Return true if |this| part is of a linked list, and false otherwise.
288 bool isInList() const {
289 MOZ_ASSERT((mNext == this) == (mPrev == this));
290 return mNext != this;
293 private:
294 friend class LinkedList<T>;
295 friend struct detail::LinkedListElementTraits<T>;
297 enum class NodeKind { Normal, Sentinel };
299 explicit LinkedListElement(NodeKind nodeKind)
300 : mNext(this), mPrev(this), mIsSentinel(nodeKind == NodeKind::Sentinel) {}
303 * Return |this| cast to T* if we're a normal node, or return nullptr if
304 * we're a sentinel node.
306 RawType asT() { return mIsSentinel ? nullptr : static_cast<RawType>(this); }
307 ConstRawType asT() const {
308 return mIsSentinel ? nullptr : static_cast<ConstRawType>(this);
312 * Insert aElem after this element, but don't check that this element is in
313 * the list. This is called by LinkedList::insertFront().
315 void setNextUnsafe(RawType aElem) {
316 LinkedListElement* listElem = static_cast<LinkedListElement*>(aElem);
317 MOZ_RELEASE_ASSERT(!listElem->isInList());
319 listElem->mNext = this->mNext;
320 listElem->mPrev = this;
321 this->mNext->mPrev = listElem;
322 this->mNext = listElem;
324 Traits::enterList(aElem);
328 * Insert aElem before this element, but don't check that this element is in
329 * the list. This is called by LinkedList::insertBack().
331 void setPreviousUnsafe(RawType aElem) {
332 LinkedListElement<T>* listElem = static_cast<LinkedListElement<T>*>(aElem);
333 MOZ_RELEASE_ASSERT(!listElem->isInList());
335 listElem->mNext = this;
336 listElem->mPrev = this->mPrev;
337 this->mPrev->mNext = listElem;
338 this->mPrev = listElem;
340 Traits::enterList(aElem);
344 * Transfers the elements [aBegin, aEnd) before the "this" list element.
346 void transferBeforeUnsafe(LinkedListElement<T>& aBegin,
347 LinkedListElement<T>& aEnd) {
348 MOZ_RELEASE_ASSERT(!aBegin.mIsSentinel);
349 if (!aBegin.isInList() || !aEnd.isInList()) {
350 return;
353 auto otherPrev = aBegin.mPrev;
355 aBegin.mPrev = this->mPrev;
356 this->mPrev->mNext = &aBegin;
357 this->mPrev = aEnd.mPrev;
358 aEnd.mPrev->mNext = this;
360 // Patch the gap in the source list
361 otherPrev->mNext = &aEnd;
362 aEnd.mPrev = otherPrev;
366 * Adjust mNext and mPrev for implementing move constructor and move
367 * assignment.
369 void adjustLinkForMove(LinkedListElement<T>&& aOther) {
370 if (!aOther.isInList()) {
371 mNext = this;
372 mPrev = this;
373 return;
376 if (!mIsSentinel) {
377 Traits::enterList(this);
380 MOZ_ASSERT(aOther.mNext->mPrev == &aOther);
381 MOZ_ASSERT(aOther.mPrev->mNext == &aOther);
384 * Initialize |this| with |aOther|'s mPrev/mNext pointers, and adjust those
385 * element to point to this one.
387 mNext = aOther.mNext;
388 mPrev = aOther.mPrev;
390 mNext->mPrev = this;
391 mPrev->mNext = this;
394 * Adjust |aOther| so it doesn't think it's in a list. This makes it
395 * safely destructable.
397 aOther.mNext = &aOther;
398 aOther.mPrev = &aOther;
400 if (!mIsSentinel) {
401 Traits::exitList(&aOther);
405 LinkedListElement& operator=(const LinkedListElement<T>& aOther) = delete;
406 LinkedListElement(const LinkedListElement<T>& aOther) = delete;
409 template <typename T>
410 class LinkedList {
411 private:
412 typedef typename detail::LinkedListElementTraits<T> Traits;
413 typedef typename Traits::RawType RawType;
414 typedef typename Traits::ConstRawType ConstRawType;
415 typedef typename Traits::ClientType ClientType;
416 typedef typename Traits::ConstClientType ConstClientType;
417 typedef LinkedListElement<T>* ElementType;
418 typedef const LinkedListElement<T>* ConstElementType;
420 LinkedListElement<T> sentinel;
422 public:
423 template <typename Type, typename Element>
424 class Iterator {
425 Type mCurrent;
427 public:
428 using iterator_category = std::forward_iterator_tag;
429 using value_type = T;
430 using difference_type = std::ptrdiff_t;
431 using pointer = T*;
432 using reference = T&;
434 explicit Iterator(Type aCurrent) : mCurrent(aCurrent) {}
436 Type operator*() const { return mCurrent; }
438 const Iterator& operator++() {
439 mCurrent = static_cast<Element>(mCurrent)->getNext();
440 return *this;
443 bool operator!=(const Iterator& aOther) const {
444 return mCurrent != aOther.mCurrent;
448 LinkedList() : sentinel(LinkedListElement<T>::NodeKind::Sentinel) {}
450 LinkedList(LinkedList<T>&& aOther) : sentinel(std::move(aOther.sentinel)) {}
452 LinkedList& operator=(LinkedList<T>&& aOther) {
453 MOZ_ASSERT(isEmpty(),
454 "Assigning to a non-empty list leaks elements in that list!");
455 sentinel = std::move(aOther.sentinel);
456 return *this;
459 ~LinkedList() {
460 # ifdef DEBUG
461 if (!isEmpty()) {
462 MOZ_CRASH_UNSAFE_PRINTF(
463 "%s has a buggy user: "
464 "it should have removed all this list's elements before "
465 "the list's destruction",
466 __PRETTY_FUNCTION__);
468 # endif
472 * Add aElem to the front of the list.
474 void insertFront(RawType aElem) {
475 /* Bypass setNext()'s this->isInList() assertion. */
476 sentinel.setNextUnsafe(aElem);
480 * Add aElem to the back of the list.
482 void insertBack(RawType aElem) { sentinel.setPreviousUnsafe(aElem); }
485 * Move all elements from another list to the back
487 void extendBack(LinkedList<T>&& aOther) {
488 MOZ_RELEASE_ASSERT(this != &aOther);
489 if (aOther.isEmpty()) {
490 return;
492 sentinel.transferBeforeUnsafe(**aOther.begin(), aOther.sentinel);
496 * Move elements from another list to the specified position
498 void splice(size_t aDestinationPos, LinkedList<T>& aListFrom,
499 size_t aSourceStart, size_t aSourceLen) {
500 MOZ_RELEASE_ASSERT(this != &aListFrom);
501 if (aListFrom.isEmpty() || !aSourceLen) {
502 return;
505 const auto safeForward = [](LinkedList<T>& aList,
506 LinkedListElement<T>& aBegin,
507 size_t aPos) -> LinkedListElement<T>& {
508 auto* iter = &aBegin;
509 for (size_t i = 0; i < aPos; ++i, (iter = iter->mNext)) {
510 if (iter->mIsSentinel) {
511 break;
514 return *iter;
517 auto& sourceBegin =
518 safeForward(aListFrom, *aListFrom.sentinel.mNext, aSourceStart);
519 if (sourceBegin.mIsSentinel) {
520 return;
522 auto& sourceEnd = safeForward(aListFrom, sourceBegin, aSourceLen);
523 auto& destination = safeForward(*this, *sentinel.mNext, aDestinationPos);
525 destination.transferBeforeUnsafe(sourceBegin, sourceEnd);
529 * Get the first element of the list, or nullptr if the list is empty.
531 RawType getFirst() { return sentinel.getNext(); }
532 ConstRawType getFirst() const { return sentinel.getNext(); }
535 * Get the last element of the list, or nullptr if the list is empty.
537 RawType getLast() { return sentinel.getPrevious(); }
538 ConstRawType getLast() const { return sentinel.getPrevious(); }
541 * Get and remove the first element of the list. If the list is empty,
542 * return nullptr.
544 ClientType popFirst() {
545 ClientType ret = sentinel.getNext();
546 if (ret) {
547 static_cast<LinkedListElement<T>*>(RawType(ret))->remove();
549 return ret;
553 * Get and remove the last element of the list. If the list is empty,
554 * return nullptr.
556 ClientType popLast() {
557 ClientType ret = sentinel.getPrevious();
558 if (ret) {
559 static_cast<LinkedListElement<T>*>(RawType(ret))->remove();
561 return ret;
565 * Return true if the list is empty, or false otherwise.
567 bool isEmpty() const { return !sentinel.isInList(); }
570 * Returns whether the given element is in the list.
572 bool contains(ConstRawType aElm) const {
573 return std::find(begin(), end(), aElm) != end();
577 * Remove all the elements from the list.
579 * This runs in time linear to the list's length, because we have to mark
580 * each element as not in the list.
582 void clear() {
583 while (popFirst()) {
588 * Return the length of elements in the list.
590 size_t length() const { return std::distance(begin(), end()); }
593 * Allow range-based iteration:
595 * for (MyElementType* elt : myList) { ... }
597 Iterator<RawType, ElementType> begin() {
598 return Iterator<RawType, ElementType>(getFirst());
600 Iterator<ConstRawType, ConstElementType> begin() const {
601 return Iterator<ConstRawType, ConstElementType>(getFirst());
603 Iterator<RawType, ElementType> end() {
604 return Iterator<RawType, ElementType>(nullptr);
606 Iterator<ConstRawType, ConstElementType> end() const {
607 return Iterator<ConstRawType, ConstElementType>(nullptr);
611 * Measures the memory consumption of the list excluding |this|. Note that
612 * it only measures the list elements themselves. If the list elements
613 * contain pointers to other memory blocks, those blocks must be measured
614 * separately during a subsequent iteration over the list.
616 size_t sizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
617 size_t n = 0;
618 ConstRawType t = getFirst();
619 while (t) {
620 n += aMallocSizeOf(t);
621 t = static_cast<const LinkedListElement<T>*>(t)->getNext();
623 return n;
627 * Like sizeOfExcludingThis(), but measures |this| as well.
629 size_t sizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
630 return aMallocSizeOf(this) + sizeOfExcludingThis(aMallocSizeOf);
634 * In a debug build, make sure that the list is sane (no cycles, consistent
635 * mNext/mPrev pointers, only one sentinel). Has no effect in release builds.
637 void debugAssertIsSane() const {
638 # ifdef DEBUG
639 const LinkedListElement<T>* slow;
640 const LinkedListElement<T>* fast1;
641 const LinkedListElement<T>* fast2;
644 * Check for cycles in the forward singly-linked list using the
645 * tortoise/hare algorithm.
647 for (slow = sentinel.mNext, fast1 = sentinel.mNext->mNext,
648 fast2 = sentinel.mNext->mNext->mNext;
649 slow != &sentinel && fast1 != &sentinel && fast2 != &sentinel;
650 slow = slow->mNext, fast1 = fast2->mNext, fast2 = fast1->mNext) {
651 MOZ_ASSERT(slow != fast1);
652 MOZ_ASSERT(slow != fast2);
655 /* Check for cycles in the backward singly-linked list. */
656 for (slow = sentinel.mPrev, fast1 = sentinel.mPrev->mPrev,
657 fast2 = sentinel.mPrev->mPrev->mPrev;
658 slow != &sentinel && fast1 != &sentinel && fast2 != &sentinel;
659 slow = slow->mPrev, fast1 = fast2->mPrev, fast2 = fast1->mPrev) {
660 MOZ_ASSERT(slow != fast1);
661 MOZ_ASSERT(slow != fast2);
665 * Check that |sentinel| is the only node in the list with
666 * mIsSentinel == true.
668 for (const LinkedListElement<T>* elem = sentinel.mNext; elem != &sentinel;
669 elem = elem->mNext) {
670 MOZ_ASSERT(!elem->mIsSentinel);
673 /* Check that the mNext/mPrev pointers match up. */
674 const LinkedListElement<T>* prev = &sentinel;
675 const LinkedListElement<T>* cur = sentinel.mNext;
676 do {
677 MOZ_ASSERT(cur->mPrev == prev);
678 MOZ_ASSERT(prev->mNext == cur);
680 prev = cur;
681 cur = cur->mNext;
682 } while (cur != &sentinel);
683 # endif /* ifdef DEBUG */
686 private:
687 friend class LinkedListElement<T>;
689 void assertContains(const RawType aValue) const {
690 # ifdef DEBUG
691 for (ConstRawType elem = getFirst(); elem; elem = elem->getNext()) {
692 if (elem == aValue) {
693 return;
696 MOZ_CRASH("element wasn't found in this list!");
697 # endif
700 LinkedList& operator=(const LinkedList<T>& aOther) = delete;
701 LinkedList(const LinkedList<T>& aOther) = delete;
704 template <typename T>
705 inline void ImplCycleCollectionUnlink(LinkedList<RefPtr<T>>& aField) {
706 aField.clear();
709 template <typename T>
710 inline void ImplCycleCollectionTraverse(
711 nsCycleCollectionTraversalCallback& aCallback,
712 LinkedList<RefPtr<T>>& aField, const char* aName, uint32_t aFlags = 0) {
713 typedef typename detail::LinkedListElementTraits<T> Traits;
714 typedef typename Traits::RawType RawType;
715 for (RawType element : aField) {
716 // RefPtr is stored as a raw pointer in LinkedList.
717 // So instead of creating a new RefPtr from the raw
718 // pointer (which is not allowed), we simply call
719 // CycleCollectionNoteChild against the raw pointer
720 CycleCollectionNoteChild(aCallback, element, aName, aFlags);
724 template <typename T>
725 class AutoCleanLinkedList : public LinkedList<T> {
726 private:
727 using Traits = detail::LinkedListElementTraits<T>;
728 using ClientType = typename detail::LinkedListElementTraits<T>::ClientType;
730 public:
731 AutoCleanLinkedList() = default;
732 AutoCleanLinkedList(AutoCleanLinkedList&&) = default;
733 ~AutoCleanLinkedList() { clear(); }
735 AutoCleanLinkedList& operator=(AutoCleanLinkedList&& aOther) = default;
737 void clear() {
738 while (ClientType element = this->popFirst()) {
739 Traits::cleanElement(element);
744 } /* namespace mozilla */
746 #endif /* __cplusplus */
748 #endif /* mozilla_LinkedList_h */