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-06-02 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          571145 :     void checkStaticInvariants() {
      78                 :         JS_STATIC_ASSERT(ZeroIsReserved<K>::result);
      79          571145 :     }
      80                 : 
      81       111864166 :     bool usingMap() const {
      82       111864166 :         return inlNext > InlineElems;
      83                 :     }
      84                 : 
      85           92830 :     bool switchToMap() {
      86           92830 :         JS_ASSERT(inlNext == InlineElems);
      87                 : 
      88           92830 :         if (map.initialized()) {
      89           69887 :             map.clear();
      90                 :         } else {
      91           22943 :             if (!map.init(count()))
      92               0 :                 return false;
      93           22943 :             JS_ASSERT(map.initialized());
      94                 :         }
      95                 : 
      96         2320750 :         for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
      97         2227920 :             if (it->key && !map.putNew(it->key, it->value))
      98               0 :                 return false;
      99                 :         }
     100                 : 
     101           92830 :         inlNext = InlineElems + 1;
     102           92830 :         JS_ASSERT(map.count() == inlCount);
     103           92830 :         JS_ASSERT(usingMap());
     104           92830 :         return true;
     105                 :     }
     106                 : 
     107                 :     JS_NEVER_INLINE
     108           92830 :     bool switchAndAdd(const K &key, const V &value) {
     109           92830 :         if (!switchToMap())
     110               0 :             return false;
     111                 : 
     112           92830 :         return map.putNew(key, value);
     113                 :     }
     114                 : 
     115                 :   public:
     116          571145 :     explicit InlineMap(JSContext *cx)
     117          571145 :       : inlNext(0), inlCount(0), map(cx) {
     118          571145 :         checkStaticInvariants(); /* Force the template to instantiate the static invariants. */
     119          571145 :     }
     120                 : 
     121                 :     class Entry
     122                 :     {
     123                 :         friend class InlineMap;
     124                 :         const K &key_;
     125                 :         const V &value_;
     126                 : 
     127        12420190 :         Entry(const K &key, const V &value) : key_(key), value_(value) {}
     128                 : 
     129                 :       public:
     130         2424962 :         const K &key() { return key_; }
     131         9995228 :         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         3054045 :         explicit Ptr(WordMapPtr p) : mapPtr(p), isInlinePtr(false) {}
     145        25297898 :         explicit Ptr(InlineElem *ie) : inlPtr(ie), isInlinePtr(true) {}
     146                 :         void operator==(const Ptr &other);
     147                 : 
     148                 :       public:
     149                 :         /* Leaves Ptr uninitialized. */
     150         1636333 :         Ptr() {
     151                 : #ifdef DEBUG
     152         1636333 :             inlPtr = (InlineElem *) 0xbad;
     153         1636333 :             isInlinePtr = true;
     154                 : #endif
     155         1636333 :         }
     156                 : 
     157                 :         /* Default copy constructor works for this structure. */
     158                 : 
     159        37949121 :         bool found() const {
     160        37949121 :             return isInlinePtr ? bool(inlPtr) : mapPtr.found();
     161                 :         }
     162                 : 
     163        29163025 :         operator ConvertibleToBool() const {
     164        29163025 :             return ConvertibleToBool(found());
     165                 :         }
     166                 : 
     167                 :         K &key() {
     168                 :             JS_ASSERT(found());
     169                 :             return isInlinePtr ? inlPtr->key : mapPtr->key;
     170                 :         }
     171                 : 
     172         8786096 :         V &value() {
     173         8786096 :             JS_ASSERT(found());
     174         8786096 :             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        32138147 :         AddPtr(InlineElem *ptr, bool found)
     189        32138147 :           : inlAddPtr(ptr), isInlinePtr(true), inlPtrFound(found)
     190        32138147 :         {}
     191                 : 
     192        10732854 :         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        99749062 :         bool found() const {
     202        99749062 :             return isInlinePtr ? inlPtrFound : mapAddPtr.found();
     203                 :         }
     204                 : 
     205        62894845 :         operator ConvertibleToBool() const {
     206        62894845 :             return found() ? ConvertibleToBool(1) : ConvertibleToBool(0);
     207                 :         }
     208                 : 
     209        22871537 :         V &value() {
     210        22871537 :             JS_ASSERT(found());
     211        22871537 :             if (isInlinePtr)
     212        18083348 :                 return inlAddPtr->value;
     213         4788189 :             return mapAddPtr->value;
     214                 :         }
     215                 :     }; /* class AddPtr */
     216                 : 
     217        31867074 :     size_t count() {
     218        31867074 :         return usingMap() ? map.count() : inlCount;
     219                 :     }
     220                 : 
     221         1630960 :     bool empty() const {
     222         1630960 :         return usingMap() ? map.empty() : !inlCount;
     223                 :     }
     224                 : 
     225         3288711 :     void clear() {
     226         3288711 :         inlNext = 0;
     227         3288711 :         inlCount = 0;
     228         3288711 :     }
     229                 : 
     230         3254171 :     bool isMap() const {
     231         3254171 :         return usingMap();
     232                 :     }
     233                 : 
     234           73192 :     const WordMap &asMap() const {
     235           73192 :         JS_ASSERT(isMap());
     236           73192 :         return map;
     237                 :     }
     238                 : 
     239         1035929 :     const InlineElem *asInline() const {
     240         1035929 :         JS_ASSERT(!isMap());
     241         1035929 :         return inl;
     242                 :     }
     243                 : 
     244         1035929 :     const InlineElem *inlineEnd() const {
     245         1035929 :         JS_ASSERT(!isMap());
     246         1035929 :         return inl + inlNext;
     247                 :     }
     248                 : 
     249                 :     JS_ALWAYS_INLINE
     250        28351943 :     Ptr lookup(const K &key) {
     251        28351943 :         if (usingMap())
     252         3054045 :             return Ptr(map.lookup(key));
     253                 : 
     254        98944072 :         for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
     255        80768936 :             if (it->key == key)
     256         7122762 :                 return Ptr(it);
     257                 :         }
     258                 : 
     259        18175136 :         return Ptr(NULL);
     260                 :     }
     261                 : 
     262                 :     JS_ALWAYS_INLINE
     263        42871001 :     AddPtr lookupForAdd(const K &key) {
     264        42871001 :         if (usingMap())
     265        10732854 :             return AddPtr(map.lookupForAdd(key));
     266                 : 
     267       167085451 :         for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
     268       153009758 :             if (it->key == key)
     269        18062454 :                 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        14075693 :         return AddPtr(inl + inlNext, false);
     278                 :     }
     279                 : 
     280                 :     JS_ALWAYS_INLINE
     281        20023844 :     bool add(AddPtr &p, const K &key, const V &value) {
     282        20023844 :         JS_ASSERT(!p);
     283                 : 
     284        20023844 :         if (p.isInlinePtr) {
     285        14075510 :             InlineElem *addPtr = p.inlAddPtr;
     286        14075510 :             JS_ASSERT(addPtr == inl + inlNext);
     287                 : 
     288                 :             /* Switching to map mode before we add this pointer. */
     289        14075510 :             if (addPtr == inl + InlineElems)
     290           92830 :                 return switchAndAdd(key, value);
     291                 : 
     292        13982680 :             JS_ASSERT(!p.found());
     293        13982680 :             JS_ASSERT(uintptr_t(inl + inlNext) == uintptr_t(p.inlAddPtr));
     294        13982680 :             p.inlAddPtr->key = key;
     295        13982680 :             p.inlAddPtr->value = value;
     296        13982680 :             ++inlCount;
     297        13982680 :             ++inlNext;
     298        13982680 :             return true;
     299                 :         }
     300                 : 
     301         5948334 :         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          811082 :     void remove(Ptr p) {
     315          811082 :         JS_ASSERT(p);
     316          811082 :         if (p.isInlinePtr) {
     317          463582 :             JS_ASSERT(inlCount > 0);
     318          463582 :             JS_ASSERT(p.inlPtr->key != NULL);
     319          463582 :             p.inlPtr->key = NULL;
     320          463582 :             --inlCount;
     321          463582 :             return;
     322                 :         }
     323          347500 :         JS_ASSERT(map.initialized() && usingMap());
     324          347500 :         map.remove(p.mapPtr);
     325                 :     }
     326                 : 
     327           46381 :     void remove(const K &key) {
     328           46381 :         if (Ptr p = lookup(key))
     329           46361 :             remove(p);
     330           46381 :     }
     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         3435933 :         Range(const InlineElem *begin, const InlineElem *end_)
     349                 :           : cur(const_cast<InlineElem *>(begin)),
     350                 :             end(const_cast<InlineElem *>(end_)),
     351         3435933 :             isInline(true) {
     352         3435933 :             advancePastNulls(cur);
     353         3435933 :             JS_ASSERT(isInlineRange());
     354         3435933 :         }
     355                 : 
     356        69265361 :         bool checkInlineRangeInvariants() const {
     357        69265361 :             JS_ASSERT(uintptr_t(cur) <= uintptr_t(end));
     358        69265361 :             JS_ASSERT_IF(cur != end, cur->key != NULL);
     359        69265361 :             return true;
     360                 :         }
     361                 : 
     362        71061010 :         bool isInlineRange() const {
     363        71061010 :             JS_ASSERT_IF(isInline, checkInlineRangeInvariants());
     364        71061010 :             return isInline;
     365                 :         }
     366                 : 
     367        13026915 :         void advancePastNulls(InlineElem *begin) {
     368        13026915 :             InlineElem *newCur = begin;
     369        26266567 :             while (newCur < end && NULL == newCur->key)
     370          212737 :                 ++newCur;
     371        13026915 :             JS_ASSERT(uintptr_t(newCur) <= uintptr_t(end));
     372        13026915 :             cur = newCur;
     373        13026915 :         }
     374                 : 
     375         9590982 :         void bumpCurPtr() {
     376         9590982 :             JS_ASSERT(isInlineRange());
     377         9590982 :             advancePastNulls(cur + 1);
     378         9590982 :         }
     379                 : 
     380                 :         void operator==(const Range &other);
     381                 : 
     382                 :       public:
     383        35688166 :         bool empty() const {
     384        35688166 :             return isInlineRange() ? cur == end : mapRange.empty();
     385                 :         }
     386                 : 
     387        12420190 :         Entry front() {
     388        12420190 :             JS_ASSERT(!empty());
     389        12420190 :             if (isInlineRange())
     390        12018124 :                 return Entry(cur->key, cur->value);
     391          402066 :             return Entry(mapRange.front().key, mapRange.front().value);
     392                 :         }
     393                 : 
     394         9912985 :         void popFront() {
     395         9912985 :             JS_ASSERT(!empty());
     396         9912985 :             if (isInlineRange())
     397         9590982 :                 bumpCurPtr();
     398                 :             else
     399          322003 :                 mapRange.popFront();
     400         9912985 :         }
     401                 :     }; /* class Range */
     402                 : 
     403         3448687 :     Range all() const {
     404         3448687 :         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