js/src/ds/InlineMap.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef ds_InlineMap_h
michael@0 8 #define ds_InlineMap_h
michael@0 9
michael@0 10 #include "jsalloc.h"
michael@0 11
michael@0 12 #include "js/HashTable.h"
michael@0 13
michael@0 14 namespace js {
michael@0 15
michael@0 16 /*
michael@0 17 * A type can only be used as an InlineMap key if zero is an invalid key value
michael@0 18 * (and thus may be used as a tombstone value by InlineMap).
michael@0 19 */
michael@0 20 template <typename T> struct ZeroIsReserved { static const bool result = false; };
michael@0 21 template <typename T> struct ZeroIsReserved<T *> { static const bool result = true; };
michael@0 22
michael@0 23 template <typename K, typename V, size_t InlineElems>
michael@0 24 class InlineMap
michael@0 25 {
michael@0 26 public:
michael@0 27 typedef HashMap<K, V, DefaultHasher<K>, SystemAllocPolicy> WordMap;
michael@0 28
michael@0 29 struct InlineElem
michael@0 30 {
michael@0 31 K key;
michael@0 32 V value;
michael@0 33 };
michael@0 34
michael@0 35 private:
michael@0 36 typedef typename WordMap::Ptr WordMapPtr;
michael@0 37 typedef typename WordMap::AddPtr WordMapAddPtr;
michael@0 38 typedef typename WordMap::Range WordMapRange;
michael@0 39
michael@0 40 size_t inlNext;
michael@0 41 size_t inlCount;
michael@0 42 InlineElem inl[InlineElems];
michael@0 43 WordMap map;
michael@0 44
michael@0 45 void checkStaticInvariants() {
michael@0 46 JS_STATIC_ASSERT(ZeroIsReserved<K>::result);
michael@0 47 }
michael@0 48
michael@0 49 bool usingMap() const {
michael@0 50 return inlNext > InlineElems;
michael@0 51 }
michael@0 52
michael@0 53 bool switchToMap() {
michael@0 54 JS_ASSERT(inlNext == InlineElems);
michael@0 55
michael@0 56 if (map.initialized()) {
michael@0 57 map.clear();
michael@0 58 } else {
michael@0 59 if (!map.init(count()))
michael@0 60 return false;
michael@0 61 JS_ASSERT(map.initialized());
michael@0 62 }
michael@0 63
michael@0 64 for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
michael@0 65 if (it->key && !map.putNew(it->key, it->value))
michael@0 66 return false;
michael@0 67 }
michael@0 68
michael@0 69 inlNext = InlineElems + 1;
michael@0 70 JS_ASSERT(map.count() == inlCount);
michael@0 71 JS_ASSERT(usingMap());
michael@0 72 return true;
michael@0 73 }
michael@0 74
michael@0 75 MOZ_NEVER_INLINE
michael@0 76 bool switchAndAdd(const K &key, const V &value) {
michael@0 77 if (!switchToMap())
michael@0 78 return false;
michael@0 79
michael@0 80 return map.putNew(key, value);
michael@0 81 }
michael@0 82
michael@0 83 public:
michael@0 84 explicit InlineMap()
michael@0 85 : inlNext(0), inlCount(0) {
michael@0 86 checkStaticInvariants(); /* Force the template to instantiate the static invariants. */
michael@0 87 }
michael@0 88
michael@0 89 class Entry
michael@0 90 {
michael@0 91 friend class InlineMap;
michael@0 92 const K &key_;
michael@0 93 V &value_;
michael@0 94
michael@0 95 Entry(const K &key, V &value) : key_(key), value_(value) {}
michael@0 96
michael@0 97 public:
michael@0 98 const K &key() { return key_; }
michael@0 99 V &value() { return value_; }
michael@0 100 }; /* class Entry */
michael@0 101
michael@0 102 class Ptr
michael@0 103 {
michael@0 104 friend class InlineMap;
michael@0 105
michael@0 106 WordMapPtr mapPtr;
michael@0 107 InlineElem *inlPtr;
michael@0 108 bool isInlinePtr;
michael@0 109
michael@0 110 typedef Ptr ******* ConvertibleToBool;
michael@0 111
michael@0 112 explicit Ptr(WordMapPtr p) : mapPtr(p), isInlinePtr(false) {}
michael@0 113 explicit Ptr(InlineElem *ie) : inlPtr(ie), isInlinePtr(true) {}
michael@0 114 void operator==(const Ptr &other);
michael@0 115
michael@0 116 public:
michael@0 117 /* Leaves Ptr uninitialized. */
michael@0 118 Ptr() {
michael@0 119 #ifdef DEBUG
michael@0 120 inlPtr = (InlineElem *) 0xbad;
michael@0 121 isInlinePtr = true;
michael@0 122 #endif
michael@0 123 }
michael@0 124
michael@0 125 /* Default copy constructor works for this structure. */
michael@0 126
michael@0 127 bool found() const {
michael@0 128 return isInlinePtr ? bool(inlPtr) : mapPtr.found();
michael@0 129 }
michael@0 130
michael@0 131 operator ConvertibleToBool() const {
michael@0 132 return ConvertibleToBool(found());
michael@0 133 }
michael@0 134
michael@0 135 K &key() {
michael@0 136 JS_ASSERT(found());
michael@0 137 return isInlinePtr ? inlPtr->key : mapPtr->key();
michael@0 138 }
michael@0 139
michael@0 140 V &value() {
michael@0 141 JS_ASSERT(found());
michael@0 142 return isInlinePtr ? inlPtr->value : mapPtr->value();
michael@0 143 }
michael@0 144 }; /* class Ptr */
michael@0 145
michael@0 146 class AddPtr
michael@0 147 {
michael@0 148 friend class InlineMap;
michael@0 149
michael@0 150 WordMapAddPtr mapAddPtr;
michael@0 151 InlineElem *inlAddPtr;
michael@0 152 bool isInlinePtr;
michael@0 153 /* Indicates whether inlAddPtr is a found result or an add pointer. */
michael@0 154 bool inlPtrFound;
michael@0 155
michael@0 156 AddPtr(InlineElem *ptr, bool found)
michael@0 157 : inlAddPtr(ptr), isInlinePtr(true), inlPtrFound(found)
michael@0 158 {}
michael@0 159
michael@0 160 AddPtr(const WordMapAddPtr &p) : mapAddPtr(p), isInlinePtr(false) {}
michael@0 161
michael@0 162 void operator==(const AddPtr &other);
michael@0 163
michael@0 164 typedef AddPtr ******* ConvertibleToBool;
michael@0 165
michael@0 166 public:
michael@0 167 AddPtr() {}
michael@0 168
michael@0 169 bool found() const {
michael@0 170 return isInlinePtr ? inlPtrFound : mapAddPtr.found();
michael@0 171 }
michael@0 172
michael@0 173 operator ConvertibleToBool() const {
michael@0 174 return found() ? ConvertibleToBool(1) : ConvertibleToBool(0);
michael@0 175 }
michael@0 176
michael@0 177 V &value() {
michael@0 178 JS_ASSERT(found());
michael@0 179 if (isInlinePtr)
michael@0 180 return inlAddPtr->value;
michael@0 181 return mapAddPtr->value();
michael@0 182 }
michael@0 183 }; /* class AddPtr */
michael@0 184
michael@0 185 size_t count() {
michael@0 186 return usingMap() ? map.count() : inlCount;
michael@0 187 }
michael@0 188
michael@0 189 bool empty() const {
michael@0 190 return usingMap() ? map.empty() : !inlCount;
michael@0 191 }
michael@0 192
michael@0 193 void clear() {
michael@0 194 inlNext = 0;
michael@0 195 inlCount = 0;
michael@0 196 }
michael@0 197
michael@0 198 bool isMap() const {
michael@0 199 return usingMap();
michael@0 200 }
michael@0 201
michael@0 202 const WordMap &asMap() const {
michael@0 203 JS_ASSERT(isMap());
michael@0 204 return map;
michael@0 205 }
michael@0 206
michael@0 207 const InlineElem *asInline() const {
michael@0 208 JS_ASSERT(!isMap());
michael@0 209 return inl;
michael@0 210 }
michael@0 211
michael@0 212 const InlineElem *inlineEnd() const {
michael@0 213 JS_ASSERT(!isMap());
michael@0 214 return inl + inlNext;
michael@0 215 }
michael@0 216
michael@0 217 MOZ_ALWAYS_INLINE
michael@0 218 Ptr lookup(const K &key) {
michael@0 219 if (usingMap())
michael@0 220 return Ptr(map.lookup(key));
michael@0 221
michael@0 222 for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
michael@0 223 if (it->key == key)
michael@0 224 return Ptr(it);
michael@0 225 }
michael@0 226
michael@0 227 return Ptr(nullptr);
michael@0 228 }
michael@0 229
michael@0 230 MOZ_ALWAYS_INLINE
michael@0 231 AddPtr lookupForAdd(const K &key) {
michael@0 232 if (usingMap())
michael@0 233 return AddPtr(map.lookupForAdd(key));
michael@0 234
michael@0 235 for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
michael@0 236 if (it->key == key)
michael@0 237 return AddPtr(it, true);
michael@0 238 }
michael@0 239
michael@0 240 /*
michael@0 241 * The add pointer that's returned here may indicate the limit entry of
michael@0 242 * the linear space, in which case the |add| operation will initialize
michael@0 243 * the map if necessary and add the entry there.
michael@0 244 */
michael@0 245 return AddPtr(inl + inlNext, false);
michael@0 246 }
michael@0 247
michael@0 248 MOZ_ALWAYS_INLINE
michael@0 249 bool add(AddPtr &p, const K &key, const V &value) {
michael@0 250 JS_ASSERT(!p);
michael@0 251
michael@0 252 if (p.isInlinePtr) {
michael@0 253 InlineElem *addPtr = p.inlAddPtr;
michael@0 254 JS_ASSERT(addPtr == inl + inlNext);
michael@0 255
michael@0 256 /* Switching to map mode before we add this pointer. */
michael@0 257 if (addPtr == inl + InlineElems)
michael@0 258 return switchAndAdd(key, value);
michael@0 259
michael@0 260 JS_ASSERT(!p.found());
michael@0 261 JS_ASSERT(uintptr_t(inl + inlNext) == uintptr_t(p.inlAddPtr));
michael@0 262 p.inlAddPtr->key = key;
michael@0 263 p.inlAddPtr->value = value;
michael@0 264 ++inlCount;
michael@0 265 ++inlNext;
michael@0 266 return true;
michael@0 267 }
michael@0 268
michael@0 269 return map.add(p.mapAddPtr, key, value);
michael@0 270 }
michael@0 271
michael@0 272 MOZ_ALWAYS_INLINE
michael@0 273 bool put(const K &key, const V &value) {
michael@0 274 AddPtr p = lookupForAdd(key);
michael@0 275 if (p) {
michael@0 276 p.value() = value;
michael@0 277 return true;
michael@0 278 }
michael@0 279 return add(p, key, value);
michael@0 280 }
michael@0 281
michael@0 282 void remove(Ptr p) {
michael@0 283 JS_ASSERT(p);
michael@0 284 if (p.isInlinePtr) {
michael@0 285 JS_ASSERT(inlCount > 0);
michael@0 286 JS_ASSERT(p.inlPtr->key != nullptr);
michael@0 287 p.inlPtr->key = nullptr;
michael@0 288 --inlCount;
michael@0 289 return;
michael@0 290 }
michael@0 291 JS_ASSERT(map.initialized() && usingMap());
michael@0 292 map.remove(p.mapPtr);
michael@0 293 }
michael@0 294
michael@0 295 void remove(const K &key) {
michael@0 296 if (Ptr p = lookup(key))
michael@0 297 remove(p);
michael@0 298 }
michael@0 299
michael@0 300 class Range
michael@0 301 {
michael@0 302 friend class InlineMap;
michael@0 303
michael@0 304 WordMapRange mapRange;
michael@0 305 InlineElem *cur;
michael@0 306 InlineElem *end;
michael@0 307 bool isInline;
michael@0 308
michael@0 309 explicit Range(WordMapRange r)
michael@0 310 : cur(nullptr), end(nullptr), /* Avoid GCC 4.3.3 over-warning. */
michael@0 311 isInline(false) {
michael@0 312 mapRange = r;
michael@0 313 JS_ASSERT(!isInlineRange());
michael@0 314 }
michael@0 315
michael@0 316 Range(const InlineElem *begin, const InlineElem *end_)
michael@0 317 : cur(const_cast<InlineElem *>(begin)),
michael@0 318 end(const_cast<InlineElem *>(end_)),
michael@0 319 isInline(true) {
michael@0 320 advancePastNulls(cur);
michael@0 321 JS_ASSERT(isInlineRange());
michael@0 322 }
michael@0 323
michael@0 324 bool checkInlineRangeInvariants() const {
michael@0 325 JS_ASSERT(uintptr_t(cur) <= uintptr_t(end));
michael@0 326 JS_ASSERT_IF(cur != end, cur->key != nullptr);
michael@0 327 return true;
michael@0 328 }
michael@0 329
michael@0 330 bool isInlineRange() const {
michael@0 331 JS_ASSERT_IF(isInline, checkInlineRangeInvariants());
michael@0 332 return isInline;
michael@0 333 }
michael@0 334
michael@0 335 void advancePastNulls(InlineElem *begin) {
michael@0 336 InlineElem *newCur = begin;
michael@0 337 while (newCur < end && nullptr == newCur->key)
michael@0 338 ++newCur;
michael@0 339 JS_ASSERT(uintptr_t(newCur) <= uintptr_t(end));
michael@0 340 cur = newCur;
michael@0 341 }
michael@0 342
michael@0 343 void bumpCurPtr() {
michael@0 344 JS_ASSERT(isInlineRange());
michael@0 345 advancePastNulls(cur + 1);
michael@0 346 }
michael@0 347
michael@0 348 void operator==(const Range &other);
michael@0 349
michael@0 350 public:
michael@0 351 bool empty() const {
michael@0 352 return isInlineRange() ? cur == end : mapRange.empty();
michael@0 353 }
michael@0 354
michael@0 355 Entry front() {
michael@0 356 JS_ASSERT(!empty());
michael@0 357 if (isInlineRange())
michael@0 358 return Entry(cur->key, cur->value);
michael@0 359 return Entry(mapRange.front().key(), mapRange.front().value());
michael@0 360 }
michael@0 361
michael@0 362 void popFront() {
michael@0 363 JS_ASSERT(!empty());
michael@0 364 if (isInlineRange())
michael@0 365 bumpCurPtr();
michael@0 366 else
michael@0 367 mapRange.popFront();
michael@0 368 }
michael@0 369 }; /* class Range */
michael@0 370
michael@0 371 Range all() const {
michael@0 372 return usingMap() ? Range(map.all()) : Range(inl, inl + inlNext);
michael@0 373 }
michael@0 374 }; /* class InlineMap */
michael@0 375
michael@0 376 } /* namespace js */
michael@0 377
michael@0 378 #endif /* ds_InlineMap_h */

mercurial