Bumping manifests a=b2g-bump
[gecko.git] / mfbt / LinkedList.h
blob693c019f92cfc24c826b2885a543c7de6690efc5
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 * };
60 #ifndef mozilla_LinkedList_h
61 #define mozilla_LinkedList_h
63 #include "mozilla/Assertions.h"
64 #include "mozilla/Attributes.h"
65 #include "mozilla/MemoryReporting.h"
66 #include "mozilla/Move.h"
67 #include "mozilla/NullPtr.h"
69 #ifdef __cplusplus
71 namespace mozilla {
73 template<typename T>
74 class LinkedList;
76 template<typename T>
77 class LinkedListElement
80 * It's convenient that we return nullptr when getNext() or getPrevious()
81 * hits the end of the list, but doing so costs an extra word of storage in
82 * each linked list node (to keep track of whether |this| is the sentinel
83 * node) and a branch on this value in getNext/getPrevious.
85 * We could get rid of the extra word of storage by shoving the "is
86 * sentinel" bit into one of the pointers, although this would, of course,
87 * have performance implications of its own.
89 * But the goal here isn't to win an award for the fastest or slimmest
90 * linked list; rather, we want a *convenient* linked list. So we won't
91 * waste time guessing which micro-optimization strategy is best.
94 * Speaking of unnecessary work, it's worth addressing here why we wrote
95 * mozilla::LinkedList in the first place, instead of using stl::list.
97 * The key difference between mozilla::LinkedList and stl::list is that
98 * mozilla::LinkedList stores the mPrev/mNext pointers in the object itself,
99 * while stl::list stores the mPrev/mNext pointers in a list element which
100 * itself points to the object being stored.
102 * mozilla::LinkedList's approach makes it harder to store an object in more
103 * than one list. But the upside is that you can call next() / prev() /
104 * remove() directly on the object. With stl::list, you'd need to store a
105 * pointer to its iterator in the object in order to accomplish this. Not
106 * only would this waste space, but you'd have to remember to update that
107 * pointer every time you added or removed the object from a list.
109 * In-place, constant-time removal is a killer feature of doubly-linked
110 * lists, and supporting this painlessly was a key design criterion.
113 private:
114 LinkedListElement* mNext;
115 LinkedListElement* mPrev;
116 const bool mIsSentinel;
118 public:
119 LinkedListElement()
120 : mNext(MOZ_THIS_IN_INITIALIZER_LIST()),
121 mPrev(MOZ_THIS_IN_INITIALIZER_LIST()),
122 mIsSentinel(false)
125 LinkedListElement(LinkedListElement<T>&& other)
126 : mIsSentinel(other.mIsSentinel)
128 if (!other.isInList()) {
129 mNext = this;
130 mPrev = this;
131 return;
134 MOZ_ASSERT(other.mNext->mPrev == &other);
135 MOZ_ASSERT(other.mPrev->mNext == &other);
138 * Initialize |this| with |other|'s mPrev/mNext pointers, and adjust those
139 * element to point to this one.
141 mNext = other.mNext;
142 mPrev = other.mPrev;
144 mNext->mPrev = this;
145 mPrev->mNext = this;
148 * Adjust |other| so it doesn't think it's in a list. This makes it
149 * safely destructable.
151 other.mNext = &other;
152 other.mPrev = &other;
155 ~LinkedListElement()
157 if (!mIsSentinel && isInList()) {
158 remove();
163 * Get the next element in the list, or nullptr if this is the last element
164 * in the list.
166 T* getNext() { return mNext->asT(); }
167 const T* getNext() const { return mNext->asT(); }
170 * Get the previous element in the list, or nullptr if this is the first
171 * element in the list.
173 T* getPrevious() { return mPrev->asT(); }
174 const T* getPrevious() const { return mPrev->asT(); }
177 * Insert aElem after this element in the list. |this| must be part of a
178 * linked list when you call setNext(); otherwise, this method will assert.
180 void setNext(T* aElem)
182 MOZ_ASSERT(isInList());
183 setNextUnsafe(aElem);
187 * Insert aElem before this element in the list. |this| must be part of a
188 * linked list when you call setPrevious(); otherwise, this method will
189 * assert.
191 void setPrevious(T* aElem)
193 MOZ_ASSERT(isInList());
194 setPreviousUnsafe(aElem);
198 * Remove this element from the list which contains it. If this element is
199 * not currently part of a linked list, this method asserts.
201 void remove()
203 MOZ_ASSERT(isInList());
205 mPrev->mNext = mNext;
206 mNext->mPrev = mPrev;
207 mNext = this;
208 mPrev = this;
212 * Identical to remove(), but also asserts in debug builds that this element
213 * is in aList.
215 void removeFrom(const LinkedList<T>& aList)
217 aList.assertContains(asT());
218 remove();
222 * Return true if |this| part is of a linked list, and false otherwise.
224 bool isInList() const
226 MOZ_ASSERT((mNext == this) == (mPrev == this));
227 return mNext != this;
230 private:
231 friend class LinkedList<T>;
233 enum NodeKind {
234 NODE_KIND_NORMAL,
235 NODE_KIND_SENTINEL
238 explicit LinkedListElement(NodeKind nodeKind)
239 : mNext(MOZ_THIS_IN_INITIALIZER_LIST()),
240 mPrev(MOZ_THIS_IN_INITIALIZER_LIST()),
241 mIsSentinel(nodeKind == NODE_KIND_SENTINEL)
245 * Return |this| cast to T* if we're a normal node, or return nullptr if
246 * we're a sentinel node.
248 T* asT()
250 return mIsSentinel ? nullptr : static_cast<T*>(this);
252 const T* asT() const
254 return mIsSentinel ? nullptr : static_cast<const T*>(this);
258 * Insert aElem after this element, but don't check that this element is in
259 * the list. This is called by LinkedList::insertFront().
261 void setNextUnsafe(T* aElem)
263 LinkedListElement *listElem = static_cast<LinkedListElement*>(aElem);
264 MOZ_ASSERT(!listElem->isInList());
266 listElem->mNext = this->mNext;
267 listElem->mPrev = this;
268 this->mNext->mPrev = listElem;
269 this->mNext = listElem;
273 * Insert aElem before this element, but don't check that this element is in
274 * the list. This is called by LinkedList::insertBack().
276 void setPreviousUnsafe(T* aElem)
278 LinkedListElement<T>* listElem = static_cast<LinkedListElement<T>*>(aElem);
279 MOZ_ASSERT(!listElem->isInList());
281 listElem->mNext = this;
282 listElem->mPrev = this->mPrev;
283 this->mPrev->mNext = listElem;
284 this->mPrev = listElem;
287 private:
288 LinkedListElement& operator=(const LinkedListElement<T>& aOther) MOZ_DELETE;
289 LinkedListElement(const LinkedListElement<T>& aOther) MOZ_DELETE;
292 template<typename T>
293 class LinkedList
295 private:
296 LinkedListElement<T> sentinel;
298 public:
299 LinkedList() : sentinel(LinkedListElement<T>::NODE_KIND_SENTINEL) { }
301 LinkedList(LinkedList<T>&& aOther)
302 : sentinel(mozilla::Move(aOther.sentinel))
305 ~LinkedList() { MOZ_ASSERT(isEmpty()); }
308 * Add aElem to the front of the list.
310 void insertFront(T* aElem)
312 /* Bypass setNext()'s this->isInList() assertion. */
313 sentinel.setNextUnsafe(aElem);
317 * Add aElem to the back of the list.
319 void insertBack(T* aElem)
321 sentinel.setPreviousUnsafe(aElem);
325 * Get the first element of the list, or nullptr if the list is empty.
327 T* getFirst() { return sentinel.getNext(); }
328 const T* getFirst() const { return sentinel.getNext(); }
331 * Get the last element of the list, or nullptr if the list is empty.
333 T* getLast() { return sentinel.getPrevious(); }
334 const T* getLast() const { return sentinel.getPrevious(); }
337 * Get and remove the first element of the list. If the list is empty,
338 * return nullptr.
340 T* popFirst()
342 T* ret = sentinel.getNext();
343 if (ret) {
344 static_cast<LinkedListElement<T>*>(ret)->remove();
346 return ret;
350 * Get and remove the last element of the list. If the list is empty,
351 * return nullptr.
353 T* popLast()
355 T* ret = sentinel.getPrevious();
356 if (ret) {
357 static_cast<LinkedListElement<T>*>(ret)->remove();
359 return ret;
363 * Return true if the list is empty, or false otherwise.
365 bool isEmpty() const
367 return !sentinel.isInList();
371 * Remove all the elements from the list.
373 * This runs in time linear to the list's length, because we have to mark
374 * each element as not in the list.
376 void clear()
378 while (popFirst()) {
379 continue;
384 * Measures the memory consumption of the list excluding |this|. Note that
385 * it only measures the list elements themselves. If the list elements
386 * contain pointers to other memory blocks, those blocks must be measured
387 * separately during a subsequent iteration over the list.
389 size_t sizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
391 size_t n = 0;
392 for (const T* t = getFirst(); t; t = t->getNext()) {
393 n += aMallocSizeOf(t);
395 return n;
399 * Like sizeOfExcludingThis(), but measures |this| as well.
401 size_t sizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
403 return aMallocSizeOf(this) + sizeOfExcludingThis(aMallocSizeOf);
407 * In a debug build, make sure that the list is sane (no cycles, consistent
408 * mNext/mPrev pointers, only one sentinel). Has no effect in release builds.
410 void debugAssertIsSane() const
412 #ifdef DEBUG
413 const LinkedListElement<T>* slow;
414 const LinkedListElement<T>* fast1;
415 const LinkedListElement<T>* fast2;
418 * Check for cycles in the forward singly-linked list using the
419 * tortoise/hare algorithm.
421 for (slow = sentinel.mNext,
422 fast1 = sentinel.mNext->mNext,
423 fast2 = sentinel.mNext->mNext->mNext;
424 slow != &sentinel && fast1 != &sentinel && fast2 != &sentinel;
425 slow = slow->mNext, fast1 = fast2->mNext, fast2 = fast1->mNext) {
426 MOZ_ASSERT(slow != fast1);
427 MOZ_ASSERT(slow != fast2);
430 /* Check for cycles in the backward singly-linked list. */
431 for (slow = sentinel.mPrev,
432 fast1 = sentinel.mPrev->mPrev,
433 fast2 = sentinel.mPrev->mPrev->mPrev;
434 slow != &sentinel && fast1 != &sentinel && fast2 != &sentinel;
435 slow = slow->mPrev, fast1 = fast2->mPrev, fast2 = fast1->mPrev) {
436 MOZ_ASSERT(slow != fast1);
437 MOZ_ASSERT(slow != fast2);
441 * Check that |sentinel| is the only node in the list with
442 * mIsSentinel == true.
444 for (const LinkedListElement<T>* elem = sentinel.mNext;
445 elem != &sentinel;
446 elem = elem->mNext) {
447 MOZ_ASSERT(!elem->mIsSentinel);
450 /* Check that the mNext/mPrev pointers match up. */
451 const LinkedListElement<T>* prev = &sentinel;
452 const LinkedListElement<T>* cur = sentinel.mNext;
453 do {
454 MOZ_ASSERT(cur->mPrev == prev);
455 MOZ_ASSERT(prev->mNext == cur);
457 prev = cur;
458 cur = cur->mNext;
459 } while (cur != &sentinel);
460 #endif /* ifdef DEBUG */
463 private:
464 friend class LinkedListElement<T>;
466 void assertContains(const T* aValue) const
468 #ifdef DEBUG
469 for (const T* elem = getFirst(); elem; elem = elem->getNext()) {
470 if (elem == aValue) {
471 return;
474 MOZ_CRASH("element wasn't found in this list!");
475 #endif
478 LinkedList& operator=(const LinkedList<T>& aOther) MOZ_DELETE;
479 LinkedList(const LinkedList<T>& aOther) MOZ_DELETE;
482 } /* namespace mozilla */
484 #endif /* __cplusplus */
486 #endif /* mozilla_LinkedList_h */