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. */
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
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
24 * class Observer : public LinkedListElement<Observer>
27 * void observe(char* topic) { ... }
30 * class ObserverContainer
33 * LinkedList<Observer> list;
36 * void addObserver(Observer* observer) {
37 * // Will assert if |observer| is part of another list.
38 * list.insertBack(observer);
41 * void removeObserver(Observer* observer) {
42 * // Will assert if |observer| is not part of some list.
44 * // Or, will assert if |observer| is not part of |list| specifically.
45 * // observer.removeFrom(list);
48 * void notifyObservers(char* topic) {
49 * for (Observer* o = list.getFirst(); o != nullptr; o = o->getNext())
56 #ifndef mozilla_LinkedList_h
57 #define mozilla_LinkedList_h
59 #include "mozilla/Assertions.h"
60 #include "mozilla/Attributes.h"
61 #include "mozilla/MemoryReporting.h"
62 #include "mozilla/Move.h"
63 #include "mozilla/NullPtr.h"
73 class LinkedListElement
76 * It's convenient that we return nullptr when getNext() or getPrevious()
77 * hits the end of the list, but doing so costs an extra word of storage in
78 * each linked list node (to keep track of whether |this| is the sentinel
79 * node) and a branch on this value in getNext/getPrevious.
81 * We could get rid of the extra word of storage by shoving the "is
82 * sentinel" bit into one of the pointers, although this would, of course,
83 * have performance implications of its own.
85 * But the goal here isn't to win an award for the fastest or slimmest
86 * linked list; rather, we want a *convenient* linked list. So we won't
87 * waste time guessing which micro-optimization strategy is best.
90 * Speaking of unnecessary work, it's worth addressing here why we wrote
91 * mozilla::LinkedList in the first place, instead of using stl::list.
93 * The key difference between mozilla::LinkedList and stl::list is that
94 * mozilla::LinkedList stores the prev/next pointers in the object itself,
95 * while stl::list stores the prev/next pointers in a list element which
96 * itself points to the object being stored.
98 * mozilla::LinkedList's approach makes it harder to store an object in more
99 * than one list. But the upside is that you can call next() / prev() /
100 * remove() directly on the object. With stl::list, you'd need to store a
101 * pointer to its iterator in the object in order to accomplish this. Not
102 * only would this waste space, but you'd have to remember to update that
103 * pointer every time you added or removed the object from a list.
105 * In-place, constant-time removal is a killer feature of doubly-linked
106 * lists, and supporting this painlessly was a key design criterion.
110 LinkedListElement
* next
;
111 LinkedListElement
* prev
;
112 const bool isSentinel
;
116 : next(MOZ_THIS_IN_INITIALIZER_LIST()),
117 prev(MOZ_THIS_IN_INITIALIZER_LIST()),
121 LinkedListElement(LinkedListElement
<T
>&& other
)
122 : isSentinel(other
.isSentinel
)
124 if (!other
.isInList()) {
130 MOZ_ASSERT(other
.next
->prev
== &other
);
131 MOZ_ASSERT(other
.prev
->next
== &other
);
134 * Initialize |this| with |other|'s prev/next pointers, and adjust those
135 * element to point to this one.
144 * Adjust |other| so it doesn't think it's in a list. This makes it
145 * safely destructable.
151 ~LinkedListElement() {
152 if (!isSentinel
&& isInList())
157 * Get the next element in the list, or nullptr if this is the last element
163 const T
* getNext() const {
168 * Get the previous element in the list, or nullptr if this is the first
169 * element in the list.
174 const T
* getPrevious() const {
179 * Insert elem after this element in the list. |this| must be part of a
180 * linked list when you call setNext(); otherwise, this method will assert.
182 void setNext(T
* elem
) {
183 MOZ_ASSERT(isInList());
188 * Insert elem before this element in the list. |this| must be part of a
189 * linked list when you call setPrevious(); otherwise, this method will
192 void setPrevious(T
* elem
) {
193 MOZ_ASSERT(isInList());
194 setPreviousUnsafe(elem
);
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.
202 MOZ_ASSERT(isInList());
211 * Identical to remove(), but also asserts in debug builds that this element
214 void removeFrom(const LinkedList
<T
>& list
) {
215 list
.assertContains(asT());
220 * Return true if |this| part is of a linked list, and false otherwise.
222 bool isInList() const {
223 MOZ_ASSERT((next
== this) == (prev
== this));
228 friend class LinkedList
<T
>;
235 LinkedListElement(NodeKind nodeKind
)
236 : next(MOZ_THIS_IN_INITIALIZER_LIST()),
237 prev(MOZ_THIS_IN_INITIALIZER_LIST()),
238 isSentinel(nodeKind
== NODE_KIND_SENTINEL
)
242 * Return |this| cast to T* if we're a normal node, or return nullptr if
243 * we're a sentinel node.
249 return static_cast<T
*>(this);
251 const T
* asT() const {
255 return static_cast<const T
*>(this);
259 * Insert elem after this element, but don't check that this element is in
260 * the list. This is called by LinkedList::insertFront().
262 void setNextUnsafe(T
* elem
) {
263 LinkedListElement
*listElem
= static_cast<LinkedListElement
*>(elem
);
264 MOZ_ASSERT(!listElem
->isInList());
266 listElem
->next
= this->next
;
267 listElem
->prev
= this;
268 this->next
->prev
= listElem
;
269 this->next
= listElem
;
273 * Insert elem 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
* elem
) {
277 LinkedListElement
<T
>* listElem
= static_cast<LinkedListElement
<T
>*>(elem
);
278 MOZ_ASSERT(!listElem
->isInList());
280 listElem
->next
= this;
281 listElem
->prev
= this->prev
;
282 this->prev
->next
= listElem
;
283 this->prev
= listElem
;
287 LinkedListElement
& operator=(const LinkedListElement
<T
>& other
) MOZ_DELETE
;
288 LinkedListElement(const LinkedListElement
<T
>& other
) MOZ_DELETE
;
295 LinkedListElement
<T
> sentinel
;
298 LinkedList() : sentinel(LinkedListElement
<T
>::NODE_KIND_SENTINEL
) { }
300 LinkedList(LinkedList
<T
>&& other
)
301 : sentinel(mozilla::Move(other
.sentinel
))
305 MOZ_ASSERT(isEmpty());
309 * Add elem to the front of the list.
311 void insertFront(T
* elem
) {
312 /* Bypass setNext()'s this->isInList() assertion. */
313 sentinel
.setNextUnsafe(elem
);
317 * Add elem to the back of the list.
319 void insertBack(T
* elem
) {
320 sentinel
.setPreviousUnsafe(elem
);
324 * Get the first element of the list, or nullptr if the list is empty.
327 return sentinel
.getNext();
329 const T
* getFirst() const {
330 return sentinel
.getNext();
334 * Get the last element of the list, or nullptr if the list is empty.
337 return sentinel
.getPrevious();
339 const T
* getLast() const {
340 return sentinel
.getPrevious();
344 * Get and remove the first element of the list. If the list is empty,
348 T
* ret
= sentinel
.getNext();
350 static_cast<LinkedListElement
<T
>*>(ret
)->remove();
355 * Get and remove the last element of the list. If the list is empty,
359 T
* ret
= sentinel
.getPrevious();
361 static_cast<LinkedListElement
<T
>*>(ret
)->remove();
366 * Return true if the list is empty, or false otherwise.
368 bool isEmpty() const {
369 return !sentinel
.isInList();
373 * Remove all the elements from the list.
375 * This runs in time linear to the list's length, because we have to mark
376 * each element as not in the list.
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 mallocSizeOf
) const {
391 for (const T
* t
= getFirst(); t
; t
= t
->getNext())
392 n
+= mallocSizeOf(t
);
397 * Like sizeOfExcludingThis(), but measures |this| as well.
399 size_t sizeOfIncludingThis(MallocSizeOf mallocSizeOf
) const {
400 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf
);
404 * In a debug build, make sure that the list is sane (no cycles, consistent
405 * next/prev pointers, only one sentinel). Has no effect in release builds.
407 void debugAssertIsSane() const {
409 const LinkedListElement
<T
>* slow
;
410 const LinkedListElement
<T
>* fast1
;
411 const LinkedListElement
<T
>* fast2
;
414 * Check for cycles in the forward singly-linked list using the
415 * tortoise/hare algorithm.
417 for (slow
= sentinel
.next
,
418 fast1
= sentinel
.next
->next
,
419 fast2
= sentinel
.next
->next
->next
;
420 slow
!= &sentinel
&& fast1
!= &sentinel
&& fast2
!= &sentinel
;
421 slow
= slow
->next
, fast1
= fast2
->next
, fast2
= fast1
->next
)
423 MOZ_ASSERT(slow
!= fast1
);
424 MOZ_ASSERT(slow
!= fast2
);
427 /* Check for cycles in the backward singly-linked list. */
428 for (slow
= sentinel
.prev
,
429 fast1
= sentinel
.prev
->prev
,
430 fast2
= sentinel
.prev
->prev
->prev
;
431 slow
!= &sentinel
&& fast1
!= &sentinel
&& fast2
!= &sentinel
;
432 slow
= slow
->prev
, fast1
= fast2
->prev
, fast2
= fast1
->prev
)
434 MOZ_ASSERT(slow
!= fast1
);
435 MOZ_ASSERT(slow
!= fast2
);
439 * Check that |sentinel| is the only node in the list with
440 * isSentinel == true.
442 for (const LinkedListElement
<T
>* elem
= sentinel
.next
;
446 MOZ_ASSERT(!elem
->isSentinel
);
449 /* Check that the next/prev pointers match up. */
450 const LinkedListElement
<T
>* prev
= &sentinel
;
451 const LinkedListElement
<T
>* cur
= sentinel
.next
;
453 MOZ_ASSERT(cur
->prev
== prev
);
454 MOZ_ASSERT(prev
->next
== cur
);
458 } while (cur
!= &sentinel
);
459 #endif /* ifdef DEBUG */
463 friend class LinkedListElement
<T
>;
465 void assertContains(const T
* t
) const {
467 for (const T
* elem
= getFirst();
469 elem
= elem
->getNext())
474 MOZ_CRASH("element wasn't found in this list!");
478 LinkedList
& operator=(const LinkedList
<T
>& other
) MOZ_DELETE
;
479 LinkedList(const LinkedList
<T
>& other
) MOZ_DELETE
;
482 } /* namespace mozilla */
484 #endif /* __cplusplus */
486 #endif /* mozilla_LinkedList_h */