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