/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
- /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=4 sw=4 et tw=99 ft=cpp:
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is SpiderMonkey JavaScript engine.
- *
- * The Initial Developer of the Original Code is
- * Mozilla Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Chris Leary <cdleary@mozilla.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
- #ifndef InlineMap_h__
- #define InlineMap_h__
- #include "js/HashTable.h"
- namespace js {
- /*
- * A type can only be used as an InlineMap key if zero is an invalid key value
- * (and thus may be used as a tombstone value by InlineMap).
- */
- template <typename T> struct ZeroIsReserved { static const bool result = false; };
- template <typename T> struct ZeroIsReserved<T *> { static const bool result = true; };
- template <typename K, typename V, size_t InlineElems>
- class InlineMap
- {
- public:
- typedef HashMap<K, V, DefaultHasher<K>, TempAllocPolicy> WordMap;
- struct InlineElem
- {
- K key;
- V value;
- };
- private:
- typedef typename WordMap::Ptr WordMapPtr;
- typedef typename WordMap::AddPtr WordMapAddPtr;
- typedef typename WordMap::Range WordMapRange;
- size_t inlNext;
- size_t inlCount;
- InlineElem inl[InlineElems];
- WordMap map;
- void checkStaticInvariants() {
- JS_STATIC_ASSERT(ZeroIsReserved<K>::result);
- }
- bool usingMap() const {
- return inlNext > InlineElems;
- }
- bool switchToMap() {
- JS_ASSERT(inlNext == InlineElems);
- if (map.initialized()) {
- map.clear();
- } else {
- if (!map.init(count()))
- return false;
- JS_ASSERT(map.initialized());
- }
- for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
- if (it->key && !map.putNew(it->key, it->value))
- return false;
- }
- inlNext = InlineElems + 1;
- JS_ASSERT(map.count() == inlCount);
- JS_ASSERT(usingMap());
- return true;
- }
- JS_NEVER_INLINE
- bool switchAndAdd(const K &key, const V &value) {
- if (!switchToMap())
- return false;
- return map.putNew(key, value);
- }
- public:
- explicit InlineMap(JSContext *cx)
- : inlNext(0), inlCount(0), map(cx) {
- checkStaticInvariants(); /* Force the template to instantiate the static invariants. */
- }
- class Entry
- {
- friend class InlineMap;
- const K &key_;
- const V &value_;
- Entry(const K &key, const V &value) : key_(key), value_(value) {}
- public:
- const K &key() { return key_; }
- const V &value() { return value_; }
- }; /* class Entry */
- class Ptr
- {
- friend class InlineMap;
- WordMapPtr mapPtr;
- InlineElem *inlPtr;
- bool isInlinePtr;
- typedef Ptr ******* ConvertibleToBool;
- explicit Ptr(WordMapPtr p) : mapPtr(p), isInlinePtr(false) {}
- explicit Ptr(InlineElem *ie) : inlPtr(ie), isInlinePtr(true) {}
- void operator==(const Ptr &other);
- public:
- /* Leaves Ptr uninitialized. */
- Ptr() {
- #ifdef DEBUG
- inlPtr = (InlineElem *) 0xbad;
- isInlinePtr = true;
- #endif
- }
- /* Default copy constructor works for this structure. */
- bool found() const {
- return isInlinePtr ? bool(inlPtr) : mapPtr.found();
- }
- operator ConvertibleToBool() const {
- return ConvertibleToBool(found());
- }
- K &key() {
- JS_ASSERT(found());
- return isInlinePtr ? inlPtr->key : mapPtr->key;
- }
- V &value() {
- JS_ASSERT(found());
- return isInlinePtr ? inlPtr->value : mapPtr->value;
- }
- }; /* class Ptr */
- class AddPtr
- {
- friend class InlineMap;
- WordMapAddPtr mapAddPtr;
- InlineElem *inlAddPtr;
- bool isInlinePtr;
- /* Indicates whether inlAddPtr is a found result or an add pointer. */
- bool inlPtrFound;
- AddPtr(InlineElem *ptr, bool found)
- : inlAddPtr(ptr), isInlinePtr(true), inlPtrFound(found)
- {}
- AddPtr(const WordMapAddPtr &p) : mapAddPtr(p), isInlinePtr(false) {}
- void operator==(const AddPtr &other);
- typedef AddPtr ******* ConvertibleToBool;
- public:
- AddPtr() {}
- bool found() const {
- return isInlinePtr ? inlPtrFound : mapAddPtr.found();
- }
- operator ConvertibleToBool() const {
- return found() ? ConvertibleToBool(1) : ConvertibleToBool(0);
- }
- V &value() {
- JS_ASSERT(found());
- if (isInlinePtr)
- return inlAddPtr->value;
- return mapAddPtr->value;
- }
- }; /* class AddPtr */
- size_t count() {
- return usingMap() ? map.count() : inlCount;
- }
- bool empty() const {
- return usingMap() ? map.empty() : !inlCount;
- }
- void clear() {
- inlNext = 0;
- inlCount = 0;
- }
- bool isMap() const {
- return usingMap();
- }
- const WordMap &asMap() const {
- JS_ASSERT(isMap());
- return map;
- }
- const InlineElem *asInline() const {
- JS_ASSERT(!isMap());
- return inl;
- }
- const InlineElem *inlineEnd() const {
- JS_ASSERT(!isMap());
- return inl + inlNext;
- }
- JS_ALWAYS_INLINE
- Ptr lookup(const K &key) {
- if (usingMap())
- return Ptr(map.lookup(key));
- for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
- if (it->key == key)
- return Ptr(it);
- }
- return Ptr(NULL);
- }
- JS_ALWAYS_INLINE
- AddPtr lookupForAdd(const K &key) {
- if (usingMap())
- return AddPtr(map.lookupForAdd(key));
- for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
- if (it->key == key)
- return AddPtr(it, true);
- }
- /*
- * The add pointer that's returned here may indicate the limit entry of
- * the linear space, in which case the |add| operation will initialize
- * the map if necessary and add the entry there.
- */
- return AddPtr(inl + inlNext, false);
- }
- JS_ALWAYS_INLINE
- bool add(AddPtr &p, const K &key, const V &value) {
- JS_ASSERT(!p);
- if (p.isInlinePtr) {
- InlineElem *addPtr = p.inlAddPtr;
- JS_ASSERT(addPtr == inl + inlNext);
- /* Switching to map mode before we add this pointer. */
- if (addPtr == inl + InlineElems)
- return switchAndAdd(key, value);
- JS_ASSERT(!p.found());
- JS_ASSERT(uintptr_t(inl + inlNext) == uintptr_t(p.inlAddPtr));
- p.inlAddPtr->key = key;
- p.inlAddPtr->value = value;
- ++inlCount;
- ++inlNext;
- return true;
- }
- return map.add(p.mapAddPtr, key, value);
- }
- JS_ALWAYS_INLINE
- bool put(const K &key, const V &value) {
- AddPtr p = lookupForAdd(key);
- if (p) {
- p.value() = value;
- return true;
- }
- return add(p, key, value);
- }
- void remove(Ptr p) {
- JS_ASSERT(p);
- if (p.isInlinePtr) {
- JS_ASSERT(inlCount > 0);
- JS_ASSERT(p.inlPtr->key != NULL);
- p.inlPtr->key = NULL;
- --inlCount;
- return;
- }
- JS_ASSERT(map.initialized() && usingMap());
- map.remove(p.mapPtr);
- }
- void remove(const K &key) {
- if (Ptr p = lookup(key))
- remove(p);
- }
- class Range
- {
- friend class InlineMap;
- WordMapRange mapRange;
- InlineElem *cur;
- InlineElem *end;
- bool isInline;
- explicit Range(WordMapRange r)
- : cur(NULL), end(NULL), /* Avoid GCC 4.3.3 over-warning. */
- isInline(false) {
- mapRange = r;
- JS_ASSERT(!isInlineRange());
- }
- Range(const InlineElem *begin, const InlineElem *end_)
- : cur(const_cast<InlineElem *>(begin)),
- end(const_cast<InlineElem *>(end_)),
- isInline(true) {
- advancePastNulls(cur);
- JS_ASSERT(isInlineRange());
- }
- bool checkInlineRangeInvariants() const {
- JS_ASSERT(uintptr_t(cur) <= uintptr_t(end));
- JS_ASSERT_IF(cur != end, cur->key != NULL);
- return true;
- }
- bool isInlineRange() const {
- JS_ASSERT_IF(isInline, checkInlineRangeInvariants());
- return isInline;
- }
- void advancePastNulls(InlineElem *begin) {
- InlineElem *newCur = begin;
- while (newCur < end && NULL == newCur->key)
- ++newCur;
- JS_ASSERT(uintptr_t(newCur) <= uintptr_t(end));
- cur = newCur;
- }
- void bumpCurPtr() {
- JS_ASSERT(isInlineRange());
- advancePastNulls(cur + 1);
- }
- void operator==(const Range &other);
- public:
- bool empty() const {
- return isInlineRange() ? cur == end : mapRange.empty();
- }
- Entry front() {
- JS_ASSERT(!empty());
- if (isInlineRange())
- return Entry(cur->key, cur->value);
- return Entry(mapRange.front().key, mapRange.front().value);
- }
- void popFront() {
- JS_ASSERT(!empty());
- if (isInlineRange())
- bumpCurPtr();
- else
- mapRange.popFront();
- }
- }; /* class Range */
- Range all() const {
- return usingMap() ? Range(map.all()) : Range(inl, inl + inlNext);
- }
- }; /* class InlineMap */
- } /* namespace js */
- #endif