Wed, 31 Dec 2014 06:09:35 +0100
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 */ |