tor-browser

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

SinglyLinkedList.h (6429B)


      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 #ifndef ds_SinglyLinkedList_h
      8 #define ds_SinglyLinkedList_h
      9 
     10 #include "mozilla/Assertions.h"
     11 
     12 #include <utility>
     13 
     14 namespace js {
     15 
     16 /*
     17 * Circular singly linked list that requires only one word per element and for
     18 * the list itself.
     19 *
     20 * Requires T has field |T::next| for the link pointer.
     21 *
     22 * The list only stores a pointer to the last element. Since the list is
     23 * circular, that provides access to the first element and allows insertion at
     24 * the start and end of the list.
     25 */
     26 template <typename T>
     27 class SinglyLinkedList {
     28  T* last_ = nullptr;
     29 
     30 public:
     31  // Create an empty list.
     32  SinglyLinkedList() {
     33    static_assert(std::is_same_v<decltype(T::next), T*>,
     34                  "SinglyLinkedList requires T has a next field of type T*");
     35    MOZ_ASSERT(isEmpty());
     36  }
     37 
     38  // Create a list from an existing non-circular linked list from |first| to
     39  // |last|.
     40  SinglyLinkedList(T* first, T* last) {
     41    MOZ_ASSERT(first);
     42    MOZ_ASSERT(last);
     43    MOZ_ASSERT(!last->next);
     44    last->next = first;
     45    last_ = last;
     46    checkContains(first);
     47    checkContains(last);
     48  }
     49 
     50  // It's not possible for elements to be present in more than one list, so copy
     51  // operations are not provided.
     52  SinglyLinkedList(const SinglyLinkedList& other) = delete;
     53  SinglyLinkedList& operator=(const SinglyLinkedList& other) = delete;
     54 
     55  SinglyLinkedList(SinglyLinkedList&& other) {
     56    MOZ_ASSERT(&other != this);
     57    std::swap(last_, other.last_);
     58    MOZ_ASSERT(other.isEmpty());
     59  }
     60  SinglyLinkedList& operator=(SinglyLinkedList&& other) {
     61    MOZ_ASSERT(&other != this);
     62    MOZ_ASSERT(isEmpty());
     63    std::swap(last_, other.last_);
     64    return *this;
     65  }
     66 
     67  ~SinglyLinkedList() { MOZ_ASSERT(isEmpty()); }
     68 
     69  bool isEmpty() const { return !last_; }
     70 
     71  // These return nullptr if the list is empty.
     72  T* getFirst() const {
     73    if (isEmpty()) {
     74      return nullptr;
     75    }
     76    T* element = last_->next;
     77    MOZ_ASSERT(element);
     78    return element;
     79  }
     80  T* getLast() const { return last_; }
     81 
     82  T* popFront() {
     83    MOZ_ASSERT(!isEmpty());
     84 
     85    T* element = last_->next;
     86 
     87    if (element == last_) {
     88      last_ = nullptr;
     89    } else {
     90      last_->next = element->next;
     91    }
     92 
     93    element->next = nullptr;
     94    return element;
     95  }
     96  // popBack cannot be implemented in constant time for a singly linked list.
     97 
     98  void pushFront(T* element) {
     99    MOZ_ASSERT(!element->next);
    100    if (isEmpty()) {
    101      element->next = element;
    102      last_ = element;
    103      return;
    104    }
    105 
    106    element->next = last_->next;
    107    last_->next = element;
    108  }
    109  void pushBack(T* element) {
    110    pushFront(element);
    111    moveFrontToBack();
    112  }
    113  void moveFrontToBack() {
    114    MOZ_ASSERT(!isEmpty());
    115    last_ = last_->next;
    116    MOZ_ASSERT(!isEmpty());
    117  }
    118 
    119  void append(SinglyLinkedList&& other) {
    120    MOZ_ASSERT(&other != this);
    121 
    122    if (other.isEmpty()) {
    123      return;
    124    }
    125 
    126    if (isEmpty()) {
    127      *this = std::move(other);
    128      return;
    129    }
    130 
    131    T* firstElement = getFirst();
    132    getLast()->next = other.getFirst();
    133    other.getLast()->next = firstElement;
    134    last_ = other.getLast();
    135    other.last_ = nullptr;
    136  }
    137 
    138  void prepend(SinglyLinkedList&& other) {
    139    MOZ_ASSERT(&other != this);
    140 
    141    if (other.isEmpty()) {
    142      return;
    143    }
    144 
    145    if (isEmpty()) {
    146      *this = std::move(other);
    147      return;
    148    }
    149 
    150    T* firstElement = getFirst();
    151    getLast()->next = other.getFirst();
    152    other.getLast()->next = firstElement;
    153    other.last_ = nullptr;
    154  }
    155 
    156  // Remove all elements between |fromExclusive| and |toInclusive|. Return the
    157  // removed list segment as a non-circular linked list.
    158  //
    159  // The fact that the first parameter is exclusive is a requirement for
    160  // implementing this in constant time for a singly linked list.
    161  T* removeRange(T* fromExclusive, T* toInclusive) {
    162    MOZ_ASSERT(fromExclusive);
    163    MOZ_ASSERT(toInclusive);
    164    MOZ_ASSERT(fromExclusive != toInclusive);
    165    MOZ_ASSERT(!isEmpty());
    166 
    167 #ifdef DEBUG
    168    size_t index = 0;
    169    size_t fromIndex = SIZE_MAX;
    170    size_t toIndex = SIZE_MAX;
    171    for (T* element = getFirst(); element; element = element->next) {
    172      if (element == fromExclusive) {
    173        fromIndex = index;
    174      }
    175      if (element == toInclusive) {
    176        toIndex = index;
    177      }
    178      index++;
    179      if (index == 100) {
    180        break;
    181      }
    182    }
    183    if (index < 100) {
    184      MOZ_ASSERT(fromIndex != SIZE_MAX);
    185      MOZ_ASSERT(toIndex != SIZE_MAX);
    186      MOZ_ASSERT(fromIndex < toIndex);
    187    }
    188 #endif
    189 
    190    T* result = fromExclusive->next;
    191    fromExclusive->next = toInclusive->next;
    192    toInclusive->next = nullptr;
    193 
    194    if (last_ == toInclusive) {
    195      last_ = fromExclusive;
    196    }
    197 
    198    return result;
    199  }
    200 
    201  // template <typename T>
    202  class Iterator {
    203    T* i = nullptr;
    204    T* last = nullptr;
    205 
    206   public:
    207    Iterator() = default;
    208    explicit Iterator(const SinglyLinkedList& list)
    209        : i(list.getFirst()), last(list.getLast()) {}
    210    Iterator(const SinglyLinkedList& list, T* first)
    211        : i(first), last(list.getLast()) {}
    212    bool done() const { return !i; }
    213    void next() {
    214      MOZ_ASSERT(!done());
    215      i = i == last ? nullptr : i->next;
    216    }
    217    T* get() const {
    218      MOZ_ASSERT(!done());
    219      return i;
    220    }
    221 
    222    T& operator*() const { return *get(); }
    223    T* operator->() const { return get(); }
    224  };
    225 
    226  Iterator iter() const { return Iterator(*this); }
    227 
    228  Iterator iterFrom(T* fromInclusive) {
    229    checkContains(fromInclusive);
    230    return Iterator(*this, fromInclusive);
    231  }
    232 
    233  void checkContains(T* element) {
    234 #ifdef DEBUG
    235    size_t i = 0;
    236    for (Iterator iter(*this); !iter.done(); iter.next()) {
    237      if (iter.get() == element) {
    238        return;  // Found.
    239      }
    240      i++;
    241      if (i == 100) {
    242        return;  // Limit time spent checking.
    243      }
    244    }
    245    MOZ_CRASH("Element not found");
    246 #endif
    247  }
    248 
    249  // Extracts a non-circular linked list and clears this object.
    250  T* release() {
    251    if (isEmpty()) {
    252      return nullptr;
    253    }
    254 
    255    T* list = getFirst();
    256    MOZ_ASSERT(last_->next);
    257    last_->next = nullptr;
    258    last_ = nullptr;
    259    return list;
    260  }
    261 };
    262 
    263 } /* namespace js */
    264 
    265 #endif /* ds_SinglyLinkedList_h */