1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 tw=80 et cin:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at:
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * The Mozilla Foundation
21 : * Portions created by the Initial Developer are Copyright (C) 2012
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Justin Lebar <justin.lebar@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /* A type-safe doubly-linked list class. */
42 :
43 : /*
44 : * The classes LinkedList<T> and LinkedListElement<T> together form a
45 : * convenient, type-safe doubly-linked list implementation.
46 : *
47 : * The class T which will be inserted into the linked list must inherit from
48 : * LinkedListElement<T>. A given object may be in only one linked list at a
49 : * time.
50 : *
51 : * For example, you might use LinkedList in a simple observer list class as
52 : * follows.
53 : *
54 : * class Observer : public LinkedListElement<Observer>
55 : * {
56 : * void observe(char* topic) { ... }
57 : * };
58 : *
59 : * class ObserverContainer
60 : * {
61 : * private:
62 : * LinkedList<ElemType> list;
63 : *
64 : * public:
65 : *
66 : * void addObserver(Observer* observer)
67 : * {
68 : * // Will assert if |observer| is part of another list.
69 : * list.insertBack(observer);
70 : * }
71 : *
72 : * void removeObserver(Observer* observer)
73 : * {
74 : * // Will assert if |observer| is not part of some list.
75 : * observer.remove();
76 : * }
77 : *
78 : * void notifyObservers(char* topic)
79 : * {
80 : * for (Observer* o = list.getFirst();
81 : * o != NULL;
82 : * o = o->getNext()) {
83 : * o->Observe(topic);
84 : * }
85 : * }
86 : * };
87 : *
88 : */
89 :
90 : #ifndef mozilla_LinkedList_h_
91 : #define mozilla_LinkedList_h_
92 :
93 : #include "mozilla/Assertions.h"
94 : #include "mozilla/Attributes.h"
95 :
96 : #ifdef __cplusplus
97 :
98 : namespace mozilla {
99 :
100 : template<typename T>
101 : class LinkedList;
102 :
103 : template<typename T>
104 : class LinkedListElement
105 : {
106 : /*
107 : * It's convenient that we return NULL when getNext() or getPrevious() hits
108 : * the end of the list, but doing so costs an extra word of storage in each
109 : * linked list node (to keep track of whether |this| is the sentinel node)
110 : * and a branch on this value in getNext/getPrevious.
111 : *
112 : * We could get rid of the extra word of storage by shoving the "is
113 : * sentinel" bit into one of the pointers, although this would, of course,
114 : * have performance implications of its own.
115 : *
116 : * But the goal here isn't to win an award for the fastest or slimmest
117 : * linked list; rather, we want a *convenient* linked list. So we won't
118 : * waste time guessing which micro-optimization strategy is best.
119 : *
120 : *
121 : * Speaking of unnecessary work, it's worth addressing here why we wrote
122 : * mozilla::LinkedList in the first place, instead of using stl::list.
123 : *
124 : * The key difference between mozilla::LinkedList and stl::list is that
125 : * mozilla::LinkedList stores the prev/next pointers in the object itself,
126 : * while stl::list stores the prev/next pointers in a list element which
127 : * itself points to the object being stored.
128 : *
129 : * mozilla::LinkedList's approach makes it harder to store an object in more
130 : * than one list. But the upside is that you can call next() / prev() /
131 : * remove() directly on the object. With stl::list, you'd need to store a
132 : * pointer to its iterator in the object in order to accomplish this. Not
133 : * only would this waste space, but you'd have to remember to update that
134 : * pointer every time you added or removed the object from a list.
135 : *
136 : * In-place, constant-time removal is a killer feature of doubly-linked
137 : * lists, and supporting this painlessly was a key design criterion.
138 : */
139 :
140 : private:
141 : LinkedListElement* next;
142 : LinkedListElement* prev;
143 : const bool isSentinel;
144 :
145 : public:
146 28 : LinkedListElement()
147 : : next(this)
148 : , prev(this)
149 28 : , isSentinel(false)
150 : {
151 28 : }
152 :
153 : /*
154 : * Get the next element in the list, or NULL if this is the last element in
155 : * the list.
156 : */
157 1431 : T* getNext()
158 : {
159 1431 : return next->asT();
160 : }
161 :
162 : /*
163 : * Get the previous element in the list, or NULL if this is the first element
164 : * in the list.
165 : */
166 : T* getPrevious()
167 : {
168 : return prev->asT();
169 : }
170 :
171 : /*
172 : * Insert elem after this element in the list. |this| must be part of a
173 : * linked list when you call setNext(); otherwise, this method will assert.
174 : */
175 : void setNext(T* elem)
176 : {
177 : MOZ_ASSERT(isInList());
178 : setNextUnsafe(elem);
179 : }
180 :
181 : /*
182 : * Insert elem before this element in the list. |this| must be part of a
183 : * linked list when you call setPrevious(); otherwise, this method will
184 : * assert.
185 : */
186 : void setPrevious(T* elem)
187 : {
188 : MOZ_ASSERT(isInList());
189 : setPreviousUnsafe(elem);
190 : }
191 :
192 : /*
193 : * Remove this element from the list which contains it. If this element is
194 : * not currently part of a linked list, this method asserts.
195 : */
196 8 : void remove()
197 : {
198 8 : MOZ_ASSERT(isInList());
199 :
200 8 : prev->next = next;
201 8 : next->prev = prev;
202 8 : next = this;
203 8 : prev = this;
204 8 : }
205 :
206 : /*
207 : * Return true if |this| part is of a linked list, and false otherwise.
208 : */
209 48 : bool isInList()
210 : {
211 48 : MOZ_ASSERT((next == this) == (prev == this));
212 48 : return next != this;
213 : }
214 :
215 : private:
216 : LinkedListElement& operator=(const LinkedList<T>& other) MOZ_DELETE;
217 : LinkedListElement(const LinkedList<T>& other) MOZ_DELETE;
218 :
219 : friend class LinkedList<T>;
220 :
221 : enum NodeKind {
222 : NODE_KIND_NORMAL,
223 : NODE_KIND_SENTINEL
224 : };
225 :
226 1472 : LinkedListElement(NodeKind nodeKind)
227 : : next(this)
228 : , prev(this)
229 1472 : , isSentinel(nodeKind == NODE_KIND_SENTINEL)
230 : {
231 1472 : }
232 :
233 : /*
234 : * Return |this| cast to T* if we're a normal node, or return NULL if we're
235 : * a sentinel node.
236 : */
237 1431 : T* asT()
238 : {
239 1431 : if (isSentinel)
240 1427 : return NULL;
241 :
242 4 : return static_cast<T*>(this);
243 : }
244 :
245 : /*
246 : * Insert elem after this element, but don't check that this element is in
247 : * the list. This is called by LinkedList::insertFront().
248 : */
249 : void setNextUnsafe(T* elem)
250 : {
251 : LinkedListElement *listElem = static_cast<LinkedListElement*>(elem);
252 : MOZ_ASSERT(!listElem->isInList());
253 :
254 : listElem->next = this->next;
255 : listElem->prev = this;
256 : this->next->prev = listElem;
257 : this->next = listElem;
258 : }
259 :
260 : /*
261 : * Insert elem before this element, but don't check that this element is in
262 : * the list. This is called by LinkedList::insertBack().
263 : */
264 8 : void setPreviousUnsafe(T* elem)
265 : {
266 8 : LinkedListElement<T>* listElem = static_cast<LinkedListElement<T>*>(elem);
267 8 : MOZ_ASSERT(!listElem->isInList());
268 :
269 8 : listElem->next = this;
270 8 : listElem->prev = this->prev;
271 8 : this->prev->next = listElem;
272 8 : this->prev = listElem;
273 8 : }
274 : };
275 :
276 : template<typename T>
277 : class LinkedList
278 : {
279 : private:
280 : LinkedListElement<T> sentinel;
281 :
282 : public:
283 : LinkedList& operator=(const LinkedList<T>& other) MOZ_DELETE;
284 : LinkedList(const LinkedList<T>& other) MOZ_DELETE;
285 :
286 1472 : LinkedList()
287 1472 : : sentinel(LinkedListElement<T>::NODE_KIND_SENTINEL)
288 : {
289 1472 : }
290 :
291 : /*
292 : * Add elem to the front of the list.
293 : */
294 : void insertFront(T* elem)
295 : {
296 : /* Bypass setNext()'s this->isInList() assertion. */
297 : sentinel.setNextUnsafe(elem);
298 : }
299 :
300 : /*
301 : * Add elem to the back of the list.
302 : */
303 8 : void insertBack(T* elem)
304 : {
305 8 : sentinel.setPreviousUnsafe(elem);
306 8 : }
307 :
308 : /*
309 : * Get the first element of the list, or NULL if the list is empty.
310 : */
311 : T* getFirst()
312 : {
313 : return sentinel.getNext();
314 : }
315 :
316 : /*
317 : * Get the last element of the list, or NULL if the list is empty.
318 : */
319 : T* getLast()
320 : {
321 : return sentinel.getPrevious();
322 : }
323 :
324 : /*
325 : * Get and remove the first element of the list. If the list is empty,
326 : * return NULL.
327 : */
328 1431 : T* popFirst()
329 : {
330 1431 : T* ret = sentinel.getNext();
331 1431 : if (ret)
332 4 : static_cast<LinkedListElement<T>*>(ret)->remove();
333 :
334 1431 : return ret;
335 : }
336 :
337 : /*
338 : * Get and remove the last element of the list. If the list is empty,
339 : * return NULL.
340 : */
341 : T* popLast()
342 : {
343 : T* ret = sentinel.getPrevious();
344 : if (ret)
345 : static_cast<LinkedListElement<T>*>(ret)->remove();
346 :
347 : return ret;
348 : }
349 :
350 : /*
351 : * Return true if the list is empty, or false otherwise.
352 : */
353 8 : bool isEmpty()
354 : {
355 8 : return !sentinel.isInList();
356 : }
357 :
358 : /*
359 : * In a debug build, make sure that the list is sane (no cycles, consistent
360 : * next/prev pointers, only one sentinel). Has no effect in release builds.
361 : */
362 : void debugAssertIsSane()
363 : {
364 : #ifdef DEBUG
365 : /*
366 : * Check for cycles in the forward singly-linked list using the
367 : * tortoise/hare algorithm.
368 : */
369 : for (LinkedListElement<T>* slow = sentinel.next,
370 : * fast1 = sentinel.next->next,
371 : * fast2 = sentinel.next->next->next;
372 : slow != sentinel && fast1 != sentinel && fast2 != sentinel;
373 : slow = slow->next,
374 : fast1 = fast2->next,
375 : fast2 = fast1->next) {
376 :
377 : MOZ_ASSERT(slow != fast1);
378 : MOZ_ASSERT(slow != fast2);
379 : }
380 :
381 : /* Check for cycles in the backward singly-linked list. */
382 : for (LinkedListElement<T>* slow = sentinel.prev,
383 : * fast1 = sentinel.prev->prev,
384 : * fast2 = sentinel.prev->prev->prev;
385 : slow != sentinel && fast1 != sentinel && fast2 != sentinel;
386 : slow = slow->prev,
387 : fast1 = fast2->prev,
388 : fast2 = fast1->prev) {
389 :
390 : MOZ_ASSERT(slow != fast1);
391 : MOZ_ASSERT(slow != fast2);
392 : }
393 :
394 : /*
395 : * Check that |sentinel| is the only node in the list with
396 : * isSentinel == true.
397 : */
398 : for (LinkedListElement<T>* elem = sentinel.next;
399 : elem != sentinel;
400 : elem = elem->next) {
401 :
402 : MOZ_ASSERT(!elem->isSentinel);
403 : }
404 :
405 : /* Check that the next/prev pointers match up. */
406 : LinkedListElement<T>* prev = sentinel;
407 : LinkedListElement<T>* cur = sentinel.next;
408 : do {
409 : MOZ_ASSERT(cur->prev == prev);
410 : MOZ_ASSERT(prev->next == cur);
411 :
412 : prev = cur;
413 : cur = cur->next;
414 : } while (cur != sentinel);
415 : #endif /* ifdef DEBUG */
416 : }
417 : };
418 :
419 : } /* namespace mozilla */
420 :
421 : #endif /* ifdef __cplusplus */
422 : #endif /* ifdef mozilla_LinkedList_h_ */
|