1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 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 Mozilla SpiderMonkey JavaScript 1.9 code, released
18 : * November 13, 2009.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * the Mozilla Corporation.
22 : *
23 : * Contributor(s):
24 : * Brendan Eich <brendan@mozilla.org> (Original Author)
25 : * Chris Waterson <waterson@netscape.com>
26 : * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
27 : * Luke Wagner <lw@mozilla.com>
28 : *
29 : * Alternatively, the contents of this file may be used under the terms of
30 : * either of the GNU General Public License Version 2 or later (the "GPL"),
31 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 : * in which case the provisions of the GPL or the LGPL are applicable instead
33 : * of those above. If you wish to allow use of your version of this file only
34 : * under the terms of either the GPL or the LGPL, and not to allow others to
35 : * use your version of this file under the terms of the MPL, indicate your
36 : * decision by deleting the provisions above and replace them with the notice
37 : * and other provisions required by the GPL or the LGPL. If you do not delete
38 : * the provisions above, a recipient may use your version of this file under
39 : * the terms of any one of the MPL, the GPL or the LGPL.
40 : *
41 : * ***** END LICENSE BLOCK ***** */
42 :
43 : #ifndef jshashtable_h_
44 : #define jshashtable_h_
45 :
46 : #include "TemplateLib.h"
47 : #include "Utility.h"
48 :
49 : namespace js {
50 :
51 : class TempAllocPolicy;
52 :
53 : /* Integral types for all hash functions. */
54 : typedef uint32_t HashNumber;
55 :
56 : /*****************************************************************************/
57 :
58 : namespace detail {
59 :
60 : template <class T, class HashPolicy, class AllocPolicy>
61 : class HashTable;
62 :
63 : template <class T>
64 657687540 : class HashTableEntry {
65 : HashNumber keyHash;
66 :
67 : typedef typename tl::StripConst<T>::result NonConstT;
68 :
69 : static const HashNumber sFreeKey = 0;
70 : static const HashNumber sRemovedKey = 1;
71 : static const HashNumber sCollisionBit = 1;
72 :
73 : template <class, class, class> friend class HashTable;
74 :
75 -696343730 : static bool isLiveHash(HashNumber hash)
76 : {
77 -696343730 : return hash > sRemovedKey;
78 : }
79 :
80 : public:
81 662645548 : HashTableEntry() : keyHash(0), t() {}
82 : HashTableEntry(MoveRef<HashTableEntry> rhs) : keyHash(rhs->keyHash), t(Move(rhs->t)) { }
83 : void operator=(const HashTableEntry &rhs) { keyHash = rhs.keyHash; t = rhs.t; }
84 148567417 : void operator=(MoveRef<HashTableEntry> rhs) { keyHash = rhs->keyHash; t = Move(rhs->t); }
85 :
86 : NonConstT t;
87 :
88 1044717016 : bool isFree() const { return keyHash == sFreeKey; }
89 21094972 : void setFree() { keyHash = sFreeKey; t = T(); }
90 482904968 : bool isRemoved() const { return keyHash == sRemovedKey; }
91 8041974 : void setRemoved() { keyHash = sRemovedKey; t = T(); }
92 -1886809747 : bool isLive() const { return isLiveHash(keyHash); }
93 128758205 : void setLive(HashNumber hn) { JS_ASSERT(isLiveHash(hn)); keyHash = hn; }
94 :
95 30060835 : void setCollision() { JS_ASSERT(isLive()); keyHash |= sCollisionBit; }
96 321950952 : void setCollision(HashNumber collisionBit) {
97 321950952 : JS_ASSERT(isLive()); keyHash |= collisionBit;
98 321950952 : }
99 143607921 : void unsetCollision() { JS_ASSERT(isLive()); keyHash &= ~sCollisionBit; }
100 172744867 : bool hasCollision() const { JS_ASSERT(isLive()); return keyHash & sCollisionBit; }
101 490491896 : bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; }
102 143607921 : HashNumber getKeyHash() const { JS_ASSERT(!hasCollision()); return keyHash; }
103 : };
104 :
105 : /*
106 : * js::detail::HashTable is an implementation detail of the js::HashMap and
107 : * js::HashSet templates. For js::Hash{Map,Set} API documentation and examples,
108 : * skip to the end of the detail namespace.
109 : */
110 :
111 : /* Reusable implementation of HashMap and HashSet. */
112 : template <class T, class HashPolicy, class AllocPolicy>
113 : class HashTable : private AllocPolicy
114 : {
115 : typedef typename tl::StripConst<T>::result NonConstT;
116 : typedef typename HashPolicy::KeyType Key;
117 : typedef typename HashPolicy::Lookup Lookup;
118 :
119 : public:
120 : typedef HashTableEntry<T> Entry;
121 :
122 : /*
123 : * A nullable pointer to a hash table element. A Ptr |p| can be tested
124 : * either explicitly |if (p.found()) p->...| or using boolean conversion
125 : * |if (p) p->...|. Ptr objects must not be used after any mutating hash
126 : * table operations unless |generation()| is tested.
127 : */
128 : class Ptr
129 : {
130 : friend class HashTable;
131 : typedef void (Ptr::* ConvertibleToBool)();
132 0 : void nonNull() {}
133 :
134 : Entry *entry;
135 :
136 : protected:
137 515552538 : Ptr(Entry &entry) : entry(&entry) {}
138 :
139 : public:
140 : /* Leaves Ptr uninitialized. */
141 59315819 : Ptr() {
142 : #ifdef DEBUG
143 59315819 : entry = (Entry *)0xbad;
144 : #endif
145 59315819 : }
146 :
147 681651875 : bool found() const { return entry->isLive(); }
148 493578156 : operator ConvertibleToBool() const { return found() ? &Ptr::nonNull : 0; }
149 : bool operator==(const Ptr &rhs) const { JS_ASSERT(found() && rhs.found()); return entry == rhs.entry; }
150 : bool operator!=(const Ptr &rhs) const { return !(*this == rhs); }
151 :
152 61197240 : T &operator*() const { return entry->t; }
153 138754087 : T *operator->() const { return &entry->t; }
154 : };
155 :
156 : /* A Ptr that can be used to add a key after a failed lookup. */
157 : class AddPtr : public Ptr
158 : {
159 : friend class HashTable;
160 : HashNumber keyHash;
161 : #ifdef DEBUG
162 : uint64_t mutationCount;
163 :
164 261548343 : AddPtr(Entry &entry, HashNumber hn, uint64_t mutationCount)
165 261548343 : : Ptr(entry), keyHash(hn), mutationCount(mutationCount) {}
166 : #else
167 : AddPtr(Entry &entry, HashNumber hn) : Ptr(entry), keyHash(hn) {}
168 : #endif
169 : public:
170 : /* Leaves AddPtr uninitialized. */
171 32318814 : AddPtr() {}
172 : };
173 :
174 : /*
175 : * A collection of hash table entries. The collection is enumerated by
176 : * calling |front()| followed by |popFront()| as long as |!empty()|. As
177 : * with Ptr/AddPtr, Range objects must not be used after any mutating hash
178 : * table operation unless the |generation()| is tested.
179 : */
180 : class Range
181 : {
182 : protected:
183 : friend class HashTable;
184 :
185 1621016 : Range(Entry *c, Entry *e) : cur(c), end(e) {
186 68731482 : while (cur < end && !cur->isLive())
187 65489450 : ++cur;
188 1621016 : }
189 :
190 : Entry *cur, *end;
191 :
192 : public:
193 3448687 : Range() : cur(NULL), end(NULL) {}
194 :
195 749481267 : bool empty() const {
196 749481267 : return cur == end;
197 : }
198 :
199 257743706 : T &front() const {
200 257743706 : JS_ASSERT(!empty());
201 257743706 : return cur->t;
202 : }
203 :
204 244775368 : void popFront() {
205 244775368 : JS_ASSERT(!empty());
206 947376398 : while (++cur < end && !cur->isLive())
207 457825662 : continue;
208 244775368 : }
209 : };
210 :
211 : /*
212 : * A Range whose lifetime delimits a mutating enumeration of a hash table.
213 : * Since rehashing when elements were removed during enumeration would be
214 : * bad, it is postponed until |endEnumeration()| is called. If
215 : * |endEnumeration()| is not called before an Enum's constructor, it will
216 : * be called automatically. Since |endEnumeration()| touches the hash
217 : * table, the user must ensure that the hash table is still alive when this
218 : * happens.
219 : */
220 : class Enum : public Range
221 : {
222 : friend class HashTable;
223 :
224 : HashTable &table;
225 : bool removed;
226 :
227 : /* Not copyable. */
228 : Enum(const Enum &);
229 : void operator=(const Enum &);
230 :
231 : public:
232 : template<class Map> explicit
233 805797 : Enum(Map &map) : Range(map.all()), table(map.impl), removed(false) {}
234 :
235 : /*
236 : * Removes the |front()| element from the table, leaving |front()|
237 : * invalid until the next call to |popFront()|. For example:
238 : *
239 : * HashSet<int> s;
240 : * for (HashSet<int>::Enum e(s); !e.empty(); e.popFront())
241 : * if (e.front() == 42)
242 : * e.removeFront();
243 : */
244 27078302 : void removeFront() {
245 27078302 : table.remove(*this->cur);
246 27078302 : removed = true;
247 27078302 : }
248 :
249 : /* Potentially rehashes the table. */
250 805797 : ~Enum() {
251 805797 : if (removed)
252 216473 : table.checkUnderloaded();
253 805797 : }
254 :
255 : /* Can be used to end the enumeration before the destructor. */
256 : void endEnumeration() {
257 : if (removed) {
258 : table.checkUnderloaded();
259 : removed = false;
260 : }
261 : }
262 : };
263 :
264 : private:
265 : uint32_t hashShift; /* multiplicative hash shift */
266 : uint32_t entryCount; /* number of entries in table */
267 : uint32_t gen; /* entry storage generation number */
268 : uint32_t removedCount; /* removed entry sentinels in table */
269 : Entry *table; /* entry storage */
270 :
271 6054815 : void setTableSizeLog2(unsigned sizeLog2) {
272 6054815 : hashShift = sHashBits - sizeLog2;
273 6054815 : }
274 :
275 : #ifdef DEBUG
276 : mutable struct Stats {
277 : uint32_t searches; /* total number of table searches */
278 : uint32_t steps; /* hash chain links traversed */
279 : uint32_t hits; /* searches that found key */
280 : uint32_t misses; /* searches that didn't find key */
281 : uint32_t addOverRemoved; /* adds that recycled a removed entry */
282 : uint32_t removes; /* calls to remove */
283 : uint32_t removeFrees; /* calls to remove that freed the entry */
284 : uint32_t grows; /* table expansions */
285 : uint32_t shrinks; /* table contractions */
286 : uint32_t compresses; /* table compressions */
287 : } stats;
288 : # define METER(x) x
289 : #else
290 : # define METER(x)
291 : #endif
292 :
293 : #ifdef DEBUG
294 : friend class js::ReentrancyGuard;
295 : mutable bool entered;
296 : uint64_t mutationCount;
297 : #endif
298 :
299 : /* The default initial capacity is 16, but you can ask for as small as 4. */
300 : static const unsigned sMinSizeLog2 = 2;
301 : static const unsigned sMinSize = 1 << sMinSizeLog2;
302 : static const unsigned sDefaultInitSizeLog2 = 4;
303 : public:
304 : static const unsigned sDefaultInitSize = 1 << sDefaultInitSizeLog2;
305 : private:
306 : static const unsigned sMaxInit = JS_BIT(23);
307 : static const unsigned sMaxCapacity = JS_BIT(24);
308 : static const unsigned sHashBits = tl::BitSize<HashNumber>::result;
309 : static const uint8_t sMinAlphaFrac = 64; /* (0x100 * .25) taken from jsdhash.h */
310 : static const uint8_t sMaxAlphaFrac = 192; /* (0x100 * .75) taken from jsdhash.h */
311 : static const uint8_t sInvMaxAlpha = 171; /* (ceil(0x100 / .75) >> 1) */
312 : static const HashNumber sGoldenRatio = 0x9E3779B9U; /* taken from jsdhash.h */
313 : static const HashNumber sFreeKey = Entry::sFreeKey;
314 : static const HashNumber sRemovedKey = Entry::sRemovedKey;
315 : static const HashNumber sCollisionBit = Entry::sCollisionBit;
316 :
317 : static void staticAsserts()
318 : {
319 : /* Rely on compiler "constant overflow warnings". */
320 : JS_STATIC_ASSERT(((sMaxInit * sInvMaxAlpha) >> 7) < sMaxCapacity);
321 : JS_STATIC_ASSERT((sMaxCapacity * sInvMaxAlpha) <= UINT32_MAX);
322 : JS_STATIC_ASSERT((sMaxCapacity * sizeof(Entry)) <= UINT32_MAX);
323 : }
324 :
325 1061707812 : static bool isLiveHash(HashNumber hash)
326 : {
327 1061707812 : return Entry::isLiveHash(hash);
328 : }
329 :
330 515552538 : static HashNumber prepareHash(const Lookup& l)
331 : {
332 515552538 : HashNumber keyHash = HashPolicy::hash(l);
333 :
334 : /* Improve keyHash distribution. */
335 515552538 : keyHash *= sGoldenRatio;
336 :
337 : /* Avoid reserved hash codes. */
338 515552538 : if (!isLiveHash(keyHash))
339 125536907 : keyHash -= (sRemovedKey + 1);
340 515552538 : return keyHash & ~sCollisionBit;
341 : }
342 :
343 6054815 : static Entry *createTable(AllocPolicy &alloc, uint32_t capacity)
344 : {
345 6054815 : Entry *newTable = (Entry *)alloc.malloc_(capacity * sizeof(Entry));
346 6054815 : if (!newTable)
347 0 : return NULL;
348 663740867 : for (Entry *e = newTable, *end = e + capacity; e < end; ++e)
349 657686052 : new(e) Entry();
350 6054815 : return newTable;
351 : }
352 :
353 6054645 : static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity)
354 : {
355 663731609 : for (Entry *e = oldTable, *end = e + capacity; e < end; ++e)
356 657676964 : e->~Entry();
357 6054645 : alloc.free_(oldTable);
358 6054645 : }
359 :
360 : public:
361 5529820 : HashTable(AllocPolicy ap)
362 : : AllocPolicy(ap),
363 : hashShift(sHashBits),
364 : entryCount(0),
365 : gen(0),
366 : removedCount(0),
367 : table(NULL)
368 : #ifdef DEBUG
369 : , entered(false),
370 5529820 : mutationCount(0)
371 : #endif
372 5529820 : {}
373 :
374 4895336 : bool init(uint32_t length)
375 : {
376 : /* Make sure that init isn't called twice. */
377 4895336 : JS_ASSERT(table == NULL);
378 :
379 : /*
380 : * Correct for sMaxAlphaFrac such that the table will not resize
381 : * when adding 'length' entries.
382 : */
383 4895336 : if (length > sMaxInit) {
384 0 : this->reportAllocOverflow();
385 0 : return false;
386 : }
387 4895336 : uint32_t capacity = (length * sInvMaxAlpha) >> 7;
388 :
389 4895336 : if (capacity < sMinSize)
390 1328764 : capacity = sMinSize;
391 :
392 : /* FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034). */
393 4895336 : uint32_t roundUp = sMinSize, roundUpLog2 = sMinSizeLog2;
394 22492266 : while (roundUp < capacity) {
395 12701594 : roundUp <<= 1;
396 12701594 : ++roundUpLog2;
397 : }
398 :
399 4895336 : capacity = roundUp;
400 4895336 : JS_ASSERT(capacity <= sMaxCapacity);
401 :
402 4895336 : table = createTable(*this, capacity);
403 4895336 : if (!table)
404 0 : return false;
405 :
406 4895336 : setTableSizeLog2(roundUpLog2);
407 4895336 : METER(memset(&stats, 0, sizeof(stats)));
408 4895336 : return true;
409 : }
410 :
411 81837972 : bool initialized() const
412 : {
413 81837972 : return !!table;
414 : }
415 :
416 5529637 : ~HashTable()
417 : {
418 5529637 : if (table)
419 4868161 : destroyTable(*this, table, capacity());
420 5529637 : }
421 :
422 : private:
423 690570253 : static HashNumber hash1(HashNumber hash0, uint32_t shift) {
424 690570253 : return hash0 >> shift;
425 : }
426 :
427 161432262 : static HashNumber hash2(HashNumber hash0, uint32_t log2, uint32_t shift) {
428 161432262 : return ((hash0 << log2) >> shift) | 1;
429 : }
430 :
431 128259723 : bool overloaded() {
432 128259723 : return entryCount + removedCount >= ((sMaxAlphaFrac * capacity()) >> 8);
433 : }
434 :
435 2275117 : bool underloaded() {
436 2275117 : uint32_t tableCapacity = capacity();
437 : return tableCapacity > sMinSize &&
438 2275117 : entryCount <= ((sMinAlphaFrac * tableCapacity) >> 8);
439 : }
440 :
441 173172113 : static bool match(Entry &e, const Lookup &l) {
442 173172113 : return HashPolicy::match(HashPolicy::getKey(e.t), l);
443 : }
444 :
445 546155274 : Entry &lookup(const Lookup &l, HashNumber keyHash, unsigned collisionBit) const
446 : {
447 546155274 : JS_ASSERT(isLiveHash(keyHash));
448 546155274 : JS_ASSERT(!(keyHash & sCollisionBit));
449 546155274 : JS_ASSERT(collisionBit == 0 || collisionBit == sCollisionBit);
450 546155274 : JS_ASSERT(table);
451 546155274 : METER(stats.searches++);
452 :
453 : /* Compute the primary hash address. */
454 546155274 : HashNumber h1 = hash1(keyHash, hashShift);
455 546155274 : Entry *entry = &table[h1];
456 :
457 : /* Miss: return space for a new entry. */
458 546155274 : if (entry->isFree()) {
459 288174012 : METER(stats.misses++);
460 288174012 : return *entry;
461 : }
462 :
463 : /* Hit: return entry. */
464 257981262 : if (entry->matchHash(keyHash) && match(*entry, l)) {
465 117520068 : METER(stats.hits++);
466 117520068 : return *entry;
467 : }
468 :
469 : /* Collision: double hash. */
470 140461194 : unsigned sizeLog2 = sHashBits - hashShift;
471 140461194 : HashNumber h2 = hash2(keyHash, sizeLog2, hashShift);
472 140461194 : HashNumber sizeMask = (HashNumber(1) << sizeLog2) - 1;
473 :
474 : /* Save the first removed entry pointer so we can recycle later. */
475 140461194 : Entry *firstRemoved = NULL;
476 :
477 183624734 : while(true) {
478 324085928 : if (JS_UNLIKELY(entry->isRemoved())) {
479 2134976 : if (!firstRemoved)
480 1526308 : firstRemoved = entry;
481 : } else {
482 321950952 : entry->setCollision(collisionBit);
483 : }
484 :
485 324085928 : METER(stats.steps++);
486 324085928 : h1 -= h2;
487 324085928 : h1 &= sizeMask;
488 :
489 324085928 : entry = &table[h1];
490 324085928 : if (entry->isFree()) {
491 91575294 : METER(stats.misses++);
492 91575294 : return firstRemoved ? *firstRemoved : *entry;
493 : }
494 :
495 232510634 : if (entry->matchHash(keyHash) && match(*entry, l)) {
496 48885900 : METER(stats.hits++);
497 48885900 : return *entry;
498 : }
499 : }
500 : }
501 :
502 : /*
503 : * This is a copy of lookup hardcoded to the assumptions:
504 : * 1. the lookup is a lookupForAdd
505 : * 2. the key, whose |keyHash| has been passed is not in the table,
506 : * 3. no entries have been removed from the table.
507 : * This specialized search avoids the need for recovering lookup values
508 : * from entries, which allows more flexible Lookup/Key types.
509 : */
510 144414979 : Entry &findFreeEntry(HashNumber keyHash)
511 : {
512 144414979 : METER(stats.searches++);
513 144414979 : JS_ASSERT(!(keyHash & sCollisionBit));
514 :
515 : /* N.B. the |keyHash| has already been distributed. */
516 :
517 : /* Compute the primary hash address. */
518 144414979 : HashNumber h1 = hash1(keyHash, hashShift);
519 144414979 : Entry *entry = &table[h1];
520 :
521 : /* Miss: return space for a new entry. */
522 144414979 : if (entry->isFree()) {
523 123443911 : METER(stats.misses++);
524 123443911 : return *entry;
525 : }
526 :
527 : /* Collision: double hash. */
528 20971068 : unsigned sizeLog2 = sHashBits - hashShift;
529 20971068 : HashNumber h2 = hash2(keyHash, sizeLog2, hashShift);
530 20971068 : HashNumber sizeMask = (HashNumber(1) << sizeLog2) - 1;
531 :
532 9089767 : while(true) {
533 30060835 : JS_ASSERT(!entry->isRemoved());
534 30060835 : entry->setCollision();
535 :
536 30060835 : METER(stats.steps++);
537 30060835 : h1 -= h2;
538 30060835 : h1 &= sizeMask;
539 :
540 30060835 : entry = &table[h1];
541 30060835 : if (entry->isFree()) {
542 20971068 : METER(stats.misses++);
543 20971068 : return *entry;
544 : }
545 : }
546 : }
547 :
548 1159479 : bool changeTableSize(int deltaLog2)
549 : {
550 : /* Look, but don't touch, until we succeed in getting new entry store. */
551 1159479 : Entry *oldTable = table;
552 1159479 : uint32_t oldCap = capacity();
553 1159479 : uint32_t newLog2 = sHashBits - hashShift + deltaLog2;
554 1159479 : uint32_t newCapacity = JS_BIT(newLog2);
555 1159479 : if (newCapacity > sMaxCapacity) {
556 0 : this->reportAllocOverflow();
557 0 : return false;
558 : }
559 :
560 1159479 : Entry *newTable = createTable(*this, newCapacity);
561 1159479 : if (!newTable)
562 0 : return false;
563 :
564 : /* We can't fail from here on, so update table parameters. */
565 1159479 : setTableSizeLog2(newLog2);
566 1159479 : removedCount = 0;
567 1159479 : gen++;
568 1159479 : table = newTable;
569 :
570 : /* Copy only live entries, leaving removed ones behind. */
571 291210031 : for (Entry *src = oldTable, *end = src + oldCap; src < end; ++src) {
572 290050552 : if (src->isLive()) {
573 143607921 : src->unsetCollision();
574 143607921 : findFreeEntry(src->getKeyHash()) = Move(*src);
575 : }
576 : }
577 :
578 1159479 : destroyTable(*this, oldTable, oldCap);
579 1159479 : return true;
580 : }
581 :
582 29136946 : void remove(Entry &e)
583 : {
584 29136946 : METER(stats.removes++);
585 29136946 : if (e.hasCollision()) {
586 8041974 : e.setRemoved();
587 8041974 : removedCount++;
588 : } else {
589 21094972 : METER(stats.removeFrees++);
590 21094972 : e.setFree();
591 : }
592 29136946 : entryCount--;
593 : #ifdef DEBUG
594 29136946 : mutationCount++;
595 : #endif
596 29136946 : }
597 :
598 2275117 : void checkUnderloaded()
599 : {
600 2275117 : if (underloaded()) {
601 352421 : METER(stats.shrinks++);
602 352421 : (void) changeTableSize(-1);
603 : }
604 2275117 : }
605 :
606 : public:
607 132537 : void clear()
608 : {
609 : if (tl::IsPodType<Entry>::result) {
610 91686 : memset(table, 0, sizeof(*table) * capacity());
611 : } else {
612 40851 : uint32_t tableCapacity = capacity();
613 5000347 : for (Entry *e = table, *end = table + tableCapacity; e < end; ++e)
614 4959496 : *e = Move(Entry());
615 : }
616 132537 : removedCount = 0;
617 132537 : entryCount = 0;
618 : #ifdef DEBUG
619 132537 : mutationCount++;
620 : #endif
621 132537 : }
622 :
623 27005 : void finish()
624 : {
625 27005 : JS_ASSERT(!entered);
626 :
627 27005 : if (!table)
628 0 : return;
629 :
630 27005 : destroyTable(*this, table, capacity());
631 27005 : table = NULL;
632 27005 : gen++;
633 27005 : entryCount = 0;
634 27005 : removedCount = 0;
635 : #ifdef DEBUG
636 27005 : mutationCount++;
637 : #endif
638 : }
639 :
640 1621016 : Range all() const {
641 1621016 : return Range(table, table + capacity());
642 : }
643 :
644 34210721 : bool empty() const {
645 34210721 : return !entryCount;
646 : }
647 :
648 14046173 : uint32_t count() const {
649 14046173 : return entryCount;
650 : }
651 :
652 139150096 : uint32_t capacity() const {
653 139150096 : return JS_BIT(sHashBits - hashShift);
654 : }
655 :
656 7055108 : uint32_t generation() const {
657 7055108 : return gen;
658 : }
659 :
660 1254 : size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
661 1254 : return mallocSizeOf(table);
662 : }
663 :
664 : size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
665 : return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
666 : }
667 :
668 254004195 : Ptr lookup(const Lookup &l) const {
669 508008390 : ReentrancyGuard g(*this);
670 254004195 : HashNumber keyHash = prepareHash(l);
671 254004195 : return Ptr(lookup(l, keyHash, 0));
672 : }
673 :
674 261548343 : AddPtr lookupForAdd(const Lookup &l) const {
675 523096686 : ReentrancyGuard g(*this);
676 261548343 : HashNumber keyHash = prepareHash(l);
677 261548343 : Entry &entry = lookup(l, keyHash, sCollisionBit);
678 : #ifdef DEBUG
679 261548343 : return AddPtr(entry, keyHash, mutationCount);
680 : #else
681 : return AddPtr(entry, keyHash);
682 : #endif
683 : }
684 :
685 128758205 : bool add(AddPtr &p)
686 : {
687 257516410 : ReentrancyGuard g(*this);
688 128758205 : JS_ASSERT(mutationCount == p.mutationCount);
689 128758205 : JS_ASSERT(table);
690 128758205 : JS_ASSERT(!p.found());
691 128758205 : JS_ASSERT(!(p.keyHash & sCollisionBit));
692 :
693 : /*
694 : * Changing an entry from removed to live does not affect whether we
695 : * are overloaded and can be handled separately.
696 : */
697 128758205 : if (p.entry->isRemoved()) {
698 498482 : METER(stats.addOverRemoved++);
699 498482 : removedCount--;
700 498482 : p.keyHash |= sCollisionBit;
701 : } else {
702 : /* If alpha is >= .75, grow or compress the table. */
703 128259723 : if (overloaded()) {
704 : /* Compress if a quarter or more of all entries are removed. */
705 : int deltaLog2;
706 807058 : if (removedCount >= (capacity() >> 2)) {
707 509 : METER(stats.compresses++);
708 509 : deltaLog2 = 0;
709 : } else {
710 806549 : METER(stats.grows++);
711 806549 : deltaLog2 = 1;
712 : }
713 :
714 807058 : if (!changeTableSize(deltaLog2))
715 0 : return false;
716 :
717 : /* Preserve the validity of |p.entry|. */
718 807058 : p.entry = &findFreeEntry(p.keyHash);
719 : }
720 : }
721 :
722 128758205 : p.entry->setLive(p.keyHash);
723 128758205 : entryCount++;
724 : #ifdef DEBUG
725 128758205 : mutationCount++;
726 : #endif
727 128758205 : return true;
728 : }
729 :
730 : /*
731 : * There is an important contract between the caller and callee for this
732 : * function: if add() returns true, the caller must assign the T value
733 : * which produced p before using the hashtable again.
734 : */
735 26734986 : bool add(AddPtr &p, T** pentry)
736 : {
737 26734986 : if (!add(p))
738 0 : return false;
739 26734986 : *pentry = &p.entry->t;
740 26734986 : return true;
741 : }
742 :
743 102023219 : bool add(AddPtr &p, const T &t)
744 : {
745 102023219 : if (!add(p))
746 0 : return false;
747 102023219 : p.entry->t = t;
748 102023219 : return true;
749 : }
750 :
751 30602736 : bool relookupOrAdd(AddPtr& p, const Lookup &l, const T& t)
752 : {
753 : #ifdef DEBUG
754 30602736 : p.mutationCount = mutationCount;
755 : #endif
756 : {
757 61205472 : ReentrancyGuard g(*this);
758 30602736 : p.entry = &lookup(l, p.keyHash, sCollisionBit);
759 : }
760 30602736 : return p.found() || add(p, t);
761 : }
762 :
763 2058644 : void remove(Ptr p)
764 : {
765 4117288 : ReentrancyGuard g(*this);
766 2058644 : JS_ASSERT(p.found());
767 2058644 : remove(*p.entry);
768 2058644 : checkUnderloaded();
769 2058644 : }
770 : #undef METER
771 : };
772 :
773 : } /* namespace detail */
774 :
775 : /*****************************************************************************/
776 :
777 : /*
778 : * Hash policy
779 : *
780 : * A hash policy P for a hash table with key-type Key must provide:
781 : * - a type |P::Lookup| to use to lookup table entries;
782 : * - a static member function |P::hash| with signature
783 : *
784 : * static js::HashNumber hash(Lookup)
785 : *
786 : * to use to hash the lookup type; and
787 : * - a static member function |P::match| with signature
788 : *
789 : * static bool match(Key, Lookup)
790 : *
791 : * to use to test equality of key and lookup values.
792 : *
793 : * Normally, Lookup = Key. In general, though, different values and types of
794 : * values can be used to lookup and store. If a Lookup value |l| is != to the
795 : * added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.:
796 : *
797 : * js::HashSet<Key, P>::AddPtr p = h.lookup(l);
798 : * if (!p) {
799 : * assert(P::match(k, l)); // must hold
800 : * h.add(p, k);
801 : * }
802 : */
803 :
804 : /* Default hashing policies. */
805 : template <class Key>
806 : struct DefaultHasher
807 : {
808 : typedef Key Lookup;
809 : static HashNumber hash(const Lookup &l) {
810 : /* Hash if can implicitly cast to hash number type. */
811 : return l;
812 : }
813 : static bool match(const Key &k, const Lookup &l) {
814 : /* Use builtin or overloaded operator==. */
815 : return k == l;
816 : }
817 : };
818 :
819 : /*
820 : * Pointer hashing policy that strips the lowest zeroBits when calculating the
821 : * hash to improve key distribution.
822 : */
823 : template <typename Key, size_t zeroBits>
824 : struct PointerHasher
825 : {
826 : typedef Key Lookup;
827 84797681 : static HashNumber hash(const Lookup &l) {
828 84797681 : size_t word = reinterpret_cast<size_t>(l) >> zeroBits;
829 : JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
830 : #if JS_BYTES_PER_WORD == 4
831 84797681 : return HashNumber(word);
832 : #else
833 : JS_STATIC_ASSERT(sizeof word == 8);
834 : return HashNumber((word >> 32) ^ word);
835 : #endif
836 : }
837 22884365 : static bool match(const Key &k, const Lookup &l) {
838 22884365 : return k == l;
839 : }
840 : };
841 :
842 : template <typename Key, size_t zeroBits>
843 : struct TaggedPointerHasher
844 : {
845 : typedef Key Lookup;
846 :
847 : static HashNumber hash(const Lookup &l) {
848 : return PointerHasher<Key, zeroBits>::hash(l);
849 : }
850 :
851 : static const uintptr_t COMPARE_MASK = uintptr_t(-1) - 1;
852 :
853 : static bool match(const Key &k, const Lookup &l) {
854 : return (uintptr_t(k) & COMPARE_MASK) == uintptr_t(l);
855 : }
856 : };
857 :
858 : /*
859 : * Specialized hashing policy for pointer types. It assumes that the type is
860 : * at least word-aligned. For types with smaller size use PointerHasher.
861 : */
862 : template <class T>
863 : struct DefaultHasher<T *>: PointerHasher<T *, tl::FloorLog2<sizeof(void *)>::result> { };
864 :
865 : /* Looking for a hasher for jsid? Try the DefaultHasher<jsid> in jsatom.h. */
866 :
867 : template <class Key, class Value>
868 : class HashMapEntry
869 20771116 : {
870 : template <class, class, class> friend class detail::HashTable;
871 : template <class> friend class detail::HashTableEntry;
872 1041747 : void operator=(const HashMapEntry &rhs) {
873 1041747 : const_cast<Key &>(key) = rhs.key;
874 1041747 : value = rhs.value;
875 1041747 : }
876 :
877 : public:
878 160611976 : HashMapEntry() : key(), value() {}
879 :
880 : template<typename KeyInput, typename ValueInput>
881 73283 : HashMapEntry(const KeyInput &k, const ValueInput &v) : key(k), value(v) {}
882 :
883 : HashMapEntry(MoveRef<HashMapEntry> rhs)
884 : : key(Move(rhs->key)), value(Move(rhs->value)) { }
885 36007820 : void operator=(MoveRef<HashMapEntry> rhs) {
886 36007820 : const_cast<Key &>(key) = Move(rhs->key);
887 36007820 : value = Move(rhs->value);
888 36007820 : }
889 :
890 : const Key key;
891 : Value value;
892 : };
893 :
894 : namespace tl {
895 :
896 : template <class T>
897 : struct IsPodType<detail::HashTableEntry<T> > {
898 : static const bool result = IsPodType<T>::result;
899 : };
900 :
901 : template <class K, class V>
902 : struct IsPodType<HashMapEntry<K, V> >
903 : {
904 : static const bool result = IsPodType<K>::result && IsPodType<V>::result;
905 : };
906 :
907 : } /* namespace tl */
908 :
909 : /*
910 : * JS-friendly, STL-like container providing a hash-based map from keys to
911 : * values. In particular, HashMap calls constructors and destructors of all
912 : * objects added so non-PODs may be used safely.
913 : *
914 : * Key/Value requirements:
915 : * - default constructible, copyable, destructible, assignable
916 : * HashPolicy requirements:
917 : * - see "Hash policy" above (default js::DefaultHasher<Key>)
918 : * AllocPolicy:
919 : * - see "Allocation policies" in jsalloc.h
920 : *
921 : * N.B: HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members
922 : * called by HashMap must not call back into the same HashMap object.
923 : * N.B: Due to the lack of exception handling, the user must call |init()|.
924 : */
925 : template <class Key,
926 : class Value,
927 : class HashPolicy = DefaultHasher<Key>,
928 : class AllocPolicy = TempAllocPolicy>
929 : class HashMap
930 2308178 : {
931 : public:
932 : typedef typename HashPolicy::Lookup Lookup;
933 :
934 : typedef HashMapEntry<Key, Value> Entry;
935 :
936 : private:
937 : /* Implement HashMap using HashTable. Lift |Key| operations to |Entry|. */
938 : struct MapHashPolicy : HashPolicy
939 : {
940 : typedef Key KeyType;
941 24439222 : static const Key &getKey(Entry &e) { return e.key; }
942 : };
943 : typedef detail::HashTable<Entry, MapHashPolicy, AllocPolicy> Impl;
944 :
945 : friend class Impl::Enum;
946 :
947 : /* Not implicitly copyable (expensive). May add explicit |clone| later. */
948 : HashMap(const HashMap &);
949 : HashMap &operator=(const HashMap &);
950 :
951 : Impl impl;
952 :
953 : public:
954 : /*
955 : * HashMap construction is fallible (due to OOM); thus the user must call
956 : * init after constructing a HashMap and check the return value.
957 : */
958 2308197 : HashMap(AllocPolicy a = AllocPolicy()) : impl(a) {}
959 1766650 : bool init(uint32_t len = Impl::sDefaultInitSize) { return impl.init(len); }
960 7306773 : bool initialized() const { return impl.initialized(); }
961 :
962 : /*
963 : * Return whether the given lookup value is present in the map. E.g.:
964 : *
965 : * typedef HashMap<int,char> HM;
966 : * HM h;
967 : * if (HM::Ptr p = h.lookup(3)) {
968 : * const HM::Entry &e = *p; // p acts like a pointer to Entry
969 : * assert(p->key == 3); // Entry contains the key
970 : * char val = p->value; // and value
971 : * }
972 : *
973 : * Also see the definition of Ptr in HashTable above (with T = Entry).
974 : */
975 : typedef typename Impl::Ptr Ptr;
976 18757702 : Ptr lookup(const Lookup &l) const { return impl.lookup(l); }
977 :
978 : /* Assuming |p.found()|, remove |*p|. */
979 841325 : void remove(Ptr p) { impl.remove(p); }
980 :
981 : /*
982 : * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
983 : * insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using
984 : * |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.:
985 : *
986 : * typedef HashMap<int,char> HM;
987 : * HM h;
988 : * HM::AddPtr p = h.lookupForAdd(3);
989 : * if (!p) {
990 : * if (!h.add(p, 3, 'a'))
991 : * return false;
992 : * }
993 : * const HM::Entry &e = *p; // p acts like a pointer to Entry
994 : * assert(p->key == 3); // Entry contains the key
995 : * char val = p->value; // and value
996 : *
997 : * Also see the definition of AddPtr in HashTable above (with T = Entry).
998 : *
999 : * N.B. The caller must ensure that no mutating hash table operations
1000 : * occur between a pair of |lookupForAdd| and |add| calls. To avoid
1001 : * looking up the key a second time, the caller may use the more efficient
1002 : * relookupOrAdd method. This method reuses part of the hashing computation
1003 : * to more efficiently insert the key if it has not been added. For
1004 : * example, a mutation-handling version of the previous example:
1005 : *
1006 : * HM::AddPtr p = h.lookupForAdd(3);
1007 : * if (!p) {
1008 : * call_that_may_mutate_h();
1009 : * if (!h.relookupOrAdd(p, 3, 'a'))
1010 : * return false;
1011 : * }
1012 : * const HM::Entry &e = *p;
1013 : * assert(p->key == 3);
1014 : * char val = p->value;
1015 : */
1016 : typedef typename Impl::AddPtr AddPtr;
1017 40202329 : AddPtr lookupForAdd(const Lookup &l) const {
1018 40202329 : return impl.lookupForAdd(l);
1019 : }
1020 :
1021 : template<typename KeyInput, typename ValueInput>
1022 23391882 : bool add(AddPtr &p, const KeyInput &k, const ValueInput &v) {
1023 : Entry *pentry;
1024 23391882 : if (!impl.add(p, &pentry))
1025 0 : return false;
1026 23391882 : const_cast<Key &>(pentry->key) = k;
1027 23391882 : pentry->value = v;
1028 23391882 : return true;
1029 : }
1030 :
1031 3343104 : bool add(AddPtr &p, const Key &k, MoveRef<Value> v) {
1032 : Entry *pentry;
1033 3343104 : if (!impl.add(p, &pentry))
1034 0 : return false;
1035 3343104 : const_cast<Key &>(pentry->key) = k;
1036 3343104 : pentry->value = v;
1037 3343104 : return true;
1038 : }
1039 :
1040 : bool add(AddPtr &p, const Key &k) {
1041 : Entry *pentry;
1042 : if (!impl.add(p, &pentry))
1043 : return false;
1044 : const_cast<Key &>(pentry->key) = k;
1045 : return true;
1046 : }
1047 :
1048 : template<typename KeyInput, typename ValueInput>
1049 73283 : bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) {
1050 73283 : return impl.relookupOrAdd(p, k, Entry(k, v));
1051 : }
1052 :
1053 : /*
1054 : * |all()| returns a Range containing |count()| elements. E.g.:
1055 : *
1056 : * typedef HashMap<int,char> HM;
1057 : * HM h;
1058 : * for (HM::Range r = h.all(); !r.empty(); r.popFront())
1059 : * char c = r.front().value;
1060 : *
1061 : * Also see the definition of Range in HashTable above (with T = Entry).
1062 : */
1063 : typedef typename Impl::Range Range;
1064 523816 : Range all() const { return impl.all(); }
1065 12139730 : uint32_t count() const { return impl.count(); }
1066 : size_t capacity() const { return impl.capacity(); }
1067 : size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
1068 : return impl.sizeOfExcludingThis(mallocSizeOf);
1069 : }
1070 0 : size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
1071 : /*
1072 : * Don't just call |impl.sizeOfExcludingThis()| because there's no
1073 : * guarantee that |impl| is the first field in HashMap.
1074 : */
1075 0 : return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
1076 : }
1077 :
1078 : /*
1079 : * Typedef for the enumeration class. An Enum may be used to examine and
1080 : * remove table entries:
1081 : *
1082 : * typedef HashMap<int,char> HM;
1083 : * HM s;
1084 : * for (HM::Enum e(s); !e.empty(); e.popFront())
1085 : * if (e.front().value == 'l')
1086 : * e.removeFront();
1087 : *
1088 : * Table resize may occur in Enum's destructor. Also see the definition of
1089 : * Enum in HashTable above (with T = Entry).
1090 : */
1091 : typedef typename Impl::Enum Enum;
1092 :
1093 : /*
1094 : * Remove all entries. This does not shrink the table. For that consider
1095 : * using the finish() method.
1096 : */
1097 112629 : void clear() { impl.clear(); }
1098 :
1099 : /*
1100 : * Remove all the entries and release all internal buffers. The map must
1101 : * be initialized again before any use.
1102 : */
1103 27005 : void finish() { impl.finish(); }
1104 :
1105 : /* Does the table contain any entries? */
1106 70736 : bool empty() const { return impl.empty(); }
1107 :
1108 : /*
1109 : * If |generation()| is the same before and after a HashMap operation,
1110 : * pointers into the table remain valid.
1111 : */
1112 6693774 : unsigned generation() const { return impl.generation(); }
1113 :
1114 : /* Shorthand operations: */
1115 :
1116 2548 : bool has(const Lookup &l) const {
1117 2548 : return impl.lookup(l) != NULL;
1118 : }
1119 :
1120 : /* Overwrite existing value with v. Return NULL on oom. */
1121 : template<typename KeyInput, typename ValueInput>
1122 2677218 : Entry *put(const KeyInput &k, const ValueInput &v) {
1123 2677218 : AddPtr p = lookupForAdd(k);
1124 2677218 : if (p) {
1125 577 : p->value = v;
1126 577 : return &*p;
1127 : }
1128 2676641 : return add(p, k, v) ? &*p : NULL;
1129 : }
1130 :
1131 : /* Like put, but assert that the given key is not already present. */
1132 2273690 : bool putNew(const Key &k, const Value &v) {
1133 2273690 : AddPtr p = lookupForAdd(k);
1134 2273690 : JS_ASSERT(!p);
1135 2273690 : return add(p, k, v);
1136 : }
1137 :
1138 : /* Add (k,defaultValue) if k no found. Return false-y Ptr on oom. */
1139 0 : Ptr lookupWithDefault(const Key &k, const Value &defaultValue) {
1140 0 : AddPtr p = lookupForAdd(k);
1141 0 : if (p)
1142 0 : return p;
1143 0 : (void)add(p, k, defaultValue); /* p is left false-y on oom. */
1144 0 : return p;
1145 : }
1146 :
1147 : /* Remove if present. */
1148 493431 : void remove(const Lookup &l) {
1149 493431 : if (Ptr p = lookup(l))
1150 493431 : remove(p);
1151 493431 : }
1152 : };
1153 :
1154 : /*
1155 : * JS-friendly, STL-like container providing a hash-based set of values. In
1156 : * particular, HashSet calls constructors and destructors of all objects added
1157 : * so non-PODs may be used safely.
1158 : *
1159 : * T requirements:
1160 : * - default constructible, copyable, destructible, assignable
1161 : * HashPolicy requirements:
1162 : * - see "Hash policy" above (default js::DefaultHasher<Key>)
1163 : * AllocPolicy:
1164 : * - see "Allocation policies" in jsalloc.h
1165 : *
1166 : * N.B: HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by
1167 : * HashSet must not call back into the same HashSet object.
1168 : * N.B: Due to the lack of exception handling, the user must call |init()|.
1169 : */
1170 : template <class T, class HashPolicy = DefaultHasher<T>, class AllocPolicy = TempAllocPolicy>
1171 : class HashSet
1172 3221459 : {
1173 : typedef typename HashPolicy::Lookup Lookup;
1174 :
1175 : /* Implement HashSet in terms of HashTable. */
1176 : struct SetOps : HashPolicy {
1177 : typedef T KeyType;
1178 148732891 : static const KeyType &getKey(const T &t) { return t; }
1179 : };
1180 : typedef detail::HashTable<const T, SetOps, AllocPolicy> Impl;
1181 :
1182 : friend class Impl::Enum;
1183 :
1184 : /* Not implicitly copyable (expensive). May add explicit |clone| later. */
1185 : HashSet(const HashSet &);
1186 : HashSet &operator=(const HashSet &);
1187 :
1188 : Impl impl;
1189 :
1190 : public:
1191 : /*
1192 : * HashSet construction is fallible (due to OOM); thus the user must call
1193 : * init after constructing a HashSet and check the return value.
1194 : */
1195 3221623 : HashSet(AllocPolicy a = AllocPolicy()) : impl(a) {}
1196 3128686 : bool init(uint32_t len = Impl::sDefaultInitSize) { return impl.init(len); }
1197 74531199 : bool initialized() const { return impl.initialized(); }
1198 :
1199 : /*
1200 : * Return whether the given lookup value is present in the map. E.g.:
1201 : *
1202 : * typedef HashSet<int> HS;
1203 : * HS h;
1204 : * if (HS::Ptr p = h.lookup(3)) {
1205 : * assert(*p == 3); // p acts like a pointer to int
1206 : * }
1207 : *
1208 : * Also see the definition of Ptr in HashTable above.
1209 : */
1210 : typedef typename Impl::Ptr Ptr;
1211 19553866 : Ptr lookup(const Lookup &l) const { return impl.lookup(l); }
1212 :
1213 : /* Assuming |p.found()|, remove |*p|. */
1214 1217319 : void remove(Ptr p) { impl.remove(p); }
1215 :
1216 : /*
1217 : * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
1218 : * insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using
1219 : * |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.:
1220 : *
1221 : * typedef HashSet<int> HS;
1222 : * HS h;
1223 : * HS::AddPtr p = h.lookupForAdd(3);
1224 : * if (!p) {
1225 : * if (!h.add(p, 3))
1226 : * return false;
1227 : * }
1228 : * assert(*p == 3); // p acts like a pointer to int
1229 : *
1230 : * Also see the definition of AddPtr in HashTable above.
1231 : *
1232 : * N.B. The caller must ensure that no mutating hash table operations
1233 : * occur between a pair of |lookupForAdd| and |add| calls. To avoid
1234 : * looking up the key a second time, the caller may use the more efficient
1235 : * relookupOrAdd method. This method reuses part of the hashing computation
1236 : * to more efficiently insert the key if it has not been added. For
1237 : * example, a mutation-handling version of the previous example:
1238 : *
1239 : * HS::AddPtr p = h.lookupForAdd(3);
1240 : * if (!p) {
1241 : * call_that_may_mutate_h();
1242 : * if (!h.relookupOrAdd(p, 3, 3))
1243 : * return false;
1244 : * }
1245 : * assert(*p == 3);
1246 : *
1247 : * Note that relookupOrAdd(p,l,t) performs Lookup using l and adds the
1248 : * entry t, where the caller ensures match(l,t).
1249 : */
1250 : typedef typename Impl::AddPtr AddPtr;
1251 221346014 : AddPtr lookupForAdd(const Lookup &l) const {
1252 221346014 : return impl.lookupForAdd(l);
1253 : }
1254 :
1255 71420483 : bool add(AddPtr &p, const T &t) {
1256 71420483 : return impl.add(p, t);
1257 : }
1258 :
1259 30529453 : bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) {
1260 30529453 : return impl.relookupOrAdd(p, l, t);
1261 : }
1262 :
1263 : /*
1264 : * |all()| returns a Range containing |count()| elements:
1265 : *
1266 : * typedef HashSet<int> HS;
1267 : * HS h;
1268 : * for (HS::Range r = h.all(); !r.empty(); r.popFront())
1269 : * int i = r.front();
1270 : *
1271 : * Also see the definition of Range in HashTable above.
1272 : */
1273 : typedef typename Impl::Range Range;
1274 1097200 : Range all() const { return impl.all(); }
1275 1906443 : uint32_t count() const { return impl.count(); }
1276 : size_t capacity() const { return impl.capacity(); }
1277 48 : size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
1278 48 : return impl.sizeOfExcludingThis(mallocSizeOf);
1279 : }
1280 1206 : size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
1281 : /*
1282 : * Don't just call |impl.sizeOfExcludingThis()| because there's no
1283 : * guarantee that |impl| is the first field in HashSet.
1284 : */
1285 1206 : return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
1286 : }
1287 :
1288 : /*
1289 : * Typedef for the enumeration class. An Enum may be used to examine and
1290 : * remove table entries:
1291 : *
1292 : * typedef HashSet<int> HS;
1293 : * HS s;
1294 : * for (HS::Enum e(s); !e.empty(); e.popFront())
1295 : * if (e.front() == 42)
1296 : * e.removeFront();
1297 : *
1298 : * Table resize may occur in Enum's destructor. Also see the definition of
1299 : * Enum in HashTable above.
1300 : */
1301 : typedef typename Impl::Enum Enum;
1302 :
1303 : /*
1304 : * Remove all entries. This does not shrink the table. For that consider
1305 : * using the finish() method.
1306 : */
1307 19908 : void clear() { impl.clear(); }
1308 :
1309 : /*
1310 : * Remove all the entries and release all internal buffers. The set must
1311 : * be initialized again before any use.
1312 : */
1313 0 : void finish() { impl.finish(); }
1314 :
1315 : /* Does the table contain any entries? */
1316 34139985 : bool empty() const { return impl.empty(); }
1317 :
1318 : /*
1319 : * If |generation()| is the same before and after a HashSet operation,
1320 : * pointers into the table remain valid.
1321 : */
1322 361334 : unsigned generation() const { return impl.generation(); }
1323 :
1324 : /* Shorthand operations: */
1325 :
1326 215690079 : bool has(const Lookup &l) const {
1327 215690079 : return impl.lookup(l) != NULL;
1328 : }
1329 :
1330 : /* Overwrite existing value with v. Return NULL on oom. */
1331 620963 : const T *put(const T &t) {
1332 620963 : AddPtr p = lookupForAdd(t);
1333 620963 : return p ? &*p : (add(p, t) ? &*p : NULL);
1334 : }
1335 :
1336 : /* Like put, but assert that the given key is not already present. */
1337 : bool putNew(const T &t) {
1338 : AddPtr p = lookupForAdd(t);
1339 : JS_ASSERT(!p);
1340 : return add(p, t);
1341 : }
1342 :
1343 3331435 : bool putNew(const Lookup &l, const T &t) {
1344 3331435 : AddPtr p = lookupForAdd(l);
1345 3331435 : JS_ASSERT(!p);
1346 3331435 : return add(p, t);
1347 : }
1348 :
1349 1017233 : void remove(const Lookup &l) {
1350 1017233 : if (Ptr p = lookup(l))
1351 1017233 : remove(p);
1352 1017233 : }
1353 : };
1354 :
1355 : } /* namespace js */
1356 :
1357 : #endif
|