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