LinkedList.h (23869B)
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/. */ 6 7 /* A type-safe doubly-linked list class. */ 8 9 /* 10 * The classes LinkedList<T> and LinkedListElement<T> together form a 11 * convenient, type-safe doubly-linked list implementation. 12 * 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. 16 * 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. 20 * 21 * For example, you might use LinkedList in a simple observer list class as 22 * follows. 23 * 24 * class Observer : public LinkedListElement<Observer> 25 * { 26 * public: 27 * void observe(char* aTopic) { ... } 28 * }; 29 * 30 * class ObserverContainer 31 * { 32 * private: 33 * LinkedList<Observer> list; 34 * 35 * public: 36 * void addObserver(Observer* aObserver) 37 * { 38 * // Will assert if |aObserver| is part of another list. 39 * list.insertBack(aObserver); 40 * } 41 * 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 * } 49 * 50 * void notifyObservers(char* aTopic) 51 * { 52 * for (Observer* o = list.getFirst(); o != nullptr; o = o->getNext()) { 53 * o->observe(aTopic); 54 * } 55 * } 56 * }; 57 * 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|. 62 */ 63 64 #ifndef mozilla_LinkedList_h 65 #define mozilla_LinkedList_h 66 67 #include <algorithm> 68 #include <iterator> 69 #include <utility> 70 71 #include "mozilla/Assertions.h" 72 #include "mozilla/MemoryReporting.h" 73 #include "mozilla/RefPtr.h" 74 75 #ifdef __cplusplus 76 77 namespace mozilla { 78 79 template <typename T> 80 class LinkedListElement; 81 82 namespace detail { 83 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. 88 */ 89 template <typename T> 90 struct LinkedListElementTraits { 91 using RawType = T*; 92 using ConstRawType = const T*; 93 using ClientType = T*; 94 using ConstClientType = const T*; 95 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) {} 103 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(); } 108 }; 109 110 template <typename T> 111 struct LinkedListElementTraits<RefPtr<T>> { 112 using RawType = T*; 113 using ConstRawType = const T*; 114 using ClientType = RefPtr<T>; 115 using ConstClientType = RefPtr<const T>; 116 117 static void enterList(LinkedListElement<RefPtr<T>>* elt) { 118 elt->asT()->AddRef(); 119 } 120 static void exitList(LinkedListElement<RefPtr<T>>* elt) { 121 elt->asT()->Release(); 122 } 123 static void cleanElement(LinkedListElement<RefPtr<T>>* elt) {} 124 }; 125 126 } /* namespace detail */ 127 128 template <typename T> 129 class LinkedList; 130 131 template <typename T> 132 class LinkedListElement { 133 using Traits = typename detail::LinkedListElementTraits<T>; 134 using RawType = typename Traits::RawType; 135 using ConstRawType = typename Traits::ConstRawType; 136 using ClientType = typename Traits::ClientType; 137 using ConstClientType = typename Traits::ConstClientType; 138 139 /* 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. 144 * 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. 148 * 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. 152 * 153 * 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. 156 * 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. 161 * 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. 168 * 169 * In-place, constant-time removal is a killer feature of doubly-linked 170 * lists, and supporting this painlessly was a key design criterion. 171 */ 172 173 private: 174 LinkedListElement* mNext; 175 LinkedListElement* mPrev; 176 const bool mIsSentinel; 177 178 public: 179 LinkedListElement() : mNext(this), mPrev(this), mIsSentinel(false) {} 180 181 /* 182 * Moves |aOther| into |*this|. If |aOther| is already in a list, then 183 * |aOther| is removed from the list and replaced by |*this|. 184 */ 185 LinkedListElement(LinkedListElement<T>&& aOther) 186 : mIsSentinel(aOther.mIsSentinel) { 187 adjustLinkForMove(std::move(aOther)); 188 } 189 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; 196 } 197 198 ~LinkedListElement() { 199 if (!mIsSentinel && isInList()) { 200 remove(); 201 } 202 } 203 204 /* 205 * Get the next element in the list, or nullptr if this is the last element 206 * in the list. 207 */ 208 RawType getNext() { return mNext->asT(); } 209 ConstRawType getNext() const { return mNext->asT(); } 210 211 /* 212 * Get the previous element in the list, or nullptr if this is the first 213 * element in the list. 214 */ 215 RawType getPrevious() { return mPrev->asT(); } 216 ConstRawType getPrevious() const { return mPrev->asT(); } 217 218 /* 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. 221 */ 222 void setNext(RawType aElem) { 223 MOZ_ASSERT(isInList()); 224 setNextUnsafe(aElem); 225 } 226 227 /* 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. 231 */ 232 void setPrevious(RawType aElem) { 233 MOZ_ASSERT(isInList()); 234 setPreviousUnsafe(aElem); 235 } 236 237 /* 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. 240 */ 241 void remove() { 242 MOZ_ASSERT(isInList()); 243 244 mPrev->mNext = mNext; 245 mNext->mPrev = mPrev; 246 mNext = this; 247 mPrev = this; 248 249 Traits::exitList(this); 250 } 251 252 /* 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. 257 */ 258 RawType removeAndGetNext() { 259 RawType r = getNext(); 260 remove(); 261 return r; 262 } 263 264 /* 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. 269 */ 270 RawType removeAndGetPrevious() { 271 RawType r = getPrevious(); 272 remove(); 273 return r; 274 } 275 276 /* 277 * Identical to remove(), but also asserts in debug builds that this element 278 * is in aList. 279 */ 280 void removeFrom(const LinkedList<T>& aList) { 281 aList.assertContains(asT()); 282 remove(); 283 } 284 285 /* 286 * Return true if |this| part is of a linked list, and false otherwise. 287 */ 288 bool isInList() const { 289 MOZ_ASSERT((mNext == this) == (mPrev == this)); 290 return mNext != this; 291 } 292 293 private: 294 friend class LinkedList<T>; 295 friend struct detail::LinkedListElementTraits<T>; 296 297 enum class NodeKind { Normal, Sentinel }; 298 299 explicit LinkedListElement(NodeKind nodeKind) 300 : mNext(this), mPrev(this), mIsSentinel(nodeKind == NodeKind::Sentinel) {} 301 302 /* 303 * Return |this| cast to T* if we're a normal node, or return nullptr if 304 * we're a sentinel node. 305 */ 306 RawType asT() { return mIsSentinel ? nullptr : static_cast<RawType>(this); } 307 ConstRawType asT() const { 308 return mIsSentinel ? nullptr : static_cast<ConstRawType>(this); 309 } 310 311 /* 312 * Insert aElem after this element, but don't check that this element is in 313 * the list. This is called by LinkedList::insertFront(). 314 */ 315 void setNextUnsafe(RawType aElem) { 316 LinkedListElement* listElem = static_cast<LinkedListElement*>(aElem); 317 MOZ_RELEASE_ASSERT(!listElem->isInList()); 318 319 listElem->mNext = this->mNext; 320 listElem->mPrev = this; 321 this->mNext->mPrev = listElem; 322 this->mNext = listElem; 323 324 Traits::enterList(aElem); 325 } 326 327 /* 328 * Insert aElem before this element, but don't check that this element is in 329 * the list. This is called by LinkedList::insertBack(). 330 */ 331 void setPreviousUnsafe(RawType aElem) { 332 LinkedListElement<T>* listElem = static_cast<LinkedListElement<T>*>(aElem); 333 MOZ_RELEASE_ASSERT(!listElem->isInList()); 334 335 listElem->mNext = this; 336 listElem->mPrev = this->mPrev; 337 this->mPrev->mNext = listElem; 338 this->mPrev = listElem; 339 340 Traits::enterList(aElem); 341 } 342 343 /* 344 * Transfers the elements [aBegin, aEnd) before the "this" list element. 345 */ 346 void transferBeforeUnsafe(LinkedListElement<T>& aBegin, 347 LinkedListElement<T>& aEnd) { 348 MOZ_RELEASE_ASSERT(!aBegin.mIsSentinel); 349 if (!aBegin.isInList() || !aEnd.isInList()) { 350 return; 351 } 352 353 auto otherPrev = aBegin.mPrev; 354 355 aBegin.mPrev = this->mPrev; 356 this->mPrev->mNext = &aBegin; 357 this->mPrev = aEnd.mPrev; 358 aEnd.mPrev->mNext = this; 359 360 // Patch the gap in the source list 361 otherPrev->mNext = &aEnd; 362 aEnd.mPrev = otherPrev; 363 } 364 365 /* 366 * Adjust mNext and mPrev for implementing move constructor and move 367 * assignment. 368 */ 369 void adjustLinkForMove(LinkedListElement<T>&& aOther) { 370 if (!aOther.isInList()) { 371 mNext = this; 372 mPrev = this; 373 return; 374 } 375 376 if (!mIsSentinel) { 377 Traits::enterList(this); 378 } 379 380 MOZ_ASSERT(aOther.mNext->mPrev == &aOther); 381 MOZ_ASSERT(aOther.mPrev->mNext == &aOther); 382 383 /* 384 * Initialize |this| with |aOther|'s mPrev/mNext pointers, and adjust those 385 * element to point to this one. 386 */ 387 mNext = aOther.mNext; 388 mPrev = aOther.mPrev; 389 390 mNext->mPrev = this; 391 mPrev->mNext = this; 392 393 /* 394 * Adjust |aOther| so it doesn't think it's in a list. This makes it 395 * safely destructable. 396 */ 397 aOther.mNext = &aOther; 398 aOther.mPrev = &aOther; 399 400 if (!mIsSentinel) { 401 Traits::exitList(&aOther); 402 } 403 } 404 405 LinkedListElement& operator=(const LinkedListElement<T>& aOther) = delete; 406 LinkedListElement(const LinkedListElement<T>& aOther) = delete; 407 }; 408 409 template <typename T> 410 class LinkedList { 411 private: 412 using Traits = typename detail::LinkedListElementTraits<T>; 413 using RawType = typename Traits::RawType; 414 using ConstRawType = typename Traits::ConstRawType; 415 using ClientType = typename Traits::ClientType; 416 using ConstClientType = typename Traits::ConstClientType; 417 using ElementType = LinkedListElement<T>*; 418 using ConstElementType = const LinkedListElement<T>*; 419 420 // The sentinel node always closes the circle of the list, making it possible 421 // to add/remove elements directly from a LinkedListElement without having a 422 // reference to the list. Iterators stop when they reach the sentinel node. 423 LinkedListElement<T> mSentinel; 424 425 // Forward and reverse iterator implementation. 426 template <bool Const = false, bool Reverse = false> 427 class IteratorImpl { 428 private: 429 using elem_type = std::conditional_t<Const, ConstElementType, ElementType>; 430 elem_type mCurrent; 431 432 public: 433 using iterator_category = std::forward_iterator_tag; 434 using value_type = std::conditional_t<Const, ConstRawType, RawType>; 435 using difference_type = std::ptrdiff_t; 436 using pointer = value_type*; 437 using reference = value_type&; 438 439 explicit IteratorImpl(elem_type aCurrent) : mCurrent(aCurrent) { 440 MOZ_ASSERT(mCurrent); 441 } 442 443 value_type operator*() const { return mCurrent->asT(); } 444 445 const IteratorImpl& operator++() { 446 MOZ_ASSERT(!mCurrent->mIsSentinel); 447 mCurrent = Reverse ? mCurrent->mPrev : mCurrent->mNext; 448 return *this; 449 } 450 451 const IteratorImpl& operator--() { 452 // We allow decrementing end() to get the last element. 453 mCurrent = Reverse ? mCurrent->mNext : mCurrent->mPrev; 454 return *this; 455 } 456 457 bool operator==(const IteratorImpl& aOther) const { 458 return mCurrent == aOther.mCurrent; 459 } 460 461 bool operator!=(const IteratorImpl& aOther) const { 462 return mCurrent != aOther.mCurrent; 463 } 464 }; 465 466 public: 467 LinkedList() : mSentinel(LinkedListElement<T>::NodeKind::Sentinel) {} 468 469 LinkedList(LinkedList<T>&& aOther) : mSentinel(std::move(aOther.mSentinel)) {} 470 471 LinkedList& operator=(LinkedList<T>&& aOther) { 472 MOZ_ASSERT(isEmpty(), 473 "Assigning to a non-empty list leaks elements in that list!"); 474 mSentinel = std::move(aOther.mSentinel); 475 return *this; 476 } 477 478 ~LinkedList() { 479 # ifdef DEBUG 480 if (!isEmpty()) { 481 MOZ_CRASH_UNSAFE_PRINTF( 482 "%s has a buggy user: " 483 "it should have removed all this list's elements before " 484 "the list's destruction", 485 __PRETTY_FUNCTION__); 486 } 487 # endif 488 } 489 490 using iterator = IteratorImpl<false, false>; 491 using const_iterator = IteratorImpl<true, false>; 492 using reverse_iterator = IteratorImpl<false, true>; 493 using const_reverse_iterator = IteratorImpl<true, true>; 494 495 // C++11 Iterator Support 496 iterator begin() { return iterator(mSentinel.mNext); } 497 const_iterator begin() const { return const_iterator(mSentinel.mNext); } 498 const_iterator cbegin() const { return begin(); } 499 iterator end() { return iterator(&mSentinel); } 500 const_iterator end() const { return const_iterator(&mSentinel); } 501 const_iterator cend() const { return end(); } 502 503 reverse_iterator rbegin() { return reverse_iterator(mSentinel.mPrev); } 504 const_reverse_iterator rbegin() const { 505 return const_reverse_iterator(mSentinel.mPrev); 506 } 507 const_reverse_iterator crbegin() const { return rbegin(); } 508 reverse_iterator rend() { return reverse_iterator(&mSentinel); } 509 const_reverse_iterator rend() const { 510 return const_reverse_iterator(&mSentinel); 511 } 512 const_reverse_iterator crend() const { return rend(); } 513 514 /* 515 * Add aElem to the front of the list. 516 */ 517 void insertFront(RawType aElem) { 518 /* Bypass setNext()'s this->isInList() assertion. */ 519 mSentinel.setNextUnsafe(aElem); 520 } 521 522 /* 523 * Add aElem to the back of the list. 524 */ 525 void insertBack(RawType aElem) { mSentinel.setPreviousUnsafe(aElem); } 526 527 /* 528 * Move all elements from another list to the back 529 */ 530 void extendBack(LinkedList<T>&& aOther) { 531 MOZ_RELEASE_ASSERT(this != &aOther); 532 if (aOther.isEmpty()) { 533 return; 534 } 535 mSentinel.transferBeforeUnsafe(**aOther.begin(), aOther.mSentinel); 536 } 537 538 /* 539 * Move elements from another list to the specified position 540 */ 541 void splice(size_t aDestinationPos, LinkedList<T>& aListFrom, 542 size_t aSourceStart, size_t aSourceLen) { 543 MOZ_RELEASE_ASSERT(this != &aListFrom); 544 if (aListFrom.isEmpty() || !aSourceLen) { 545 return; 546 } 547 548 const auto safeForward = [](LinkedList<T>& aList, 549 LinkedListElement<T>& aBegin, 550 size_t aPos) -> LinkedListElement<T>& { 551 auto* iter = &aBegin; 552 for (size_t i = 0; i < aPos; ++i, (iter = iter->mNext)) { 553 if (iter->mIsSentinel) { 554 break; 555 } 556 } 557 return *iter; 558 }; 559 560 auto& sourceBegin = 561 safeForward(aListFrom, *aListFrom.mSentinel.mNext, aSourceStart); 562 if (sourceBegin.mIsSentinel) { 563 return; 564 } 565 auto& sourceEnd = safeForward(aListFrom, sourceBegin, aSourceLen); 566 auto& destination = safeForward(*this, *mSentinel.mNext, aDestinationPos); 567 568 destination.transferBeforeUnsafe(sourceBegin, sourceEnd); 569 } 570 571 /* 572 * Get the first element of the list, or nullptr if the list is empty. 573 */ 574 RawType getFirst() { return mSentinel.getNext(); } 575 ConstRawType getFirst() const { return mSentinel.getNext(); } 576 577 /* 578 * Get the last element of the list, or nullptr if the list is empty. 579 */ 580 RawType getLast() { return mSentinel.getPrevious(); } 581 ConstRawType getLast() const { return mSentinel.getPrevious(); } 582 583 /* 584 * Get and remove the first element of the list. If the list is empty, 585 * return nullptr. 586 */ 587 ClientType popFirst() { 588 ClientType ret = mSentinel.getNext(); 589 if (ret) { 590 static_cast<LinkedListElement<T>*>(RawType(ret))->remove(); 591 } 592 return ret; 593 } 594 595 /* 596 * Get and remove the last element of the list. If the list is empty, 597 * return nullptr. 598 */ 599 ClientType popLast() { 600 ClientType ret = mSentinel.getPrevious(); 601 if (ret) { 602 static_cast<LinkedListElement<T>*>(RawType(ret))->remove(); 603 } 604 return ret; 605 } 606 607 /* 608 * Return true if the list is empty, or false otherwise. 609 */ 610 bool isEmpty() const { return !mSentinel.isInList(); } 611 612 /** 613 * Returns whether the given element is in the list. 614 */ 615 bool contains(ConstRawType aElm) const { 616 return std::find(begin(), end(), aElm) != end(); 617 } 618 619 /* 620 * Remove all the elements from the list. 621 * 622 * This runs in time linear to the list's length, because we have to mark 623 * each element as not in the list. 624 */ 625 void clear() { 626 while (popFirst()) { 627 } 628 } 629 630 /** 631 * Return the length of elements in the list. 632 */ 633 size_t length() const { return std::distance(begin(), end()); } 634 635 /* 636 * Measures the memory consumption of the list excluding |this|. Note that 637 * it only measures the list elements themselves. If the list elements 638 * contain pointers to other memory blocks, those blocks must be measured 639 * separately during a subsequent iteration over the list. 640 */ 641 size_t sizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { 642 size_t n = 0; 643 ConstRawType t = getFirst(); 644 while (t) { 645 n += aMallocSizeOf(t); 646 t = static_cast<const LinkedListElement<T>*>(t)->getNext(); 647 } 648 return n; 649 } 650 651 /* 652 * Like sizeOfExcludingThis(), but measures |this| as well. 653 */ 654 size_t sizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { 655 return aMallocSizeOf(this) + sizeOfExcludingThis(aMallocSizeOf); 656 } 657 658 /* 659 * In a debug build, make sure that the list is sane (no cycles, consistent 660 * mNext/mPrev pointers, only one sentinel). Has no effect in release builds. 661 */ 662 void debugAssertIsSane() const { 663 # ifdef DEBUG 664 const LinkedListElement<T>* slow; 665 const LinkedListElement<T>* fast1; 666 const LinkedListElement<T>* fast2; 667 668 /* 669 * Check for cycles in the forward singly-linked list using the 670 * tortoise/hare algorithm. 671 */ 672 for (slow = mSentinel.mNext, fast1 = mSentinel.mNext->mNext, 673 fast2 = mSentinel.mNext->mNext->mNext; 674 slow != &mSentinel && fast1 != &mSentinel && fast2 != &mSentinel; 675 slow = slow->mNext, fast1 = fast2->mNext, fast2 = fast1->mNext) { 676 MOZ_ASSERT(slow != fast1); 677 MOZ_ASSERT(slow != fast2); 678 } 679 680 /* Check for cycles in the backward singly-linked list. */ 681 for (slow = mSentinel.mPrev, fast1 = mSentinel.mPrev->mPrev, 682 fast2 = mSentinel.mPrev->mPrev->mPrev; 683 slow != &mSentinel && fast1 != &mSentinel && fast2 != &mSentinel; 684 slow = slow->mPrev, fast1 = fast2->mPrev, fast2 = fast1->mPrev) { 685 MOZ_ASSERT(slow != fast1); 686 MOZ_ASSERT(slow != fast2); 687 } 688 689 /* 690 * Check that |sentinel| is the only node in the list with 691 * mIsSentinel == true. 692 */ 693 for (const LinkedListElement<T>* elem = mSentinel.mNext; elem != &mSentinel; 694 elem = elem->mNext) { 695 MOZ_ASSERT(!elem->mIsSentinel); 696 } 697 698 /* Check that the mNext/mPrev pointers match up. */ 699 const LinkedListElement<T>* prev = &mSentinel; 700 const LinkedListElement<T>* cur = mSentinel.mNext; 701 do { 702 MOZ_ASSERT(cur->mPrev == prev); 703 MOZ_ASSERT(prev->mNext == cur); 704 705 prev = cur; 706 cur = cur->mNext; 707 } while (cur != &mSentinel); 708 # endif /* ifdef DEBUG */ 709 } 710 711 private: 712 friend class LinkedListElement<T>; 713 714 void assertContains(const RawType aValue) const { 715 # ifdef DEBUG 716 for (ConstRawType elem = getFirst(); elem; elem = elem->getNext()) { 717 if (elem == aValue) { 718 return; 719 } 720 } 721 MOZ_CRASH("element wasn't found in this list!"); 722 # endif 723 } 724 725 LinkedList& operator=(const LinkedList<T>& aOther) = delete; 726 LinkedList(const LinkedList<T>& aOther) = delete; 727 }; 728 729 template <typename T> 730 size_t RangeSizeEstimate(const LinkedList<T>&) { 731 return 0; 732 } 733 734 template <typename T> 735 inline void ImplCycleCollectionUnlink(LinkedList<RefPtr<T>>& aField) { 736 aField.clear(); 737 } 738 739 template <typename T> 740 inline void ImplCycleCollectionTraverse( 741 nsCycleCollectionTraversalCallback& aCallback, 742 LinkedList<RefPtr<T>>& aField, const char* aName, uint32_t aFlags = 0) { 743 using Traits = typename detail::LinkedListElementTraits<T>; 744 using RawType = typename Traits::RawType; 745 for (RawType element : aField) { 746 // RefPtr is stored as a raw pointer in LinkedList. 747 // So instead of creating a new RefPtr from the raw 748 // pointer (which is not allowed), we simply call 749 // CycleCollectionNoteChild against the raw pointer 750 CycleCollectionNoteChild(aCallback, element, aName, aFlags); 751 } 752 } 753 754 template <typename T> 755 class AutoCleanLinkedList : public LinkedList<T> { 756 private: 757 using Traits = detail::LinkedListElementTraits<T>; 758 using ClientType = typename detail::LinkedListElementTraits<T>::ClientType; 759 760 public: 761 AutoCleanLinkedList() = default; 762 AutoCleanLinkedList(AutoCleanLinkedList&&) = default; 763 ~AutoCleanLinkedList() { clear(); } 764 765 AutoCleanLinkedList& operator=(AutoCleanLinkedList&& aOther) = default; 766 767 void clear() { 768 while (ClientType element = this->popFirst()) { 769 Traits::cleanElement(element); 770 } 771 } 772 }; 773 774 } /* namespace mozilla */ 775 776 #endif /* __cplusplus */ 777 778 #endif /* mozilla_LinkedList_h */