tor-browser

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

DoublyLinkedList.h (17370B)


      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 doubly-linked list with flexible next/prev naming. */
      8 
      9 #ifndef mozilla_DoublyLinkedList_h
     10 #define mozilla_DoublyLinkedList_h
     11 
     12 #include <algorithm>
     13 #include <iterator>
     14 #include <type_traits>
     15 
     16 #include "mozilla/Assertions.h"
     17 
     18 /**
     19 * Where mozilla::LinkedList strives for ease of use above all other
     20 * considerations, mozilla::DoublyLinkedList strives for flexibility. The
     21 * following are things that can be done with mozilla::DoublyLinkedList that
     22 * cannot be done with mozilla::LinkedList:
     23 *
     24 *   * Arbitrary next/prev placement and naming. With the tools provided here,
     25 *     the next and previous pointers can be at the end of the structure, in a
     26 *     sub-structure, stored with a tag, in a union, wherever, as long as you
     27 *     can look them up and set them on demand.
     28 *   * Can be used without deriving from a new base and, thus, does not require
     29 *     use of constructors.
     30 *
     31 * Example:
     32 *
     33 *   class Observer : public DoublyLinkedListElement<Observer>
     34 *   {
     35 *   public:
     36 *     void observe(char* aTopic) { ... }
     37 *   };
     38 *
     39 *   class ObserverContainer
     40 *   {
     41 *   private:
     42 *     DoublyLinkedList<Observer> mList;
     43 *
     44 *   public:
     45 *     void addObserver(Observer* aObserver)
     46 *     {
     47 *       // Will assert if |aObserver| is part of another list.
     48 *       mList.pushBack(aObserver);
     49 *     }
     50 *
     51 *     void removeObserver(Observer* aObserver)
     52 *     {
     53 *       // Will assert if |aObserver| is not part of |list|.
     54 *       mList.remove(aObserver);
     55 *     }
     56 *
     57 *     void notifyObservers(char* aTopic)
     58 *     {
     59 *       for (Observer* o : mList) {
     60 *         o->observe(aTopic);
     61 *       }
     62 *     }
     63 *   };
     64 */
     65 
     66 namespace mozilla {
     67 
     68 /**
     69 *  Deriving from this will allow T to be inserted into and removed from a
     70 *  DoublyLinkedList.
     71 */
     72 template <typename T>
     73 class DoublyLinkedListElement {
     74  template <typename U, typename E>
     75  friend class DoublyLinkedList;
     76  friend T;
     77  T* mNext;
     78  T* mPrev;
     79 
     80 public:
     81  DoublyLinkedListElement() : mNext(nullptr), mPrev(nullptr) {}
     82 };
     83 
     84 /**
     85 * Provides access to a DoublyLinkedListElement within T.
     86 *
     87 * The default implementation of this template works for types that derive
     88 * from DoublyLinkedListElement, but one can specialize for their class so
     89 * that some appropriate DoublyLinkedListElement reference is returned.
     90 *
     91 * For more complex cases (multiple DoublyLinkedListElements, for example),
     92 * one can define their own trait class and use that as ElementAccess for
     93 * DoublyLinkedList. See TestDoublyLinkedList.cpp for an example.
     94 */
     95 template <typename T>
     96 struct GetDoublyLinkedListElement {
     97  static_assert(std::is_base_of<DoublyLinkedListElement<T>, T>::value,
     98                "You need your own specialization of GetDoublyLinkedListElement"
     99                " or use a separate Trait.");
    100  static const DoublyLinkedListElement<T>& Get(const T* aThis) {
    101    return *aThis;
    102  }
    103  static DoublyLinkedListElement<T>& Get(T* aThis) { return *aThis; }
    104 };
    105 
    106 /**
    107 * A doubly linked list. |T| is the type of element stored in this list. |T|
    108 * must contain or have access to unique next and previous element pointers.
    109 * The template argument |ElementAccess| provides code to tell this list how to
    110 * get a reference to a DoublyLinkedListElement that may reside anywhere.
    111 */
    112 template <typename T, typename ElementAccess = GetDoublyLinkedListElement<T>>
    113 class DoublyLinkedList final {
    114  T* mHead;
    115  T* mTail;
    116 
    117  /**
    118   * Checks that either the list is empty and both mHead and mTail are nullptr
    119   * or the list has entries and both mHead and mTail are non-null.
    120   */
    121  bool isStateValid() const { return (mHead != nullptr) == (mTail != nullptr); }
    122 
    123  bool ElementNotInList(const T* aElm) const {
    124    if (!ElementAccess::Get(aElm).mNext && !ElementAccess::Get(aElm).mPrev) {
    125      // Both mNext and mPrev being NULL can mean two things:
    126      // - the element is not in the list.
    127      // - the element is the first and only element in the list.
    128      // So check for the latter.
    129      return mHead != aElm;
    130    }
    131    return false;
    132  }
    133 
    134 public:
    135  constexpr DoublyLinkedList() : mHead(nullptr), mTail(nullptr) {}
    136 
    137  class Iterator final {
    138    T* mCurrent;
    139 
    140   public:
    141    using iterator_category = std::forward_iterator_tag;
    142    using value_type = T;
    143    using difference_type = std::ptrdiff_t;
    144    using pointer = T*;
    145    using reference = T&;
    146 
    147    Iterator() : mCurrent(nullptr) {}
    148    explicit Iterator(T* aCurrent) : mCurrent(aCurrent) {}
    149 
    150    T& operator*() const { return *mCurrent; }
    151    T* operator->() const { return mCurrent; }
    152 
    153    Iterator& operator++() {
    154      mCurrent = mCurrent ? ElementAccess::Get(mCurrent).mNext : nullptr;
    155      return *this;
    156    }
    157 
    158    Iterator operator++(int) {
    159      Iterator result = *this;
    160      ++(*this);
    161      return result;
    162    }
    163 
    164    Iterator& operator--() {
    165      mCurrent = ElementAccess::Get(mCurrent).mPrev;
    166      return *this;
    167    }
    168 
    169    Iterator operator--(int) {
    170      Iterator result = *this;
    171      --(*this);
    172      return result;
    173    }
    174 
    175    bool operator!=(const Iterator& aOther) const {
    176      return mCurrent != aOther.mCurrent;
    177    }
    178 
    179    bool operator==(const Iterator& aOther) const {
    180      return mCurrent == aOther.mCurrent;
    181    }
    182 
    183    explicit operator bool() const { return mCurrent; }
    184  };
    185 
    186  Iterator begin() { return Iterator(mHead); }
    187  const Iterator begin() const { return Iterator(mHead); }
    188  const Iterator cbegin() const { return Iterator(mHead); }
    189 
    190  Iterator end() { return Iterator(); }
    191  const Iterator end() const { return Iterator(); }
    192  const Iterator cend() const { return Iterator(); }
    193 
    194  /**
    195   * Returns true if the list contains no elements.
    196   */
    197  bool isEmpty() const {
    198    MOZ_ASSERT(isStateValid());
    199    return mHead == nullptr;
    200  }
    201 
    202  /**
    203   * Inserts aElm into the list at the head position. |aElm| must not already
    204   * be in a list.
    205   */
    206  void pushFront(T* aElm) {
    207    MOZ_ASSERT(aElm);
    208    MOZ_ASSERT(ElementNotInList(aElm));
    209    MOZ_ASSERT(isStateValid());
    210 
    211    ElementAccess::Get(aElm).mNext = mHead;
    212    if (mHead) {
    213      MOZ_ASSERT(!ElementAccess::Get(mHead).mPrev);
    214      ElementAccess::Get(mHead).mPrev = aElm;
    215    }
    216 
    217    mHead = aElm;
    218    if (!mTail) {
    219      mTail = aElm;
    220    }
    221  }
    222 
    223  /**
    224   * Remove the head of the list and return it. Calling this on an empty list
    225   * will assert.
    226   */
    227  T* popFront() {
    228    MOZ_ASSERT(!isEmpty());
    229    MOZ_ASSERT(isStateValid());
    230 
    231    T* result = mHead;
    232    mHead = result ? ElementAccess::Get(result).mNext : nullptr;
    233    if (mHead) {
    234      ElementAccess::Get(mHead).mPrev = nullptr;
    235    }
    236 
    237    if (mTail == result) {
    238      mTail = nullptr;
    239    }
    240 
    241    if (result) {
    242      ElementAccess::Get(result).mNext = nullptr;
    243      ElementAccess::Get(result).mPrev = nullptr;
    244    }
    245 
    246    return result;
    247  }
    248 
    249  /**
    250   * Inserts aElm into the list at the tail position. |aElm| must not already
    251   * be in a list.
    252   */
    253  void pushBack(T* aElm) {
    254    MOZ_ASSERT(aElm);
    255    MOZ_ASSERT(ElementNotInList(aElm));
    256    MOZ_ASSERT(isStateValid());
    257 
    258    ElementAccess::Get(aElm).mNext = nullptr;
    259    ElementAccess::Get(aElm).mPrev = mTail;
    260    if (mTail) {
    261      MOZ_ASSERT(!ElementAccess::Get(mTail).mNext);
    262      ElementAccess::Get(mTail).mNext = aElm;
    263    }
    264 
    265    mTail = aElm;
    266    if (!mHead) {
    267      mHead = aElm;
    268    }
    269  }
    270 
    271  /**
    272   * Remove the tail of the list and return it. Calling this on an empty list
    273   * will assert.
    274   */
    275  T* popBack() {
    276    MOZ_ASSERT(!isEmpty());
    277    MOZ_ASSERT(isStateValid());
    278 
    279    T* result = mTail;
    280    mTail = result ? ElementAccess::Get(result).mPrev : nullptr;
    281    if (mTail) {
    282      ElementAccess::Get(mTail).mNext = nullptr;
    283    }
    284 
    285    if (mHead == result) {
    286      mHead = nullptr;
    287    }
    288 
    289    if (result) {
    290      ElementAccess::Get(result).mNext = nullptr;
    291      ElementAccess::Get(result).mPrev = nullptr;
    292    }
    293 
    294    return result;
    295  }
    296 
    297  /**
    298   * Insert the given |aElm| *before* |aIter|.
    299   */
    300  void insertBefore(const Iterator& aIter, T* aElm) {
    301    MOZ_ASSERT(aElm);
    302    MOZ_ASSERT(ElementNotInList(aElm));
    303    MOZ_ASSERT(isStateValid());
    304 
    305    if (!aIter) {
    306      return pushBack(aElm);
    307    } else if (aIter == begin()) {
    308      return pushFront(aElm);
    309    }
    310 
    311    T* after = &(*aIter);
    312    T* before = ElementAccess::Get(after).mPrev;
    313    MOZ_ASSERT(before);
    314 
    315    ElementAccess::Get(before).mNext = aElm;
    316    ElementAccess::Get(aElm).mPrev = before;
    317    ElementAccess::Get(aElm).mNext = after;
    318    ElementAccess::Get(after).mPrev = aElm;
    319  }
    320 
    321  /**
    322   * Removes the given element from the list. The element must be in this list.
    323   */
    324  void remove(T* aElm) {
    325    MOZ_ASSERT(aElm);
    326    MOZ_ASSERT(ElementAccess::Get(aElm).mNext ||
    327                   ElementAccess::Get(aElm).mPrev ||
    328                   (aElm == mHead && aElm == mTail),
    329               "Attempted to remove element not in this list");
    330 
    331    if (T* prev = ElementAccess::Get(aElm).mPrev) {
    332      ElementAccess::Get(prev).mNext = ElementAccess::Get(aElm).mNext;
    333    } else {
    334      MOZ_ASSERT(mHead == aElm);
    335      mHead = ElementAccess::Get(aElm).mNext;
    336    }
    337 
    338    if (T* next = ElementAccess::Get(aElm).mNext) {
    339      ElementAccess::Get(next).mPrev = ElementAccess::Get(aElm).mPrev;
    340    } else {
    341      MOZ_ASSERT(mTail == aElm);
    342      mTail = ElementAccess::Get(aElm).mPrev;
    343    }
    344 
    345    ElementAccess::Get(aElm).mNext = nullptr;
    346    ElementAccess::Get(aElm).mPrev = nullptr;
    347  }
    348 
    349  /**
    350   * Returns an iterator referencing the first found element whose value matches
    351   * the given element according to operator==.
    352   */
    353  Iterator find(const T& aElm) const { return std::find(begin(), end(), aElm); }
    354 
    355  /**
    356   * Returns whether the given element is in the list. Note that this uses
    357   * T::operator==, not pointer comparison.
    358   */
    359  bool contains(const T& aElm) const { return find(aElm) != Iterator(); }
    360 
    361  /**
    362   * Returns whether the given element might be in the list. Note that this
    363   * assumes the element is either in the list or not in the list, and ignores
    364   * the case where the element might be in another list in order to make the
    365   * check fast.
    366   */
    367  bool ElementProbablyInList(const T* aElm) const {
    368    if (isEmpty()) {
    369      return false;
    370    }
    371    return !ElementNotInList(aElm);
    372  }
    373 
    374  /**
    375   * Returns whether an element is linked correctly to its predecessor and/or
    376   * successor, if any. Used for internal sanity checks.
    377   */
    378  bool ElementIsLinkedWell(const T* aElm) const {
    379    MOZ_ASSERT(aElm);
    380    if (!ElementProbablyInList(aElm)) {
    381      return false;
    382    }
    383    T* next = ElementAccess::Get(aElm).mNext;
    384    if (next) {
    385      if (ElementAccess::Get(next).mPrev != aElm || aElm == mTail) {
    386        return false;
    387      }
    388    } else {
    389      if (aElm != mTail) {
    390        return false;
    391      }
    392    }
    393    T* prev = ElementAccess::Get(aElm).mPrev;
    394    if (prev) {
    395      if (ElementAccess::Get(prev).mNext != aElm || aElm == mHead) {
    396        return false;
    397      }
    398    } else {
    399      if (aElm != mHead) {
    400        return false;
    401      }
    402    }
    403    return true;
    404  }
    405 };
    406 
    407 /**
    408 * @brief Double linked list that allows insertion/removal during iteration.
    409 *
    410 * This class uses the mozilla::DoublyLinkedList internally and keeps
    411 * track of created iterator instances by putting them on a simple list on stack
    412 * (compare nsTAutoObserverArray).
    413 * This allows insertion or removal operations to adjust iterators and therefore
    414 * keeping them valid during iteration.
    415 */
    416 template <typename T, typename ElementAccess = GetDoublyLinkedListElement<T>>
    417 class SafeDoublyLinkedList {
    418 public:
    419  /**
    420   * @brief Iterator class for SafeDoublyLinkedList.
    421   *
    422   * The iterator contains two iterators of the underlying list:
    423   *  - mCurrent points to the current list element of the iterator.
    424   *  - mNext points to the next element of the list.
    425   *
    426   * When removing an element from the list, mCurrent and mNext may
    427   * be adjusted:
    428   *  - If mCurrent is the element to be deleted, it is set to empty. mNext can
    429   *    still be used to advance to the next element.
    430   *  - If mNext is the element to be deleted, it is set to its next element
    431   *    (or to empty if mNext is the last element of the list).
    432   */
    433  class SafeIterator {
    434    using BaseIterator = typename DoublyLinkedList<T, ElementAccess>::Iterator;
    435    friend class SafeDoublyLinkedList<T, ElementAccess>;
    436 
    437   public:
    438    using iterator_category = std::forward_iterator_tag;
    439    using value_type = T;
    440    using difference_type = std::ptrdiff_t;
    441    using pointer = T*;
    442    using const_pointer = const T*;
    443    using reference = T&;
    444    using const_reference = const T&;
    445 
    446    SafeIterator() = default;
    447    SafeIterator(SafeIterator const& aOther)
    448        : SafeIterator(aOther.mCurrent, aOther.mList) {}
    449 
    450    SafeIterator(BaseIterator aBaseIter,
    451                 SafeDoublyLinkedList<T, ElementAccess>* aList)
    452        : mCurrent(aBaseIter),
    453          mNext(aBaseIter ? ++aBaseIter : BaseIterator()),
    454          mList(aList) {
    455      if (mList) {
    456        mNextIterator = mList->mIter;
    457        mList->mIter = this;
    458      }
    459    }
    460    ~SafeIterator() {
    461      if (mList) {
    462        MOZ_ASSERT(mList->mIter == this,
    463                   "Iterators must currently be destroyed in opposite order "
    464                   "from the construction order. It is suggested that you "
    465                   "simply put them on the stack");
    466        mList->mIter = mNextIterator;
    467      }
    468    }
    469 
    470    SafeIterator& operator++() {
    471      mCurrent = mNext;
    472      if (mNext) {
    473        ++mNext;
    474      }
    475      return *this;
    476    }
    477 
    478    pointer operator->() { return &*mCurrent; }
    479    const_pointer operator->() const { return &*mCurrent; }
    480    reference operator*() { return *mCurrent; }
    481    const_reference operator*() const { return *mCurrent; }
    482 
    483    pointer current() { return mCurrent ? &*mCurrent : nullptr; }
    484    const_pointer current() const { return mCurrent ? &*mCurrent : nullptr; }
    485 
    486    explicit operator bool() const { return bool(mCurrent); }
    487    bool operator==(SafeIterator const& other) const {
    488      return mCurrent == other.mCurrent;
    489    }
    490    bool operator!=(SafeIterator const& other) const {
    491      return mCurrent != other.mCurrent;
    492    }
    493 
    494    BaseIterator& next() { return mNext; }  // mainly needed for unittests.
    495   private:
    496    /**
    497     * Base list iterator pointing to the current list element of the iteration.
    498     * If element mCurrent points to gets removed, the iterator will be set to
    499     * empty. mNext keeps the iterator valid.
    500     */
    501    BaseIterator mCurrent{nullptr};
    502    /**
    503     * Base list iterator pointing to the next list element of the iteration.
    504     * If element mCurrent points to gets removed, mNext is still valid.
    505     * If element mNext points to gets removed, mNext advances, keeping this
    506     * iterator valid.
    507     */
    508    BaseIterator mNext{nullptr};
    509 
    510    /**
    511     * Next element in the stack-allocated list of iterators stored in the
    512     * SafeLinkedList object.
    513     */
    514    SafeIterator* mNextIterator{nullptr};
    515    SafeDoublyLinkedList<T, ElementAccess>* mList{nullptr};
    516 
    517    void setNext(T* aElm) { mNext = BaseIterator(aElm); }
    518    void setCurrent(T* aElm) { mCurrent = BaseIterator(aElm); }
    519  };
    520 
    521 private:
    522  using BaseListType = DoublyLinkedList<T, ElementAccess>;
    523  friend class SafeIterator;
    524 
    525 public:
    526  SafeDoublyLinkedList() = default;
    527 
    528  bool isEmpty() const { return mList.isEmpty(); }
    529  bool contains(T* aElm) {
    530    for (const T& el : *this) {
    531      if (aElm == &el) {
    532        return true;
    533      }
    534    }
    535    return false;
    536  }
    537 
    538  SafeIterator begin() { return SafeIterator(mList.begin(), this); }
    539  SafeIterator begin() const { return SafeIterator(mList.begin(), this); }
    540  SafeIterator cbegin() const { return begin(); }
    541 
    542  SafeIterator end() { return SafeIterator(); }
    543  SafeIterator end() const { return SafeIterator(); }
    544  SafeIterator cend() const { return SafeIterator(); }
    545 
    546  void pushFront(T* aElm) { mList.pushFront(aElm); }
    547 
    548  void pushBack(T* aElm) {
    549    mList.pushBack(aElm);
    550    auto* iter = mIter;
    551    while (iter) {
    552      if (!iter->mNext) {
    553        iter->setNext(aElm);
    554      }
    555      iter = iter->mNextIterator;
    556    }
    557  }
    558 
    559  T* popFront() {
    560    T* firstElm = mList.popFront();
    561    auto* iter = mIter;
    562    while (iter) {
    563      if (iter->current() == firstElm) {
    564        iter->setCurrent(nullptr);
    565      }
    566      iter = iter->mNextIterator;
    567    }
    568 
    569    return firstElm;
    570  }
    571 
    572  T* popBack() {
    573    T* lastElm = mList.popBack();
    574    auto* iter = mIter;
    575    while (iter) {
    576      if (iter->current() == lastElm) {
    577        iter->setCurrent(nullptr);
    578      } else if (iter->mNext && &*(iter->mNext) == lastElm) {
    579        iter->setNext(nullptr);
    580      }
    581      iter = iter->mNextIterator;
    582    }
    583 
    584    return lastElm;
    585  }
    586 
    587  void remove(T* aElm) {
    588    if (!mList.ElementProbablyInList(aElm)) {
    589      return;
    590    }
    591    auto* iter = mIter;
    592    while (iter) {
    593      if (iter->mNext && &*(iter->mNext) == aElm) {
    594        ++(iter->mNext);
    595      }
    596      if (iter->current() == aElm) {
    597        iter->setCurrent(nullptr);
    598      }
    599      iter = iter->mNextIterator;
    600    }
    601 
    602    mList.remove(aElm);
    603  }
    604 
    605 private:
    606  BaseListType mList;
    607  SafeIterator* mIter{nullptr};
    608 };
    609 
    610 }  // namespace mozilla
    611 
    612 #endif  // mozilla_DoublyLinkedList_h