Bug 1887677: Correctly compute inClass predicate when validating unbound private...
[gecko.git] / mfbt / DoublyLinkedList.h
blobdf178440d2d727bb82ff876378be70e3072b237e
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
12 #include <algorithm>
13 #include <iosfwd>
14 #include <iterator>
15 #include <type_traits>
17 #include "mozilla/Assertions.h"
19 /**
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.
32 * Example:
34 * class Observer : public DoublyLinkedListElement<Observer>
35 * {
36 * public:
37 * void observe(char* aTopic) { ... }
38 * };
40 * class ObserverContainer
41 * {
42 * private:
43 * DoublyLinkedList<Observer> mList;
45 * public:
46 * void addObserver(Observer* aObserver)
47 * {
48 * // Will assert if |aObserver| is part of another list.
49 * mList.pushBack(aObserver);
50 * }
52 * void removeObserver(Observer* aObserver)
53 * {
54 * // Will assert if |aObserver| is not part of |list|.
55 * mList.remove(aObserver);
56 * }
58 * void notifyObservers(char* aTopic)
59 * {
60 * for (Observer* o : mList) {
61 * o->observe(aTopic);
62 * }
63 * }
64 * };
67 namespace mozilla {
69 /**
70 * Deriving from this will allow T to be inserted into and removed from a
71 * DoublyLinkedList.
73 template <typename T>
74 class DoublyLinkedListElement {
75 template <typename U, typename E>
76 friend class DoublyLinkedList;
77 friend T;
78 T* mNext;
79 T* mPrev;
81 public:
82 DoublyLinkedListElement() : mNext(nullptr), mPrev(nullptr) {}
85 /**
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.
96 template <typename T>
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 {
112 T* mHead;
113 T* mTail;
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;
129 return false;
132 public:
133 DoublyLinkedList() : mHead(nullptr), mTail(nullptr) {}
135 class Iterator final {
136 T* mCurrent;
138 public:
139 using iterator_category = std::forward_iterator_tag;
140 using value_type = T;
141 using difference_type = std::ptrdiff_t;
142 using pointer = 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;
153 return *this;
156 Iterator operator++(int) {
157 Iterator result = *this;
158 ++(*this);
159 return result;
162 Iterator& operator--() {
163 mCurrent = ElementAccess::Get(mCurrent).mPrev;
164 return *this;
167 Iterator operator--(int) {
168 Iterator result = *this;
169 --(*this);
170 return result;
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
202 * be in a list.
204 void pushFront(T* aElm) {
205 MOZ_ASSERT(aElm);
206 MOZ_ASSERT(ElementNotInList(aElm));
207 MOZ_ASSERT(isStateValid());
209 ElementAccess::Get(aElm).mNext = mHead;
210 if (mHead) {
211 MOZ_ASSERT(!ElementAccess::Get(mHead).mPrev);
212 ElementAccess::Get(mHead).mPrev = aElm;
215 mHead = aElm;
216 if (!mTail) {
217 mTail = aElm;
222 * Remove the head of the list and return it. Calling this on an empty list
223 * will assert.
225 T* popFront() {
226 MOZ_ASSERT(!isEmpty());
227 MOZ_ASSERT(isStateValid());
229 T* result = mHead;
230 mHead = result ? ElementAccess::Get(result).mNext : nullptr;
231 if (mHead) {
232 ElementAccess::Get(mHead).mPrev = nullptr;
235 if (mTail == result) {
236 mTail = nullptr;
239 if (result) {
240 ElementAccess::Get(result).mNext = nullptr;
241 ElementAccess::Get(result).mPrev = nullptr;
244 return result;
248 * Inserts aElm into the list at the tail position. |aElm| must not already
249 * be in a list.
251 void pushBack(T* aElm) {
252 MOZ_ASSERT(aElm);
253 MOZ_ASSERT(ElementNotInList(aElm));
254 MOZ_ASSERT(isStateValid());
256 ElementAccess::Get(aElm).mNext = nullptr;
257 ElementAccess::Get(aElm).mPrev = mTail;
258 if (mTail) {
259 MOZ_ASSERT(!ElementAccess::Get(mTail).mNext);
260 ElementAccess::Get(mTail).mNext = aElm;
263 mTail = aElm;
264 if (!mHead) {
265 mHead = aElm;
270 * Remove the tail of the list and return it. Calling this on an empty list
271 * will assert.
273 T* popBack() {
274 MOZ_ASSERT(!isEmpty());
275 MOZ_ASSERT(isStateValid());
277 T* result = mTail;
278 mTail = result ? ElementAccess::Get(result).mPrev : nullptr;
279 if (mTail) {
280 ElementAccess::Get(mTail).mNext = nullptr;
283 if (mHead == result) {
284 mHead = nullptr;
287 if (result) {
288 ElementAccess::Get(result).mNext = nullptr;
289 ElementAccess::Get(result).mPrev = nullptr;
292 return result;
296 * Insert the given |aElm| *before* |aIter|.
298 void insertBefore(const Iterator& aIter, T* aElm) {
299 MOZ_ASSERT(aElm);
300 MOZ_ASSERT(ElementNotInList(aElm));
301 MOZ_ASSERT(isStateValid());
303 if (!aIter) {
304 return pushBack(aElm);
305 } else if (aIter == begin()) {
306 return pushFront(aElm);
309 T* after = &(*aIter);
310 T* before = ElementAccess::Get(after).mPrev;
311 MOZ_ASSERT(before);
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) {
323 MOZ_ASSERT(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;
331 } else {
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;
338 } else {
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
363 * check fast.
365 bool ElementProbablyInList(T* aElm) {
366 if (isEmpty()) {
367 return false;
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 {
384 public:
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
393 * be adjusted:
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).
399 class SafeIterator {
400 using BaseIterator = typename DoublyLinkedList<T, ElementAccess>::Iterator;
401 friend class SafeDoublyLinkedList<T, ElementAccess>;
403 public:
404 using iterator_category = std::forward_iterator_tag;
405 using value_type = T;
406 using difference_type = std::ptrdiff_t;
407 using pointer = 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()),
420 mList(aList) {
421 if (mList) {
422 mNextIterator = mList->mIter;
423 mList->mIter = this;
426 ~SafeIterator() {
427 if (mList) {
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++() {
437 mCurrent = mNext;
438 if (mNext) {
439 ++mNext;
441 return *this;
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.
461 private:
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
472 * iterator valid.
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); }
487 private:
488 using BaseListType = DoublyLinkedList<T, ElementAccess>;
489 friend class SafeIterator;
491 public:
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) {
498 return true;
501 return false;
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);
516 auto* iter = mIter;
517 while (iter) {
518 if (!iter->mNext) {
519 iter->setNext(aElm);
521 iter = iter->mNextIterator;
525 T* popFront() {
526 T* firstElm = mList.popFront();
527 auto* iter = mIter;
528 while (iter) {
529 if (iter->current() == firstElm) {
530 iter->setCurrent(nullptr);
532 iter = iter->mNextIterator;
535 return firstElm;
538 T* popBack() {
539 T* lastElm = mList.popBack();
540 auto* iter = mIter;
541 while (iter) {
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;
550 return lastElm;
553 void remove(T* aElm) {
554 if (!mList.ElementProbablyInList(aElm)) {
555 return;
557 auto* iter = mIter;
558 while (iter) {
559 if (iter->mNext && &*(iter->mNext) == aElm) {
560 ++(iter->mNext);
562 if (iter->current() == aElm) {
563 iter->setCurrent(nullptr);
565 iter = iter->mNextIterator;
568 mList.remove(aElm);
571 private:
572 BaseListType mList;
573 SafeIterator* mIter{nullptr};
576 } // namespace mozilla
578 #endif // mozilla_DoublyLinkedList_h