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