LCOV - code coverage report
Current view: directory - js/src/ds - InlineMap.h (source / functions) Found Hit Coverage
Test: app.info Lines: 166 163 98.2 %
Date: 2012-04-21 Functions: 91 90 98.9 %

       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          570197 :     void checkStaticInvariants() {
      78                 :         JS_STATIC_ASSERT(ZeroIsReserved<K>::result);
      79          570197 :     }
      80                 : 
      81       111645665 :     bool usingMap() const {
      82       111645665 :         return inlNext > InlineElems;
      83                 :     }
      84                 : 
      85           92653 :     bool switchToMap() {
      86           92653 :         JS_ASSERT(inlNext == InlineElems);
      87                 : 
      88           92653 :         if (map.initialized()) {
      89           69751 :             map.clear();
      90                 :         } else {
      91           22902 :             if (!map.init(count()))
      92               0 :                 return false;
      93           22902 :             JS_ASSERT(map.initialized());
      94                 :         }
      95                 : 
      96         2316325 :         for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
      97         2223672 :             if (it->key && !map.putNew(it->key, it->value))
      98               0 :                 return false;
      99                 :         }
     100                 : 
     101           92653 :         inlNext = InlineElems + 1;
     102           92653 :         JS_ASSERT(map.count() == inlCount);
     103           92653 :         JS_ASSERT(usingMap());
     104           92653 :         return true;
     105                 :     }
     106                 : 
     107                 :     JS_NEVER_INLINE
     108           92653 :     bool switchAndAdd(const K &key, const V &value) {
     109           92653 :         if (!switchToMap())
     110               0 :             return false;
     111                 : 
     112           92653 :         return map.putNew(key, value);
     113                 :     }
     114                 : 
     115                 :   public:
     116          570197 :     explicit InlineMap(JSContext *cx)
     117          570197 :       : inlNext(0), inlCount(0), map(cx) {
     118          570197 :         checkStaticInvariants(); /* Force the template to instantiate the static invariants. */
     119          570197 :     }
     120                 : 
     121                 :     class Entry
     122                 :     {
     123                 :         friend class InlineMap;
     124                 :         const K &key_;
     125                 :         const V &value_;
     126                 : 
     127        12399202 :         Entry(const K &key, const V &value) : key_(key), value_(value) {}
     128                 : 
     129                 :       public:
     130         2420120 :         const K &key() { return key_; }
     131         9979082 :         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         3050680 :         explicit Ptr(WordMapPtr p) : mapPtr(p), isInlinePtr(false) {}
     145        25251676 :         explicit Ptr(InlineElem *ie) : inlPtr(ie), isInlinePtr(true) {}
     146                 :         void operator==(const Ptr &other);
     147                 : 
     148                 :       public:
     149                 :         /* Leaves Ptr uninitialized. */
     150         1633908 :         Ptr() {
     151                 : #ifdef DEBUG
     152         1633908 :             inlPtr = (InlineElem *) 0xbad;
     153         1633908 :             isInlinePtr = true;
     154                 : #endif
     155         1633908 :         }
     156                 : 
     157                 :         /* Default copy constructor works for this structure. */
     158                 : 
     159        37883727 :         bool found() const {
     160        37883727 :             return isInlinePtr ? bool(inlPtr) : mapPtr.found();
     161                 :         }
     162                 : 
     163        29112575 :         operator ConvertibleToBool() const {
     164        29112575 :             return ConvertibleToBool(found());
     165                 :         }
     166                 : 
     167                 :         K &key() {
     168                 :             JS_ASSERT(found());
     169                 :             return isInlinePtr ? inlPtr->key : mapPtr->key;
     170                 :         }
     171                 : 
     172         8771152 :         V &value() {
     173         8771152 :             JS_ASSERT(found());
     174         8771152 :             return isInlinePtr ? inlPtr->value : mapPtr->value;
     175                 :         }
     176                 :     }; /* class Ptr */
     177                 : 
     178                 :     class AddPtr
     179                 :     {
     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        32074137 :         AddPtr(InlineElem *ptr, bool found)
     189        32074137 :           : inlAddPtr(ptr), isInlinePtr(true), inlPtrFound(found)
     190        32074137 :         {}
     191                 : 
     192        10711402 :         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        99548547 :         bool found() const {
     202        99548547 :             return isInlinePtr ? inlPtrFound : mapAddPtr.found();
     203                 :         }
     204                 : 
     205        62769744 :         operator ConvertibleToBool() const {
     206        62769744 :             return found() ? ConvertibleToBool(1) : ConvertibleToBool(0);
     207                 :         }
     208                 : 
     209        22825730 :         V &value() {
     210        22825730 :             JS_ASSERT(found());
     211        22825730 :             if (isInlinePtr)
     212        18049139 :                 return inlAddPtr->value;
     213         4776591 :             return mapAddPtr->value;
     214                 :         }
     215                 :     }; /* class AddPtr */
     216                 : 
     217        31798926 :     size_t count() {
     218        31798926 :         return usingMap() ? map.count() : inlCount;
     219                 :     }
     220                 : 
     221         1628355 :     bool empty() const {
     222         1628355 :         return usingMap() ? map.empty() : !inlCount;
     223                 :     }
     224                 : 
     225         3282077 :     void clear() {
     226         3282077 :         inlNext = 0;
     227         3282077 :         inlCount = 0;
     228         3282077 :     }
     229                 : 
     230         3247596 :     bool isMap() const {
     231         3247596 :         return usingMap();
     232                 :     }
     233                 : 
     234           73041 :     const WordMap &asMap() const {
     235           73041 :         JS_ASSERT(isMap());
     236           73041 :         return map;
     237                 :     }
     238                 : 
     239         1033838 :     const InlineElem *asInline() const {
     240         1033838 :         JS_ASSERT(!isMap());
     241         1033838 :         return inl;
     242                 :     }
     243                 : 
     244         1033838 :     const InlineElem *inlineEnd() const {
     245         1033838 :         JS_ASSERT(!isMap());
     246         1033838 :         return inl + inlNext;
     247                 :     }
     248                 : 
     249                 :     JS_ALWAYS_INLINE
     250        28302356 :     Ptr lookup(const K &key) {
     251        28302356 :         if (usingMap())
     252         3050680 :             return Ptr(map.lookup(key));
     253                 : 
     254        98726275 :         for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
     255        80585670 :             if (it->key == key)
     256         7111071 :                 return Ptr(it);
     257                 :         }
     258                 : 
     259        18140605 :         return Ptr(NULL);
     260                 :     }
     261                 : 
     262                 :     JS_ALWAYS_INLINE
     263        42785539 :     AddPtr lookupForAdd(const K &key) {
     264        42785539 :         if (usingMap())
     265        10711402 :             return AddPtr(map.lookupForAdd(key));
     266                 : 
     267       166731040 :         for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
     268       152685131 :             if (it->key == key)
     269        18028228 :                 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        14045909 :         return AddPtr(inl + inlNext, false);
     278                 :     }
     279                 : 
     280                 :     JS_ALWAYS_INLINE
     281        19984205 :     bool add(AddPtr &p, const K &key, const V &value) {
     282        19984205 :         JS_ASSERT(!p);
     283                 : 
     284        19984205 :         if (p.isInlinePtr) {
     285        14045726 :             InlineElem *addPtr = p.inlAddPtr;
     286        14045726 :             JS_ASSERT(addPtr == inl + inlNext);
     287                 : 
     288                 :             /* Switching to map mode before we add this pointer. */
     289        14045726 :             if (addPtr == inl + InlineElems)
     290           92653 :                 return switchAndAdd(key, value);
     291                 : 
     292        13953073 :             JS_ASSERT(!p.found());
     293        13953073 :             JS_ASSERT(uintptr_t(inl + inlNext) == uintptr_t(p.inlAddPtr));
     294        13953073 :             p.inlAddPtr->key = key;
     295        13953073 :             p.inlAddPtr->value = value;
     296        13953073 :             ++inlCount;
     297        13953073 :             ++inlNext;
     298        13953073 :             return true;
     299                 :         }
     300                 : 
     301         5938479 :         return map.add(p.mapAddPtr, key, value);
     302                 :     }
     303                 : 
     304                 :     JS_ALWAYS_INLINE
     305             232 :     bool put(const K &key, const V &value) {
     306             232 :         AddPtr p = lookupForAdd(key);
     307             232 :         if (p) {
     308              20 :             p.value() = value;
     309              20 :             return true;
     310                 :         }
     311             212 :         return add(p, key, value);
     312                 :     }
     313                 : 
     314          810219 :     void remove(Ptr p) {
     315          810219 :         JS_ASSERT(p);
     316          810219 :         if (p.isInlinePtr) {
     317          462800 :             JS_ASSERT(inlCount > 0);
     318          462800 :             JS_ASSERT(p.inlPtr->key != NULL);
     319          462800 :             p.inlPtr->key = NULL;
     320          462800 :             --inlCount;
     321          462800 :             return;
     322                 :         }
     323          347419 :         JS_ASSERT(map.initialized() && usingMap());
     324          347419 :         map.remove(p.mapPtr);
     325                 :     }
     326                 : 
     327           46218 :     void remove(const K &key) {
     328           46218 :         if (Ptr p = lookup(key))
     329           46198 :             remove(p);
     330           46218 :     }
     331                 : 
     332                 :     class Range
     333                 :     {
     334                 :         friend class InlineMap;
     335                 : 
     336                 :         WordMapRange    mapRange;
     337                 :         InlineElem      *cur;
     338                 :         InlineElem      *end;
     339                 :         bool            isInline;
     340                 : 
     341           12754 :         explicit Range(WordMapRange r)
     342                 :           : cur(NULL), end(NULL), /* Avoid GCC 4.3.3 over-warning. */
     343           12754 :             isInline(false) {
     344           12754 :             mapRange = r;
     345           12754 :             JS_ASSERT(!isInlineRange());
     346           12754 :         }
     347                 : 
     348         3430067 :         Range(const InlineElem *begin, const InlineElem *end_)
     349                 :           : cur(const_cast<InlineElem *>(begin)),
     350                 :             end(const_cast<InlineElem *>(end_)),
     351         3430067 :             isInline(true) {
     352         3430067 :             advancePastNulls(cur);
     353         3430067 :             JS_ASSERT(isInlineRange());
     354         3430067 :         }
     355                 : 
     356        69148339 :         bool checkInlineRangeInvariants() const {
     357        69148339 :             JS_ASSERT(uintptr_t(cur) <= uintptr_t(end));
     358        69148339 :             JS_ASSERT_IF(cur != end, cur->key != NULL);
     359        69148339 :             return true;
     360                 :         }
     361                 : 
     362        70942971 :         bool isInlineRange() const {
     363        70942971 :             JS_ASSERT_IF(isInline, checkInlineRangeInvariants());
     364        70942971 :             return isInline;
     365                 :         }
     366                 : 
     367        13005105 :         void advancePastNulls(InlineElem *begin) {
     368        13005105 :             InlineElem *newCur = begin;
     369        26222911 :             while (newCur < end && NULL == newCur->key)
     370          212701 :                 ++newCur;
     371        13005105 :             JS_ASSERT(uintptr_t(newCur) <= uintptr_t(end));
     372        13005105 :             cur = newCur;
     373        13005105 :         }
     374                 : 
     375         9575038 :         void bumpCurPtr() {
     376         9575038 :             JS_ASSERT(isInlineRange());
     377         9575038 :             advancePastNulls(cur + 1);
     378         9575038 :         }
     379                 : 
     380                 :         void operator==(const Range &other);
     381                 : 
     382                 :       public:
     383        35629054 :         bool empty() const {
     384        35629054 :             return isInlineRange() ? cur == end : mapRange.empty();
     385                 :         }
     386                 : 
     387        12399202 :         Entry front() {
     388        12399202 :             JS_ASSERT(!empty());
     389        12399202 :             if (isInlineRange())
     390        11997367 :                 return Entry(cur->key, cur->value);
     391          401835 :             return Entry(mapRange.front().key, mapRange.front().value);
     392                 :         }
     393                 : 
     394         9896856 :         void popFront() {
     395         9896856 :             JS_ASSERT(!empty());
     396         9896856 :             if (isInlineRange())
     397         9575038 :                 bumpCurPtr();
     398                 :             else
     399          321818 :                 mapRange.popFront();
     400         9896856 :         }
     401                 :     }; /* class Range */
     402                 : 
     403         3442821 :     Range all() const {
     404         3442821 :         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