tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */