LCOV - code coverage report
Current view: directory - js/src/ds - InlineMap.h (source / functions) Found Hit Coverage
Test: app.info Lines: 167 164 98.2 %
Date: 2012-04-07 Functions: 93 91 97.8 %

       1                 : /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: set ts=4 sw=4 et tw=99 ft=cpp:
       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 SpiderMonkey JavaScript engine.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  * Mozilla Corporation.
      21                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *   Chris Leary <cdleary@mozilla.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                 : #ifndef InlineMap_h__
      42                 : #define InlineMap_h__
      43                 : 
      44                 : #include "js/HashTable.h"
      45                 : 
      46                 : namespace js {
      47                 : 
      48                 : /*
      49                 :  * A type can only be used as an InlineMap key if zero is an invalid key value
      50                 :  * (and thus may be used as a tombstone value by InlineMap).
      51                 :  */
      52                 : template <typename T> struct ZeroIsReserved         { static const bool result = false; };
      53                 : template <typename T> struct ZeroIsReserved<T *>    { static const bool result = true; };
      54                 : 
      55                 : template <typename K, typename V, size_t InlineElems>
      56                 : class InlineMap
      57                 : {
      58                 :   public:
      59                 :     typedef HashMap<K, V, DefaultHasher<K>, TempAllocPolicy> WordMap;
      60                 : 
      61                 :     struct InlineElem
      62                 :     {
      63                 :         K key;
      64                 :         V value;
      65                 :     };
      66                 : 
      67                 :   private:
      68                 :     typedef typename WordMap::Ptr       WordMapPtr;
      69                 :     typedef typename WordMap::AddPtr    WordMapAddPtr;
      70                 :     typedef typename WordMap::Range     WordMapRange;
      71                 : 
      72                 :     size_t          inlNext;
      73                 :     size_t          inlCount;
      74                 :     InlineElem      inl[InlineElems];
      75                 :     WordMap         map;
      76                 : 
      77          226506 :     void checkStaticInvariants() {
      78                 :         JS_STATIC_ASSERT(ZeroIsReserved<K>::result);
      79          226506 :     }
      80                 : 
      81        14284613 :     bool usingMap() const {
      82        14284613 :         return inlNext > InlineElems;
      83                 :     }
      84                 : 
      85            1341 :     bool switchToMap() {
      86            1341 :         JS_ASSERT(inlNext == InlineElems);
      87                 : 
      88            1341 :         if (map.initialized()) {
      89             261 :             map.clear();
      90                 :         } else {
      91            1080 :             if (!map.init(count()))
      92               0 :                 return false;
      93            1080 :             JS_ASSERT(map.initialized());
      94                 :         }
      95                 : 
      96           33525 :         for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
      97           32184 :             if (it->key && !map.putNew(it->key, it->value))
      98               0 :                 return false;
      99                 :         }
     100                 : 
     101            1341 :         inlNext = InlineElems + 1;
     102            1341 :         JS_ASSERT(map.count() == inlCount);
     103            1341 :         JS_ASSERT(usingMap());
     104            1341 :         return true;
     105                 :     }
     106                 : 
     107                 :     JS_NEVER_INLINE
     108            1341 :     bool switchAndAdd(const K &key, const V &value) {
     109            1341 :         if (!switchToMap())
     110               0 :             return false;
     111                 : 
     112            1341 :         return map.putNew(key, value);
     113                 :     }
     114                 : 
     115                 :   public:
     116          226506 :     explicit InlineMap(JSContext *cx)
     117          226506 :       : inlNext(0), inlCount(0), map(cx) {
     118          226506 :         checkStaticInvariants(); /* Force the template to instantiate the static invariants. */
     119          226506 :     }
     120                 : 
     121                 :     class Entry
     122                 :     {
     123                 :         friend class InlineMap;
     124                 :         const K &key_;
     125                 :         const V &value_;
     126                 : 
     127          289603 :         Entry(const K &key, const V &value) : key_(key), value_(value) {}
     128                 : 
     129                 :       public:
     130           94473 :         const K &key() { return key_; }
     131          195130 :         const V &value() { return value_; }
     132                 :     }; /* class Entry */
     133                 : 
     134                 :     class Ptr
     135                 :     {
     136                 :         friend class InlineMap;
     137                 : 
     138                 :         WordMapPtr  mapPtr;
     139                 :         InlineElem  *inlPtr;
     140                 :         bool        isInlinePtr;
     141                 : 
     142                 :         typedef Ptr ******* ConvertibleToBool;
     143                 : 
     144         1265040 :         explicit Ptr(WordMapPtr p) : mapPtr(p), isInlinePtr(false) {}
     145         4242045 :         explicit Ptr(InlineElem *ie) : inlPtr(ie), isInlinePtr(true) {}
     146                 :         void operator==(const Ptr &other);
     147                 : 
     148                 :       public:
     149                 :         /* Leaves Ptr uninitialized. */
     150          391628 :         Ptr() {
     151                 : #ifdef DEBUG
     152          391628 :             inlPtr = (InlineElem *) 0xbad;
     153          391628 :             isInlinePtr = true;
     154                 : #endif
     155          391628 :         }
     156                 : 
     157                 :         /* Default copy constructor works for this structure. */
     158                 : 
     159         6956132 :         bool found() const {
     160         6956132 :             return isInlinePtr ? bool(inlPtr) : mapPtr.found();
     161                 :         }
     162                 : 
     163         5831483 :         operator ConvertibleToBool() const {
     164         5831483 :             return ConvertibleToBool(found());
     165                 :         }
     166                 : 
     167                 :         K &key() {
     168                 :             JS_ASSERT(found());
     169                 :             return isInlinePtr ? inlPtr->key : mapPtr->key;
     170                 :         }
     171                 : 
     172         1124649 :         V &value() {
     173         1124649 :             JS_ASSERT(found());
     174         1124649 :             return isInlinePtr ? inlPtr->value : mapPtr->value;
     175                 :         }
     176                 :     }; /* class Ptr */
     177                 : 
     178                 :     class AddPtr
     179         5332454 :     {
     180                 :         friend class InlineMap;
     181                 : 
     182                 :         WordMapAddPtr   mapAddPtr;
     183                 :         InlineElem      *inlAddPtr;
     184                 :         bool            isInlinePtr;
     185                 :         /* Indicates whether inlAddPtr is a found result or an add pointer. */
     186                 :         bool            inlPtrFound;
     187                 : 
     188         4665635 :         AddPtr(InlineElem *ptr, bool found)
     189         4665635 :           : inlAddPtr(ptr), isInlinePtr(true), inlPtrFound(found)
     190         4665635 :         {}
     191                 : 
     192          666819 :         AddPtr(const WordMapAddPtr &p) : mapAddPtr(p), isInlinePtr(false) {}
     193                 : 
     194                 :         void operator==(const AddPtr &other);
     195                 : 
     196                 :         typedef AddPtr ******* ConvertibleToBool;
     197                 : 
     198                 :       public:
     199                 :         AddPtr() {}
     200                 : 
     201        11965365 :         bool found() const {
     202        11965365 :             return isInlinePtr ? inlPtrFound : mapAddPtr.found();
     203                 :         }
     204                 : 
     205         7190842 :         operator ConvertibleToBool() const {
     206         7190842 :             return found() ? ConvertibleToBool(1) : ConvertibleToBool(0);
     207                 :         }
     208                 : 
     209         3479877 :         V &value() {
     210         3479877 :             JS_ASSERT(found());
     211         3479877 :             if (isInlinePtr)
     212         3375450 :                 return inlAddPtr->value;
     213          104427 :             return mapAddPtr->value;
     214                 :         }
     215                 :     }; /* class AddPtr */
     216                 : 
     217         2171059 :     size_t count() {
     218         2171059 :         return usingMap() ? map.count() : inlCount;
     219                 :     }
     220                 : 
     221           65147 :     bool empty() const {
     222           65147 :         return usingMap() ? map.empty() : !inlCount;
     223                 :     }
     224                 : 
     225          688356 :     void clear() {
     226          688356 :         inlNext = 0;
     227          688356 :         inlCount = 0;
     228          688356 :     }
     229                 : 
     230          776382 :     bool isMap() const {
     231          776382 :         return usingMap();
     232                 :     }
     233                 : 
     234             648 :     const WordMap &asMap() const {
     235             648 :         JS_ASSERT(isMap());
     236             648 :         return map;
     237                 :     }
     238                 : 
     239          258362 :     const InlineElem *asInline() const {
     240          258362 :         JS_ASSERT(!isMap());
     241          258362 :         return inl;
     242                 :     }
     243                 : 
     244          258362 :     const InlineElem *inlineEnd() const {
     245          258362 :         JS_ASSERT(!isMap());
     246          258362 :         return inl + inlNext;
     247                 :     }
     248                 : 
     249                 :     JS_ALWAYS_INLINE
     250         5507085 :     Ptr lookup(const K &key) {
     251         5507085 :         if (usingMap())
     252         1265040 :             return Ptr(map.lookup(key));
     253                 : 
     254         7170940 :         for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
     255         3676048 :             if (it->key == key)
     256          747153 :                 return Ptr(it);
     257                 :         }
     258                 : 
     259         3494892 :         return Ptr(NULL);
     260                 :     }
     261                 : 
     262                 :     JS_ALWAYS_INLINE
     263         5332454 :     AddPtr lookupForAdd(const K &key) {
     264         5332454 :         if (usingMap())
     265          666819 :             return AddPtr(map.lookupForAdd(key));
     266                 : 
     267        17500888 :         for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
     268        16204718 :             if (it->key == key)
     269         3369465 :                 return AddPtr(it, true);
     270                 :         }
     271                 : 
     272                 :         /*
     273                 :          * The add pointer that's returned here may indicate the limit entry of
     274                 :          * the linear space, in which case the |add| operation will initialize
     275                 :          * the map if necessary and add the entry there.
     276                 :          */
     277         1296170 :         return AddPtr(inl + inlNext, false);
     278                 :     }
     279                 : 
     280                 :     JS_ALWAYS_INLINE
     281         1858388 :     bool add(AddPtr &p, const K &key, const V &value) {
     282         1858388 :         JS_ASSERT(!p);
     283                 : 
     284         1858388 :         if (p.isInlinePtr) {
     285         1295987 :             InlineElem *addPtr = p.inlAddPtr;
     286         1295987 :             JS_ASSERT(addPtr == inl + inlNext);
     287                 : 
     288                 :             /* Switching to map mode before we add this pointer. */
     289         1295987 :             if (addPtr == inl + InlineElems)
     290            1341 :                 return switchAndAdd(key, value);
     291                 : 
     292         1294646 :             JS_ASSERT(!p.found());
     293         1294646 :             JS_ASSERT(uintptr_t(inl + inlNext) == uintptr_t(p.inlAddPtr));
     294         1294646 :             p.inlAddPtr->key = key;
     295         1294646 :             p.inlAddPtr->value = value;
     296         1294646 :             ++inlCount;
     297         1294646 :             ++inlNext;
     298         1294646 :             return true;
     299                 :         }
     300                 : 
     301          562401 :         return map.add(p.mapAddPtr, key, value);
     302                 :     }
     303                 : 
     304                 :     JS_ALWAYS_INLINE
     305             216 :     bool put(const K &key, const V &value) {
     306             432 :         AddPtr p = lookupForAdd(key);
     307             216 :         if (p) {
     308              18 :             p.value() = value;
     309              18 :             return true;
     310                 :         }
     311             198 :         return add(p, key, value);
     312                 :     }
     313                 : 
     314          324398 :     void remove(Ptr p) {
     315          324398 :         JS_ASSERT(p);
     316          324398 :         if (p.isInlinePtr) {
     317           20729 :             JS_ASSERT(inlCount > 0);
     318           20729 :             JS_ASSERT(p.inlPtr->key != NULL);
     319           20729 :             p.inlPtr->key = NULL;
     320           20729 :             --inlCount;
     321           20729 :             return;
     322                 :         }
     323          303669 :         JS_ASSERT(map.initialized() && usingMap());
     324          303669 :         map.remove(p.mapPtr);
     325                 :     }
     326                 : 
     327            3429 :     void remove(const K &key) {
     328            3429 :         if (Ptr p = lookup(key))
     329            3411 :             remove(p);
     330            3429 :     }
     331                 : 
     332                 :     class Range
     333                 :     {
     334                 :         friend class InlineMap;
     335                 : 
     336                 :         WordMapRange    mapRange;
     337                 :         InlineElem      *cur;
     338                 :         InlineElem      *end;
     339                 :         bool            isInline;
     340                 : 
     341             189 :         explicit Range(WordMapRange r)
     342                 :           : cur(NULL), end(NULL), /* Avoid GCC 4.3.3 over-warning. */
     343             189 :             isInline(false) {
     344             189 :             mapRange = r;
     345             189 :             JS_ASSERT(!isInlineRange());
     346             189 :         }
     347                 : 
     348          127287 :         Range(const InlineElem *begin, const InlineElem *end_)
     349                 :           : cur(const_cast<InlineElem *>(begin)),
     350                 :             end(const_cast<InlineElem *>(end_)),
     351          127287 :             isInline(true) {
     352          127287 :             advancePastNulls(cur);
     353          127287 :             JS_ASSERT(isInlineRange());
     354          127287 :         }
     355                 : 
     356         1559414 :         bool checkInlineRangeInvariants() const {
     357         1559414 :             JS_ASSERT(uintptr_t(cur) <= uintptr_t(end));
     358         1559414 :             JS_ASSERT_IF(cur != end, cur->key != NULL);
     359         1559414 :             return true;
     360                 :         }
     361                 : 
     362         1591346 :         bool isInlineRange() const {
     363         1591346 :             JS_ASSERT_IF(isInline, checkInlineRangeInvariants());
     364         1591346 :             return isInline;
     365                 :         }
     366                 : 
     367          312669 :         void advancePastNulls(InlineElem *begin) {
     368          312669 :             InlineElem *newCur = begin;
     369          626724 :             while (newCur < end && NULL == newCur->key)
     370            1386 :                 ++newCur;
     371          312669 :             JS_ASSERT(uintptr_t(newCur) <= uintptr_t(end));
     372          312669 :             cur = newCur;
     373          312669 :         }
     374                 : 
     375          185382 :         void bumpCurPtr() {
     376          185382 :             JS_ASSERT(isInlineRange());
     377          185382 :             advancePastNulls(cur + 1);
     378          185382 :         }
     379                 : 
     380                 :         void operator==(const Range &other);
     381                 : 
     382                 :       public:
     383          798265 :         bool empty() const {
     384          798265 :             return isInlineRange() ? cur == end : mapRange.empty();
     385                 :         }
     386                 : 
     387          289603 :         Entry front() {
     388          289603 :             JS_ASSERT(!empty());
     389          289603 :             if (isInlineRange())
     390          281683 :                 return Entry(cur->key, cur->value);
     391            7920 :             return Entry(mapRange.front().key, mapRange.front().value);
     392                 :         }
     393                 : 
     394          190620 :         void popFront() {
     395          190620 :             JS_ASSERT(!empty());
     396          190620 :             if (isInlineRange())
     397          185382 :                 bumpCurPtr();
     398                 :             else
     399            5238 :                 mapRange.popFront();
     400          190620 :         }
     401                 :     }; /* class Range */
     402                 : 
     403          127476 :     Range all() const {
     404          127476 :         return usingMap() ? Range(map.all()) : Range(inl, inl + inlNext);
     405                 :     }
     406                 : }; /* class InlineMap */
     407                 : 
     408                 : } /* namespace js */
     409                 : 
     410                 : #endif

Generated by: LCOV version 1.7