LCOV - app.info - objdir/dist/include/mozilla/LinkedList.h

LCOV - code coverage report
| Current view: | directory - objdir/dist/include/mozilla - LinkedList.h (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 43 | 43 | 100.0 %
| Date: | 2012-06-02 | Functions: | 21 | 21 | 100.0 %

           1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
           2                 :  * vim: set ts=4 sw=4 tw=80 et cin:
           3                 :  *
           4                 :  * ***** BEGIN LICENSE BLOCK *****
           5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           6                 :  *
           7                 :  * The contents of this file are subject to the Mozilla Public License Version
           8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           9                 :  * the License. You may obtain a copy of the License at:
          10                 :  * http://www.mozilla.org/MPL/
          11                 :  *
          12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          14                 :  * for the specific language governing rights and limitations under the
          15                 :  * License.
          16                 :  *
          17                 :  * The Original Code is Mozilla Code.
          18                 :  *
          19                 :  * The Initial Developer of the Original Code is
          20                 :  *   The Mozilla Foundation
          21                 :  * Portions created by the Initial Developer are Copyright (C) 2012
          22                 :  * the Initial Developer. All Rights Reserved.
          23                 :  *
          24                 :  * Contributor(s):
          25                 :  *   Justin Lebar <justin.lebar@gmail.com>
          26                 :  *
          27                 :  * Alternatively, the contents of this file may be used under the terms of
          28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
          29                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          31                 :  * of those above. If you wish to allow use of your version of this file only
          32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          33                 :  * use your version of this file under the terms of the MPL, indicate your
          34                 :  * decision by deleting the provisions above and replace them with the notice
          35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          36                 :  * the provisions above, a recipient may use your version of this file under
          37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          38                 :  *
          39                 :  * ***** END LICENSE BLOCK ***** */
          40                 :
          41                 : /* A type-safe doubly-linked list class. */
          42                 :
          43                 : /*
          44                 :  * The classes LinkedList<T> and LinkedListElement<T> together form a
          45                 :  * convenient, type-safe doubly-linked list implementation.
          46                 :  *
          47                 :  * The class T which will be inserted into the linked list must inherit from
          48                 :  * LinkedListElement<T>.  A given object may be in only one linked list at a
          49                 :  * time.
          50                 :  *
          51                 :  * For example, you might use LinkedList in a simple observer list class as
          52                 :  * follows.
          53                 :  *
          54                 :  *   class Observer : public LinkedListElement<Observer>
          55                 :  *   {
          56                 :  *     void observe(char* topic) { ... }
          57                 :  *   };
          58                 :  *
          59                 :  *   class ObserverContainer
          60                 :  *   {
          61                 :  *   private:
          62                 :  *     LinkedList<ElemType> list;
          63                 :  *
          64                 :  *   public:
          65                 :  *
          66                 :  *     void addObserver(Observer* observer)
          67                 :  *     {
          68                 :  *       // Will assert if |observer| is part of another list.
          69                 :  *       list.insertBack(observer);
          70                 :  *     }
          71                 :  *
          72                 :  *     void removeObserver(Observer* observer)
          73                 :  *     {
          74                 :  *       // Will assert if |observer| is not part of some list.
          75                 :  *       observer.remove();
          76                 :  *     }
          77                 :  *
          78                 :  *     void notifyObservers(char* topic)
          79                 :  *     {
          80                 :  *       for (Observer* o = list.getFirst();
          81                 :  *            o != NULL;
          82                 :  *            o = o->getNext()) {
          83                 :  *         o->Observe(topic);
          84                 :  *       }
          85                 :  *     }
          86                 :  *   };
          87                 :  *
          88                 :  */
          89                 :
          90                 : #ifndef mozilla_LinkedList_h_
          91                 : #define mozilla_LinkedList_h_
          92                 :
          93                 : #include "mozilla/Assertions.h"
          94                 : #include "mozilla/Attributes.h"
          95                 :
          96                 : #ifdef __cplusplus
          97                 :
          98                 : namespace mozilla {
          99                 :
         100                 : template<typename T>
         101                 : class LinkedList;
         102                 :
         103                 : template<typename T>
         104                 : class LinkedListElement
         105                 : {
         106                 :     /*
         107                 :      * It's convenient that we return NULL when getNext() or getPrevious() hits
         108                 :      * the end of the list, but doing so costs an extra word of storage in each
         109                 :      * linked list node (to keep track of whether |this| is the sentinel node)
         110                 :      * and a branch on this value in getNext/getPrevious.
         111                 :      *
         112                 :      * We could get rid of the extra word of storage by shoving the "is
         113                 :      * sentinel" bit into one of the pointers, although this would, of course,
         114                 :      * have performance implications of its own.
         115                 :      *
         116                 :      * But the goal here isn't to win an award for the fastest or slimmest
         117                 :      * linked list; rather, we want a *convenient* linked list.  So we won't
         118                 :      * waste time guessing which micro-optimization strategy is best.
         119                 :      *
         120                 :      *
         121                 :      * Speaking of unnecessary work, it's worth addressing here why we wrote
         122                 :      * mozilla::LinkedList in the first place, instead of using stl::list.
         123                 :      *
         124                 :      * The key difference between mozilla::LinkedList and stl::list is that
         125                 :      * mozilla::LinkedList stores the prev/next pointers in the object itself,
         126                 :      * while stl::list stores the prev/next pointers in a list element which
         127                 :      * itself points to the object being stored.
         128                 :      *
         129                 :      * mozilla::LinkedList's approach makes it harder to store an object in more
         130                 :      * than one list.  But the upside is that you can call next() / prev() /
         131                 :      * remove() directly on the object.  With stl::list, you'd need to store a
         132                 :      * pointer to its iterator in the object in order to accomplish this.  Not
         133                 :      * only would this waste space, but you'd have to remember to update that
         134                 :      * pointer every time you added or removed the object from a list.
         135                 :      *
         136                 :      * In-place, constant-time removal is a killer feature of doubly-linked
         137                 :      * lists, and supporting this painlessly was a key design criterion.
         138                 :      */
         139                 :
         140                 : private:
         141                 :     LinkedListElement* next;
         142                 :     LinkedListElement* prev;
         143                 :     const bool isSentinel;
         144                 :
         145                 : public:
         146              28 :     LinkedListElement()
         147                 :         : next(this)
         148                 :         , prev(this)
         149              28 :         , isSentinel(false)
         150                 :     {
         151              28 :     }
         152                 :
         153                 :     /*
         154                 :      * Get the next element in the list, or NULL if this is the last element in
         155                 :      * the list.
         156                 :      */
         157            1431 :     T* getNext()
         158                 :     {
         159            1431 :         return next->asT();
         160                 :     }
         161                 :
         162                 :     /*
         163                 :      * Get the previous element in the list, or NULL if this is the first element
         164                 :      * in the list.
         165                 :      */
         166                 :     T* getPrevious()
         167                 :     {
         168                 :         return prev->asT();
         169                 :     }
         170                 :
         171                 :     /*
         172                 :      * Insert elem after this element in the list.  |this| must be part of a
         173                 :      * linked list when you call setNext(); otherwise, this method will assert.
         174                 :      */
         175                 :     void setNext(T* elem)
         176                 :     {
         177                 :         MOZ_ASSERT(isInList());
         178                 :         setNextUnsafe(elem);
         179                 :     }
         180                 :
         181                 :     /*
         182                 :      * Insert elem before this element in the list.  |this| must be part of a
         183                 :      * linked list when you call setPrevious(); otherwise, this method will
         184                 :      * assert.
         185                 :      */
         186                 :     void setPrevious(T* elem)
         187                 :     {
         188                 :         MOZ_ASSERT(isInList());
         189                 :         setPreviousUnsafe(elem);
         190                 :     }
         191                 :
         192                 :     /*
         193                 :      * Remove this element from the list which contains it.  If this element is
         194                 :      * not currently part of a linked list, this method asserts.
         195                 :      */
         196               8 :     void remove()
         197                 :     {
         198               8 :         MOZ_ASSERT(isInList());
         199                 :
         200               8 :         prev->next = next;
         201               8 :         next->prev = prev;
         202               8 :         next = this;
         203               8 :         prev = this;
         204               8 :     }
         205                 :
         206                 :     /*
         207                 :      * Return true if |this| part is of a linked list, and false otherwise.
         208                 :      */
         209              48 :     bool isInList()
         210                 :     {
         211              48 :         MOZ_ASSERT((next == this) == (prev == this));
         212              48 :         return next != this;
         213                 :     }
         214                 :
         215                 : private:
         216                 :     LinkedListElement& operator=(const LinkedList<T>& other) MOZ_DELETE;
         217                 :     LinkedListElement(const LinkedList<T>& other) MOZ_DELETE;
         218                 :
         219                 :     friend class LinkedList<T>;
         220                 :
         221                 :     enum NodeKind {
         222                 :         NODE_KIND_NORMAL,
         223                 :         NODE_KIND_SENTINEL
         224                 :     };
         225                 :
         226            1472 :     LinkedListElement(NodeKind nodeKind)
         227                 :         : next(this)
         228                 :         , prev(this)
         229            1472 :         , isSentinel(nodeKind == NODE_KIND_SENTINEL)
         230                 :     {
         231            1472 :     }
         232                 :
         233                 :     /*
         234                 :      * Return |this| cast to T* if we're a normal node, or return NULL if we're
         235                 :      * a sentinel node.
         236                 :      */
         237            1431 :     T* asT()
         238                 :     {
         239            1431 :         if (isSentinel)
         240            1427 :             return NULL;
         241                 :
         242               4 :         return static_cast<T*>(this);
         243                 :     }
         244                 :
         245                 :     /*
         246                 :      * Insert elem after this element, but don't check that this element is in
         247                 :      * the list.  This is called by LinkedList::insertFront().
         248                 :      */
         249                 :     void setNextUnsafe(T* elem)
         250                 :     {
         251                 :         LinkedListElement *listElem = static_cast<LinkedListElement*>(elem);
         252                 :         MOZ_ASSERT(!listElem->isInList());
         253                 :
         254                 :         listElem->next = this->next;
         255                 :         listElem->prev = this;
         256                 :         this->next->prev = listElem;
         257                 :         this->next = listElem;
         258                 :     }
         259                 :
         260                 :     /*
         261                 :      * Insert elem before this element, but don't check that this element is in
         262                 :      * the list.  This is called by LinkedList::insertBack().
         263                 :      */
         264               8 :     void setPreviousUnsafe(T* elem)
         265                 :     {
         266               8 :         LinkedListElement<T>* listElem = static_cast<LinkedListElement<T>*>(elem);
         267               8 :         MOZ_ASSERT(!listElem->isInList());
         268                 :
         269               8 :         listElem->next = this;
         270               8 :         listElem->prev = this->prev;
         271               8 :         this->prev->next = listElem;
         272               8 :         this->prev = listElem;
         273               8 :     }
         274                 : };
         275                 :
         276                 : template<typename T>
         277                 : class LinkedList
         278                 : {
         279                 : private:
         280                 :     LinkedListElement<T> sentinel;
         281                 :
         282                 : public:
         283                 :     LinkedList& operator=(const LinkedList<T>& other) MOZ_DELETE;
         284                 :     LinkedList(const LinkedList<T>& other) MOZ_DELETE;
         285                 :
         286            1472 :     LinkedList()
         287            1472 :         : sentinel(LinkedListElement<T>::NODE_KIND_SENTINEL)
         288                 :     {
         289            1472 :     }
         290                 :
         291                 :     /*
         292                 :      * Add elem to the front of the list.
         293                 :      */
         294                 :     void insertFront(T* elem)
         295                 :     {
         296                 :         /* Bypass setNext()'s this->isInList() assertion. */
         297                 :         sentinel.setNextUnsafe(elem);
         298                 :     }
         299                 :
         300                 :     /*
         301                 :      * Add elem to the back of the list.
         302                 :      */
         303               8 :     void insertBack(T* elem)
         304                 :     {
         305               8 :         sentinel.setPreviousUnsafe(elem);
         306               8 :     }
         307                 :
         308                 :     /*
         309                 :      * Get the first element of the list, or NULL if the list is empty.
         310                 :      */
         311                 :     T* getFirst()
         312                 :     {
         313                 :         return sentinel.getNext();
         314                 :     }
         315                 :
         316                 :     /*
         317                 :      * Get the last element of the list, or NULL if the list is empty.
         318                 :      */
         319                 :     T* getLast()
         320                 :     {
         321                 :         return sentinel.getPrevious();
         322                 :     }
         323                 :
         324                 :     /*
         325                 :      * Get and remove the first element of the list.  If the list is empty,
         326                 :      * return NULL.
         327                 :      */
         328            1431 :     T* popFirst()
         329                 :     {
         330            1431 :         T* ret = sentinel.getNext();
         331            1431 :         if (ret)
         332               4 :             static_cast<LinkedListElement<T>*>(ret)->remove();
         333                 :
         334            1431 :         return ret;
         335                 :     }
         336                 :
         337                 :     /*
         338                 :      * Get and remove the last element of the list.  If the list is empty,
         339                 :      * return NULL.
         340                 :      */
         341                 :     T* popLast()
         342                 :     {
         343                 :         T* ret = sentinel.getPrevious();
         344                 :         if (ret)
         345                 :             static_cast<LinkedListElement<T>*>(ret)->remove();
         346                 :
         347                 :         return ret;
         348                 :     }
         349                 :
         350                 :     /*
         351                 :      * Return true if the list is empty, or false otherwise.
         352                 :      */
         353               8 :     bool isEmpty()
         354                 :     {
         355               8 :         return !sentinel.isInList();
         356                 :     }
         357                 :
         358                 :     /*
         359                 :      * In a debug build, make sure that the list is sane (no cycles, consistent
         360                 :      * next/prev pointers, only one sentinel).  Has no effect in release builds.
         361                 :      */
         362                 :     void debugAssertIsSane()
         363                 :     {
         364                 : #ifdef DEBUG
         365                 :         /*
         366                 :          * Check for cycles in the forward singly-linked list using the
         367                 :          * tortoise/hare algorithm.
         368                 :          */
         369                 :         for (LinkedListElement<T>* slow = sentinel.next,
         370                 :                                  * fast1 = sentinel.next->next,
         371                 :                                  * fast2 = sentinel.next->next->next;
         372                 :              slow != sentinel && fast1 != sentinel && fast2 != sentinel;
         373                 :              slow = slow->next,
         374                 :              fast1 = fast2->next,
         375                 :              fast2 = fast1->next) {
         376                 :
         377                 :             MOZ_ASSERT(slow != fast1);
         378                 :             MOZ_ASSERT(slow != fast2);
         379                 :         }
         380                 :
         381                 :         /* Check for cycles in the backward singly-linked list. */
         382                 :         for (LinkedListElement<T>* slow = sentinel.prev,
         383                 :                                  * fast1 = sentinel.prev->prev,
         384                 :                                  * fast2 = sentinel.prev->prev->prev;
         385                 :              slow != sentinel && fast1 != sentinel && fast2 != sentinel;
         386                 :              slow = slow->prev,
         387                 :              fast1 = fast2->prev,
         388                 :              fast2 = fast1->prev) {
         389                 :
         390                 :             MOZ_ASSERT(slow != fast1);
         391                 :             MOZ_ASSERT(slow != fast2);
         392                 :         }
         393                 :
         394                 :         /*
         395                 :          * Check that |sentinel| is the only node in the list with
         396                 :          * isSentinel == true.
         397                 :          */
         398                 :         for (LinkedListElement<T>* elem = sentinel.next;
         399                 :              elem != sentinel;
         400                 :              elem = elem->next) {
         401                 :
         402                 :           MOZ_ASSERT(!elem->isSentinel);
         403                 :         }
         404                 :
         405                 :         /* Check that the next/prev pointers match up. */
         406                 :         LinkedListElement<T>* prev = sentinel;
         407                 :         LinkedListElement<T>* cur = sentinel.next;
         408                 :         do {
         409                 :             MOZ_ASSERT(cur->prev == prev);
         410                 :             MOZ_ASSERT(prev->next == cur);
         411                 :
         412                 :             prev = cur;
         413                 :             cur = cur->next;
         414                 :         } while (cur != sentinel);
         415                 : #endif /* ifdef DEBUG */
         416                 :     }
         417                 : };
         418                 :
         419                 : } /* namespace mozilla */
         420                 :
         421                 : #endif /* ifdef __cplusplus */
         422                 : #endif /* ifdef mozilla_LinkedList_h_ */

Generated by: LCOV version 1.7