PageRenderTime 38ms CodeModel.GetById 16ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 0ms

/js/src/ds/InlineMap.h

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