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 533808308 : 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 -1922557132 : static bool isLiveHash(HashNumber hash)
76 : {
77 -1922557132 : return hash > sRemovedKey;
78 : }
79 :
80 : public:
81 540800196 : 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 126752654 : void operator=(MoveRef<HashTableEntry> rhs) { keyHash = rhs->keyHash; t = Move(rhs->t); }
85 :
86 : NonConstT t;
87 :
88 806717889 : bool isFree() const { return keyHash == sFreeKey; }
89 12248994 : void setFree() { keyHash = sFreeKey; t = T(); }
90 355932938 : bool isRemoved() const { return keyHash == sRemovedKey; }
91 3999617 : void setRemoved() { keyHash = sRemovedKey; t = T(); }
92 1422174543 : bool isLive() const { return isLiveHash(keyHash); }
93 102395950 : void setLive(HashNumber hn) { JS_ASSERT(isLiveHash(hn)); keyHash = hn; }
94 :
95 22399004 : void setCollision() { JS_ASSERT(isLive()); keyHash |= sCollisionBit; }
96 230454619 : void setCollision(HashNumber collisionBit) {
97 230454619 : JS_ASSERT(isLive()); keyHash |= collisionBit;
98 230454619 : }
99 119750190 : void unsetCollision() { keyHash &= ~sCollisionBit; }
100 135998801 : bool hasCollision() const { JS_ASSERT(isLive()); return keyHash & sCollisionBit; }
101 335262681 : bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; }
102 119750190 : 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 414634394 : Ptr(Entry &entry) : entry(&entry) {}
138 :
139 : public:
140 : /* Leaves Ptr uninitialized. */
141 9487930 : Ptr() {
142 : #ifdef DEBUG
143 9487930 : entry = (Entry *)0xbad;
144 : #endif
145 9487930 : }
146 :
147 532129659 : bool found() const { return entry->isLive(); }
148 408247616 : 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 45595323 : T &operator*() const { return entry->t; }
153 54646749 : 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 186703248 : {
159 : friend class HashTable;
160 : HashNumber keyHash;
161 : DebugOnly<uint64_t> mutationCount;
162 :
163 180578947 : AddPtr(Entry &entry, HashNumber hn) : Ptr(entry), keyHash(hn) {}
164 : public:
165 : /* Leaves AddPtr uninitialized. */
166 4790663 : AddPtr() {}
167 : };
168 :
169 : /*
170 : * A collection of hash table entries. The collection is enumerated by
171 : * calling |front()| followed by |popFront()| as long as |!empty()|. As
172 : * with Ptr/AddPtr, Range objects must not be used after any mutating hash
173 : * table operation unless the |generation()| is tested.
174 : */
175 : class Range
176 : {
177 : protected:
178 : friend class HashTable;
179 :
180 1069429 : Range(Entry *c, Entry *e) : cur(c), end(e) {
181 56305919 : while (cur < end && !cur->isLive())
182 54167061 : ++cur;
183 1069429 : }
184 :
185 : Entry *cur, *end;
186 :
187 : public:
188 127476 : Range() : cur(NULL), end(NULL) {}
189 :
190 215289624 : bool empty() const {
191 215289624 : return cur == end;
192 : }
193 :
194 76553596 : T &front() const {
195 76553596 : JS_ASSERT(!empty());
196 76553596 : return cur->t;
197 : }
198 :
199 68839776 : void popFront() {
200 68839776 : JS_ASSERT(!empty());
201 296111544 : while (++cur < end && !cur->isLive())
202 158431992 : continue;
203 68839776 : }
204 : };
205 :
206 : /*
207 : * A Range whose lifetime delimits a mutating enumeration of a hash table.
208 : * Since rehashing when elements were removed during enumeration would be
209 : * bad, it is postponed until |endEnumeration()| is called. If
210 : * |endEnumeration()| is not called before an Enum's constructor, it will
211 : * be called automatically. Since |endEnumeration()| touches the hash
212 : * table, the user must ensure that the hash table is still alive when this
213 : * happens.
214 : */
215 : class Enum : public Range
216 : {
217 : friend class HashTable;
218 :
219 : HashTable &table;
220 : bool added;
221 : bool removed;
222 :
223 : /* Not copyable. */
224 : Enum(const Enum &);
225 : void operator=(const Enum &);
226 :
227 : public:
228 : template<class Map> explicit
229 601929 : Enum(Map &map) : Range(map.all()), table(map.impl), added(false), removed(false) {}
230 :
231 : /*
232 : * Removes the |front()| element from the table, leaving |front()|
233 : * invalid until the next call to |popFront()|. For example:
234 : *
235 : * HashSet<int> s;
236 : * for (HashSet<int>::Enum e(s); !e.empty(); e.popFront())
237 : * if (e.front() == 42)
238 : * e.removeFront();
239 : */
240 15387131 : void removeFront() {
241 15387131 : table.remove(*this->cur);
242 15387131 : removed = true;
243 15387131 : }
244 :
245 : /*
246 : * Removes the |front()| element and re-inserts it into the table with
247 : * a new key at the new Lookup position. |front()| is invalid after
248 : * this operation until the next call to |popFront()|.
249 : */
250 299938 : void rekeyFront(Key &k) {
251 299938 : JS_ASSERT(&k != &HashPolicy::getKey(this->cur->t));
252 299938 : JS_ASSERT(!table.match(*this->cur, k));
253 299938 : Entry e = *this->cur;
254 299938 : HashPolicy::setKey(e.t, k);
255 299938 : table.remove(*this->cur);
256 299938 : table.add(k, e);
257 299938 : added = true;
258 299938 : }
259 :
260 : /* Potentially rehashes the table. */
261 601929 : ~Enum() {
262 601929 : if (added)
263 40 : table.checkOverloaded();
264 601929 : if (removed)
265 164833 : table.checkUnderloaded();
266 601929 : }
267 :
268 : /* Can be used to end the enumeration before the destructor. */
269 : void endEnumeration() {
270 : if (added) {
271 : table.checkOverloaded();
272 : added = false;
273 : }
274 : if (removed) {
275 : table.checkUnderloaded();
276 : removed = false;
277 : }
278 : }
279 : };
280 :
281 : private:
282 : uint32_t hashShift; /* multiplicative hash shift */
283 : uint32_t entryCount; /* number of entries in table */
284 : uint32_t gen; /* entry storage generation number */
285 : uint32_t removedCount; /* removed entry sentinels in table */
286 : Entry *table; /* entry storage */
287 :
288 3838054 : void setTableSizeLog2(unsigned sizeLog2) {
289 3838054 : hashShift = sHashBits - sizeLog2;
290 3838054 : }
291 :
292 : #ifdef DEBUG
293 : mutable struct Stats {
294 : uint32_t searches; /* total number of table searches */
295 : uint32_t steps; /* hash chain links traversed */
296 : uint32_t hits; /* searches that found key */
297 : uint32_t misses; /* searches that didn't find key */
298 : uint32_t addOverRemoved; /* adds that recycled a removed entry */
299 : uint32_t removes; /* calls to remove */
300 : uint32_t removeFrees; /* calls to remove that freed the entry */
301 : uint32_t grows; /* table expansions */
302 : uint32_t shrinks; /* table contractions */
303 : uint32_t compresses; /* table compressions */
304 : } stats;
305 : # define METER(x) x
306 : #else
307 : # define METER(x)
308 : #endif
309 :
310 : friend class js::ReentrancyGuard;
311 : mutable DebugOnly<bool> entered;
312 : DebugOnly<uint64_t> mutationCount;
313 :
314 : /* The default initial capacity is 16, but you can ask for as small as 4. */
315 : static const unsigned sMinSizeLog2 = 2;
316 : static const unsigned sMinSize = 1 << sMinSizeLog2;
317 : static const unsigned sDefaultInitSizeLog2 = 4;
318 : public:
319 : static const unsigned sDefaultInitSize = 1 << sDefaultInitSizeLog2;
320 : private:
321 : static const unsigned sMaxInit = JS_BIT(23);
322 : static const unsigned sMaxCapacity = JS_BIT(24);
323 : static const unsigned sHashBits = tl::BitSize<HashNumber>::result;
324 : static const uint8_t sMinAlphaFrac = 64; /* (0x100 * .25) taken from jsdhash.h */
325 : static const uint8_t sMaxAlphaFrac = 192; /* (0x100 * .75) taken from jsdhash.h */
326 : static const uint8_t sInvMaxAlpha = 171; /* (ceil(0x100 / .75) >> 1) */
327 : static const HashNumber sGoldenRatio = 0x9E3779B9U; /* taken from jsdhash.h */
328 : static const HashNumber sFreeKey = Entry::sFreeKey;
329 : static const HashNumber sRemovedKey = Entry::sRemovedKey;
330 : static const HashNumber sCollisionBit = Entry::sCollisionBit;
331 :
332 : static void staticAsserts()
333 : {
334 : /* Rely on compiler "constant overflow warnings". */
335 : JS_STATIC_ASSERT(((sMaxInit * sInvMaxAlpha) >> 7) < sMaxCapacity);
336 : JS_STATIC_ASSERT((sMaxCapacity * sInvMaxAlpha) <= UINT32_MAX);
337 : JS_STATIC_ASSERT((sMaxCapacity * sizeof(Entry)) <= UINT32_MAX);
338 : }
339 :
340 847839671 : static bool isLiveHash(HashNumber hash)
341 : {
342 847839671 : return Entry::isLiveHash(hash);
343 : }
344 :
345 414934332 : static HashNumber prepareHash(const Lookup& l)
346 : {
347 414934332 : HashNumber keyHash = HashPolicy::hash(l);
348 :
349 : /* Improve keyHash distribution. */
350 414934332 : keyHash *= sGoldenRatio;
351 :
352 : /* Avoid reserved hash codes. */
353 414934332 : if (!isLiveHash(keyHash))
354 119573220 : keyHash -= (sRemovedKey + 1);
355 414934332 : return keyHash & ~sCollisionBit;
356 : }
357 :
358 3838054 : static Entry *createTable(AllocPolicy &alloc, uint32_t capacity)
359 : {
360 3838054 : Entry *newTable = (Entry *)alloc.malloc_(capacity * sizeof(Entry));
361 3838054 : if (!newTable)
362 0 : return NULL;
363 537635786 : for (Entry *e = newTable, *end = e + capacity; e < end; ++e)
364 533797732 : new(e) Entry();
365 3838054 : return newTable;
366 : }
367 :
368 3838054 : static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity)
369 : {
370 537635786 : for (Entry *e = oldTable, *end = e + capacity; e < end; ++e)
371 533797732 : e->~Entry();
372 3838054 : alloc.free_(oldTable);
373 3838054 : }
374 :
375 : public:
376 3400418 : HashTable(AllocPolicy ap)
377 : : AllocPolicy(ap),
378 : hashShift(sHashBits),
379 : entryCount(0),
380 : gen(0),
381 : removedCount(0),
382 : table(NULL),
383 : entered(false),
384 3400418 : mutationCount(0)
385 3400418 : {}
386 :
387 3092544 : bool init(uint32_t length)
388 : {
389 : /* Make sure that init isn't called twice. */
390 3092544 : JS_ASSERT(table == NULL);
391 :
392 : /*
393 : * Correct for sMaxAlphaFrac such that the table will not resize
394 : * when adding 'length' entries.
395 : */
396 3092544 : if (length > sMaxInit) {
397 0 : this->reportAllocOverflow();
398 0 : return false;
399 : }
400 3092544 : uint32_t capacity = (length * sInvMaxAlpha) >> 7;
401 :
402 3092544 : if (capacity < sMinSize)
403 708946 : capacity = sMinSize;
404 :
405 : /* FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034). */
406 3092544 : uint32_t roundUp = sMinSize, roundUpLog2 = sMinSizeLog2;
407 15316053 : while (roundUp < capacity) {
408 9130965 : roundUp <<= 1;
409 9130965 : ++roundUpLog2;
410 : }
411 :
412 3092544 : capacity = roundUp;
413 3092544 : JS_ASSERT(capacity <= sMaxCapacity);
414 :
415 3092544 : table = createTable(*this, capacity);
416 3092544 : if (!table)
417 0 : return false;
418 :
419 3092544 : setTableSizeLog2(roundUpLog2);
420 3092544 : METER(memset(&stats, 0, sizeof(stats)));
421 3092544 : return true;
422 : }
423 :
424 54607262 : bool initialized() const
425 : {
426 54607262 : return !!table;
427 : }
428 :
429 3400418 : ~HashTable()
430 : {
431 3400418 : if (table)
432 3070974 : destroyTable(*this, table, capacity());
433 3400418 : }
434 :
435 : private:
436 553180901 : static HashNumber hash1(HashNumber hash0, uint32_t shift) {
437 553180901 : return hash0 >> shift;
438 : }
439 :
440 : struct DoubleHash {
441 : HashNumber h2;
442 : HashNumber sizeMask;
443 : };
444 :
445 117872256 : DoubleHash hash2(HashNumber curKeyHash, uint32_t hashShift) const {
446 117872256 : unsigned sizeLog2 = sHashBits - hashShift;
447 : DoubleHash dh = {
448 : ((curKeyHash << sizeLog2) >> hashShift) | 1,
449 : (HashNumber(1) << sizeLog2) - 1
450 117872256 : };
451 : return dh;
452 : }
453 :
454 253536988 : static HashNumber applyDoubleHash(HashNumber h1, const DoubleHash &dh) {
455 253536988 : return (h1 - dh.h2) & dh.sizeMask;
456 : }
457 :
458 102004229 : bool overloaded() {
459 102004229 : return entryCount + removedCount >= ((sMaxAlphaFrac * capacity()) >> 8);
460 : }
461 :
462 726375 : bool underloaded() {
463 726375 : uint32_t tableCapacity = capacity();
464 : return tableCapacity > sMinSize &&
465 726375 : entryCount <= ((sMinAlphaFrac * tableCapacity) >> 8);
466 : }
467 :
468 107219695 : static bool match(Entry &e, const Lookup &l) {
469 107219695 : return HashPolicy::match(HashPolicy::getKey(e.t), l);
470 : }
471 :
472 432905339 : Entry &lookup(const Lookup &l, HashNumber keyHash, unsigned collisionBit) const
473 : {
474 432905339 : JS_ASSERT(isLiveHash(keyHash));
475 432905339 : JS_ASSERT(!(keyHash & sCollisionBit));
476 432905339 : JS_ASSERT(collisionBit == 0 || collisionBit == sCollisionBit);
477 432905339 : JS_ASSERT(table);
478 432905339 : METER(stats.searches++);
479 :
480 : /* Compute the primary hash address. */
481 432905339 : HashNumber h1 = hash1(keyHash, hashShift);
482 432905339 : Entry *entry = &table[h1];
483 :
484 : /* Miss: return space for a new entry. */
485 432905339 : if (entry->isFree()) {
486 251825808 : METER(stats.misses++);
487 251825808 : return *entry;
488 : }
489 :
490 : /* Hit: return entry. */
491 181079531 : if (entry->matchHash(keyHash) && match(*entry, l)) {
492 78895236 : METER(stats.hits++);
493 78895236 : return *entry;
494 : }
495 :
496 : /* Collision: double hash. */
497 102184295 : DoubleHash dh = hash2(keyHash, hashShift);
498 :
499 : /* Save the first removed entry pointer so we can recycle later. */
500 102184295 : Entry *firstRemoved = NULL;
501 :
502 128953689 : while(true) {
503 231137984 : if (JS_UNLIKELY(entry->isRemoved())) {
504 683365 : if (!firstRemoved)
505 340707 : firstRemoved = entry;
506 : } else {
507 230454619 : entry->setCollision(collisionBit);
508 : }
509 :
510 231137984 : METER(stats.steps++);
511 231137984 : h1 = applyDoubleHash(h1, dh);
512 :
513 231137984 : entry = &table[h1];
514 231137984 : if (entry->isFree()) {
515 76954834 : METER(stats.misses++);
516 76954834 : return firstRemoved ? *firstRemoved : *entry;
517 : }
518 :
519 154183150 : if (entry->matchHash(keyHash) && match(*entry, l)) {
520 25229461 : METER(stats.hits++);
521 25229461 : return *entry;
522 : }
523 : }
524 : }
525 :
526 : /*
527 : * This is a copy of lookup hardcoded to the assumptions:
528 : * 1. the lookup is a lookupForAdd
529 : * 2. the key, whose |keyHash| has been passed is not in the table,
530 : * 3. no entries have been removed from the table.
531 : * This specialized search avoids the need for recovering lookup values
532 : * from entries, which allows more flexible Lookup/Key types.
533 : */
534 120275562 : Entry &findFreeEntry(HashNumber keyHash)
535 : {
536 120275562 : METER(stats.searches++);
537 120275562 : JS_ASSERT(!(keyHash & sCollisionBit));
538 :
539 : /* N.B. the |keyHash| has already been distributed. */
540 :
541 : /* Compute the primary hash address. */
542 120275562 : HashNumber h1 = hash1(keyHash, hashShift);
543 120275562 : Entry *entry = &table[h1];
544 :
545 : /* Miss: return space for a new entry. */
546 120275562 : if (entry->isFree()) {
547 104587601 : METER(stats.misses++);
548 104587601 : return *entry;
549 : }
550 :
551 : /* Collision: double hash. */
552 15687961 : DoubleHash dh = hash2(keyHash, hashShift);
553 :
554 6711043 : while(true) {
555 22399004 : JS_ASSERT(!entry->isRemoved());
556 22399004 : entry->setCollision();
557 :
558 22399004 : METER(stats.steps++);
559 22399004 : h1 = applyDoubleHash(h1, dh);
560 :
561 22399004 : entry = &table[h1];
562 22399004 : if (entry->isFree()) {
563 15687961 : METER(stats.misses++);
564 15687961 : return *entry;
565 : }
566 : }
567 : }
568 :
569 745510 : bool changeTableSize(int deltaLog2)
570 : {
571 : /* Look, but don't touch, until we succeed in getting new entry store. */
572 745510 : Entry *oldTable = table;
573 745510 : uint32_t oldCap = capacity();
574 745510 : uint32_t newLog2 = sHashBits - hashShift + deltaLog2;
575 745510 : uint32_t newCapacity = JS_BIT(newLog2);
576 745510 : if (newCapacity > sMaxCapacity) {
577 0 : this->reportAllocOverflow();
578 0 : return false;
579 : }
580 :
581 745510 : Entry *newTable = createTable(*this, newCapacity);
582 745510 : if (!newTable)
583 0 : return false;
584 :
585 : /* We can't fail from here on, so update table parameters. */
586 745510 : setTableSizeLog2(newLog2);
587 745510 : removedCount = 0;
588 745510 : gen++;
589 745510 : table = newTable;
590 :
591 : /* Copy only live entries, leaving removed ones behind. */
592 220499078 : for (Entry *src = oldTable, *end = src + oldCap; src < end; ++src) {
593 219753568 : if (src->isLive()) {
594 119750190 : src->unsetCollision();
595 119750190 : findFreeEntry(src->getKeyHash()) = Move(*src);
596 : }
597 : }
598 :
599 745510 : destroyTable(*this, oldTable, oldCap);
600 745510 : return true;
601 : }
602 :
603 299938 : void add(const Lookup &l, const Entry &e)
604 : {
605 299938 : HashNumber keyHash = prepareHash(l);
606 299938 : Entry &entry = lookup(l, keyHash, sCollisionBit);
607 :
608 299938 : if (entry.isRemoved()) {
609 83798 : METER(stats.addOverRemoved++);
610 83798 : removedCount--;
611 83798 : keyHash |= sCollisionBit;
612 : }
613 :
614 299938 : entry.t = e.t;
615 299938 : entry.setLive(keyHash);
616 299938 : entryCount++;
617 299938 : mutationCount++;
618 299938 : }
619 :
620 102004229 : bool checkOverloaded()
621 : {
622 102004229 : if (overloaded()) {
623 : /* Compress if a quarter or more of all entries are removed. */
624 : int deltaLog2;
625 525392 : if (removedCount >= (capacity() >> 2)) {
626 100 : METER(stats.compresses++);
627 100 : deltaLog2 = 0;
628 : } else {
629 525292 : METER(stats.grows++);
630 525292 : deltaLog2 = 1;
631 : }
632 :
633 525392 : (void) changeTableSize(deltaLog2);
634 :
635 525392 : return true;
636 : }
637 :
638 101478837 : return false;
639 : }
640 :
641 16248611 : void remove(Entry &e)
642 : {
643 16248611 : METER(stats.removes++);
644 16248611 : if (e.hasCollision()) {
645 3999617 : e.setRemoved();
646 3999617 : removedCount++;
647 : } else {
648 12248994 : METER(stats.removeFrees++);
649 12248994 : e.setFree();
650 : }
651 16248611 : entryCount--;
652 16248611 : mutationCount++;
653 16248611 : }
654 :
655 726375 : void checkUnderloaded()
656 : {
657 726375 : if (underloaded()) {
658 220118 : METER(stats.shrinks++);
659 220118 : (void) changeTableSize(-1);
660 : }
661 726375 : }
662 :
663 : public:
664 99228 : void clear()
665 : {
666 : if (tl::IsPodType<Entry>::result) {
667 19136 : memset(table, 0, sizeof(*table) * capacity());
668 : } else {
669 80092 : uint32_t tableCapacity = capacity();
670 7082556 : for (Entry *e = table, *end = table + tableCapacity; e < end; ++e)
671 7002464 : *e = Move(Entry());
672 : }
673 99228 : removedCount = 0;
674 99228 : entryCount = 0;
675 99228 : mutationCount++;
676 99228 : }
677 :
678 21570 : void finish()
679 : {
680 21570 : JS_ASSERT(!entered);
681 :
682 21570 : if (!table)
683 0 : return;
684 :
685 21570 : destroyTable(*this, table, capacity());
686 21570 : table = NULL;
687 21570 : gen++;
688 21570 : entryCount = 0;
689 21570 : removedCount = 0;
690 21570 : mutationCount++;
691 : }
692 :
693 1069429 : Range all() const {
694 1069429 : return Range(table, table + capacity());
695 : }
696 :
697 28267042 : bool empty() const {
698 28267042 : return !entryCount;
699 : }
700 :
701 198030 : uint32_t count() const {
702 198030 : return entryCount;
703 : }
704 :
705 108262707 : uint32_t capacity() const {
706 108262707 : return JS_BIT(sHashBits - hashShift);
707 : }
708 :
709 6950010 : uint32_t generation() const {
710 6950010 : return gen;
711 : }
712 :
713 0 : size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
714 0 : return mallocSizeOf(table);
715 : }
716 :
717 : size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
718 : return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
719 : }
720 :
721 234055447 : Ptr lookup(const Lookup &l) const {
722 468110894 : ReentrancyGuard g(*this);
723 234055447 : HashNumber keyHash = prepareHash(l);
724 234055447 : return Ptr(lookup(l, keyHash, 0));
725 : }
726 :
727 180578947 : AddPtr lookupForAdd(const Lookup &l) const {
728 361157894 : ReentrancyGuard g(*this);
729 180578947 : HashNumber keyHash = prepareHash(l);
730 180578947 : Entry &entry = lookup(l, keyHash, sCollisionBit);
731 180578947 : AddPtr p(entry, keyHash);
732 180578947 : p.mutationCount = mutationCount;
733 : return p;
734 : }
735 :
736 102096012 : bool add(AddPtr &p)
737 : {
738 204192024 : ReentrancyGuard g(*this);
739 102096012 : JS_ASSERT(mutationCount == p.mutationCount);
740 102096012 : JS_ASSERT(table);
741 102096012 : JS_ASSERT(!p.found());
742 102096012 : JS_ASSERT(!(p.keyHash & sCollisionBit));
743 :
744 : /*
745 : * Changing an entry from removed to live does not affect whether we
746 : * are overloaded and can be handled separately.
747 : */
748 102096012 : if (p.entry->isRemoved()) {
749 91823 : METER(stats.addOverRemoved++);
750 91823 : removedCount--;
751 91823 : p.keyHash |= sCollisionBit;
752 : } else {
753 102004189 : if (checkOverloaded())
754 : /* Preserve the validity of |p.entry|. */
755 525372 : p.entry = &findFreeEntry(p.keyHash);
756 : }
757 :
758 102096012 : p.entry->setLive(p.keyHash);
759 102096012 : entryCount++;
760 102096012 : mutationCount++;
761 102096012 : return true;
762 : }
763 :
764 : /*
765 : * There is an important contract between the caller and callee for this
766 : * function: if add() returns true, the caller must assign the T value
767 : * which produced p before using the hashtable again.
768 : */
769 17970098 : bool add(AddPtr &p, T** pentry)
770 : {
771 17970098 : if (!add(p))
772 0 : return false;
773 17970098 : *pentry = &p.entry->t;
774 17970098 : return true;
775 : }
776 :
777 84125914 : bool add(AddPtr &p, const T &t)
778 : {
779 84125914 : if (!add(p))
780 0 : return false;
781 84125914 : p.entry->t = t;
782 84125914 : return true;
783 : }
784 :
785 17971007 : bool relookupOrAdd(AddPtr& p, const Lookup &l, const T& t)
786 : {
787 17971007 : p.mutationCount = mutationCount;
788 : {
789 35942014 : ReentrancyGuard g(*this);
790 17971007 : p.entry = &lookup(l, p.keyHash, sCollisionBit);
791 : }
792 17971007 : return p.found() || add(p, t);
793 : }
794 :
795 561542 : void remove(Ptr p)
796 : {
797 1123084 : ReentrancyGuard g(*this);
798 561542 : JS_ASSERT(p.found());
799 561542 : remove(*p.entry);
800 561542 : checkUnderloaded();
801 561542 : }
802 :
803 : #undef METER
804 : };
805 :
806 : } /* namespace detail */
807 :
808 : /*****************************************************************************/
809 :
810 : /*
811 : * Hash policy
812 : *
813 : * A hash policy P for a hash table with key-type Key must provide:
814 : * - a type |P::Lookup| to use to lookup table entries;
815 : * - a static member function |P::hash| with signature
816 : *
817 : * static js::HashNumber hash(Lookup)
818 : *
819 : * to use to hash the lookup type; and
820 : * - a static member function |P::match| with signature
821 : *
822 : * static bool match(Key, Lookup)
823 : *
824 : * to use to test equality of key and lookup values.
825 : *
826 : * Normally, Lookup = Key. In general, though, different values and types of
827 : * values can be used to lookup and store. If a Lookup value |l| is != to the
828 : * added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.:
829 : *
830 : * js::HashSet<Key, P>::AddPtr p = h.lookup(l);
831 : * if (!p) {
832 : * assert(P::match(k, l)); // must hold
833 : * h.add(p, k);
834 : * }
835 : */
836 :
837 : /* Default hashing policies. */
838 : template <class Key>
839 : struct DefaultHasher
840 : {
841 : typedef Key Lookup;
842 4233642 : static HashNumber hash(const Lookup &l) {
843 : /* Hash if can implicitly cast to hash number type. */
844 4233642 : return l;
845 : }
846 1733798 : static bool match(const Key &k, const Lookup &l) {
847 : /* Use builtin or overloaded operator==. */
848 1733798 : return k == l;
849 : }
850 : };
851 :
852 : /*
853 : * Pointer hashing policy that strips the lowest zeroBits when calculating the
854 : * hash to improve key distribution.
855 : */
856 : template <typename Key, size_t zeroBits>
857 : struct PointerHasher
858 : {
859 : typedef Key Lookup;
860 59806738 : static HashNumber hash(const Lookup &l) {
861 59806738 : size_t word = reinterpret_cast<size_t>(l) >> zeroBits;
862 : JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
863 : #if JS_BYTES_PER_WORD == 4
864 59806738 : return HashNumber(word);
865 : #else
866 : JS_STATIC_ASSERT(sizeof word == 8);
867 : return HashNumber((word >> 32) ^ word);
868 : #endif
869 : }
870 16458018 : static bool match(const Key &k, const Lookup &l) {
871 16458018 : return k == l;
872 : }
873 : };
874 :
875 : template <typename Key, size_t zeroBits>
876 : struct TaggedPointerHasher
877 : {
878 : typedef Key Lookup;
879 :
880 : static HashNumber hash(const Lookup &l) {
881 : return PointerHasher<Key, zeroBits>::hash(l);
882 : }
883 :
884 : static const uintptr_t COMPARE_MASK = uintptr_t(-1) - 1;
885 :
886 : static bool match(const Key &k, const Lookup &l) {
887 : return (uintptr_t(k) & COMPARE_MASK) == uintptr_t(l);
888 : }
889 : };
890 :
891 : /*
892 : * Specialized hashing policy for pointer types. It assumes that the type is
893 : * at least word-aligned. For types with smaller size use PointerHasher.
894 : */
895 : template <class T>
896 : struct DefaultHasher<T *>: PointerHasher<T *, tl::FloorLog2<sizeof(void *)>::result> { };
897 :
898 : /* Looking for a hasher for jsid? Try the DefaultHasher<jsid> in jsatom.h. */
899 :
900 : template <class Key, class Value>
901 : class HashMapEntry
902 20774816 : {
903 : template <class, class, class> friend class detail::HashTable;
904 : template <class> friend class detail::HashTableEntry;
905 810394 : void operator=(const HashMapEntry &rhs) {
906 810394 : const_cast<Key &>(key) = rhs.key;
907 810394 : value = rhs.value;
908 810394 : }
909 :
910 : public:
911 122223664 : HashMapEntry() : key(), value() {}
912 :
913 : template<typename KeyInput, typename ValueInput>
914 39665 : HashMapEntry(const KeyInput &k, const ValueInput &v) : key(k), value(v) {}
915 :
916 : HashMapEntry(MoveRef<HashMapEntry> rhs)
917 : : key(Move(rhs->key)), value(Move(rhs->value)) { }
918 34136545 : void operator=(MoveRef<HashMapEntry> rhs) {
919 34136545 : const_cast<Key &>(key) = Move(rhs->key);
920 34136545 : value = Move(rhs->value);
921 34136545 : }
922 :
923 : const Key key;
924 : Value value;
925 : };
926 :
927 : namespace tl {
928 :
929 : template <class T>
930 : struct IsPodType<detail::HashTableEntry<T> > {
931 : static const bool result = IsPodType<T>::result;
932 : };
933 :
934 : template <class K, class V>
935 : struct IsPodType<HashMapEntry<K, V> >
936 : {
937 : static const bool result = IsPodType<K>::result && IsPodType<V>::result;
938 : };
939 :
940 : } /* namespace tl */
941 :
942 : /*
943 : * JS-friendly, STL-like container providing a hash-based map from keys to
944 : * values. In particular, HashMap calls constructors and destructors of all
945 : * objects added so non-PODs may be used safely.
946 : *
947 : * Key/Value requirements:
948 : * - default constructible, copyable, destructible, assignable
949 : * HashPolicy requirements:
950 : * - see "Hash policy" above (default js::DefaultHasher<Key>)
951 : * AllocPolicy:
952 : * - see "Allocation policies" in jsalloc.h
953 : *
954 : * N.B: HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members
955 : * called by HashMap must not call back into the same HashMap object.
956 : * N.B: Due to the lack of exception handling, the user must call |init()|.
957 : */
958 : template <class Key,
959 : class Value,
960 : class HashPolicy = DefaultHasher<Key>,
961 : class AllocPolicy = TempAllocPolicy>
962 : class HashMap
963 671751 : {
964 : public:
965 : typedef typename HashPolicy::Lookup Lookup;
966 :
967 : typedef HashMapEntry<Key, Value> Entry;
968 :
969 : private:
970 : /* Implement HashMap using HashTable. Lift |Key| operations to |Entry|. */
971 : struct MapHashPolicy : HashPolicy
972 : {
973 : typedef Key KeyType;
974 19061329 : static const Key &getKey(Entry &e) { return e.key; }
975 149969 : static void setKey(Entry &e, Key &k) { const_cast<Key &>(e.key) = k; }
976 : };
977 : typedef detail::HashTable<Entry, MapHashPolicy, AllocPolicy> Impl;
978 :
979 : friend class Impl::Enum;
980 :
981 : /* Not implicitly copyable (expensive). May add explicit |clone| later. */
982 : HashMap(const HashMap &);
983 : HashMap &operator=(const HashMap &);
984 :
985 : Impl impl;
986 :
987 : public:
988 : const static unsigned sDefaultInitSize = Impl::sDefaultInitSize;
989 :
990 : /*
991 : * HashMap construction is fallible (due to OOM); thus the user must call
992 : * init after constructing a HashMap and check the return value.
993 : */
994 671751 : HashMap(AllocPolicy a = AllocPolicy()) : impl(a) {}
995 449134 : bool init(uint32_t len = sDefaultInitSize) { return impl.init(len); }
996 6120116 : bool initialized() const { return impl.initialized(); }
997 :
998 : /*
999 : * Return whether the given lookup value is present in the map. E.g.:
1000 : *
1001 : * typedef HashMap<int,char> HM;
1002 : * HM h;
1003 : * if (HM::Ptr p = h.lookup(3)) {
1004 : * const HM::Entry &e = *p; // p acts like a pointer to Entry
1005 : * assert(p->key == 3); // Entry contains the key
1006 : * char val = p->value; // and value
1007 : * }
1008 : *
1009 : * Also see the definition of Ptr in HashTable above (with T = Entry).
1010 : */
1011 : typedef typename Impl::Ptr Ptr;
1012 15394491 : Ptr lookup(const Lookup &l) const { return impl.lookup(l); }
1013 :
1014 : /* Assuming |p.found()|, remove |*p|. */
1015 337152 : void remove(Ptr p) { impl.remove(p); }
1016 :
1017 : /*
1018 : * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
1019 : * insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using
1020 : * |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.:
1021 : *
1022 : * typedef HashMap<int,char> HM;
1023 : * HM h;
1024 : * HM::AddPtr p = h.lookupForAdd(3);
1025 : * if (!p) {
1026 : * if (!h.add(p, 3, 'a'))
1027 : * return false;
1028 : * }
1029 : * const HM::Entry &e = *p; // p acts like a pointer to Entry
1030 : * assert(p->key == 3); // Entry contains the key
1031 : * char val = p->value; // and value
1032 : *
1033 : * Also see the definition of AddPtr in HashTable above (with T = Entry).
1034 : *
1035 : * N.B. The caller must ensure that no mutating hash table operations
1036 : * occur between a pair of |lookupForAdd| and |add| calls. To avoid
1037 : * looking up the key a second time, the caller may use the more efficient
1038 : * relookupOrAdd method. This method reuses part of the hashing computation
1039 : * to more efficiently insert the key if it has not been added. For
1040 : * example, a mutation-handling version of the previous example:
1041 : *
1042 : * HM::AddPtr p = h.lookupForAdd(3);
1043 : * if (!p) {
1044 : * call_that_may_mutate_h();
1045 : * if (!h.relookupOrAdd(p, 3, 'a'))
1046 : * return false;
1047 : * }
1048 : * const HM::Entry &e = *p;
1049 : * assert(p->key == 3);
1050 : * char val = p->value;
1051 : */
1052 : typedef typename Impl::AddPtr AddPtr;
1053 26650361 : AddPtr lookupForAdd(const Lookup &l) const {
1054 26650361 : return impl.lookupForAdd(l);
1055 : }
1056 :
1057 : template<typename KeyInput, typename ValueInput>
1058 14623946 : bool add(AddPtr &p, const KeyInput &k, const ValueInput &v) {
1059 : Entry *pentry;
1060 14623946 : if (!impl.add(p, &pentry))
1061 0 : return false;
1062 14623946 : const_cast<Key &>(pentry->key) = k;
1063 14623946 : pentry->value = v;
1064 14623946 : return true;
1065 : }
1066 :
1067 3346152 : bool add(AddPtr &p, const Key &k, MoveRef<Value> v) {
1068 : Entry *pentry;
1069 3346152 : if (!impl.add(p, &pentry))
1070 0 : return false;
1071 3346152 : const_cast<Key &>(pentry->key) = k;
1072 3346152 : pentry->value = v;
1073 3346152 : return true;
1074 : }
1075 :
1076 : bool add(AddPtr &p, const Key &k) {
1077 : Entry *pentry;
1078 : if (!impl.add(p, &pentry))
1079 : return false;
1080 : const_cast<Key &>(pentry->key) = k;
1081 : return true;
1082 : }
1083 :
1084 : template<typename KeyInput, typename ValueInput>
1085 39665 : bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) {
1086 39665 : return impl.relookupOrAdd(p, k, Entry(k, v));
1087 : }
1088 :
1089 : /*
1090 : * |all()| returns a Range containing |count()| elements. E.g.:
1091 : *
1092 : * typedef HashMap<int,char> HM;
1093 : * HM h;
1094 : * for (HM::Range r = h.all(); !r.empty(); r.popFront())
1095 : * char c = r.front().value;
1096 : *
1097 : * Also see the definition of Range in HashTable above (with T = Entry).
1098 : */
1099 : typedef typename Impl::Range Range;
1100 332554 : Range all() const { return impl.all(); }
1101 73144 : uint32_t count() const { return impl.count(); }
1102 : size_t capacity() const { return impl.capacity(); }
1103 : size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
1104 : return impl.sizeOfExcludingThis(mallocSizeOf);
1105 : }
1106 0 : size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
1107 : /*
1108 : * Don't just call |impl.sizeOfExcludingThis()| because there's no
1109 : * guarantee that |impl| is the first field in HashMap.
1110 : */
1111 0 : return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
1112 : }
1113 :
1114 : /*
1115 : * Typedef for the enumeration class. An Enum may be used to examine and
1116 : * remove table entries:
1117 : *
1118 : * typedef HashMap<int,char> HM;
1119 : * HM s;
1120 : * for (HM::Enum e(s); !e.empty(); e.popFront())
1121 : * if (e.front().value == 'l')
1122 : * e.removeFront();
1123 : *
1124 : * Table resize may occur in Enum's destructor. Also see the definition of
1125 : * Enum in HashTable above (with T = Entry).
1126 : */
1127 : typedef typename Impl::Enum Enum;
1128 :
1129 : /*
1130 : * Remove all entries. This does not shrink the table. For that consider
1131 : * using the finish() method.
1132 : */
1133 39122 : void clear() { impl.clear(); }
1134 :
1135 : /*
1136 : * Remove all the entries and release all internal buffers. The map must
1137 : * be initialized again before any use.
1138 : */
1139 21570 : void finish() { impl.finish(); }
1140 :
1141 : /* Does the table contain any entries? */
1142 60172 : bool empty() const { return impl.empty(); }
1143 :
1144 : /*
1145 : * If |generation()| is the same before and after a HashMap operation,
1146 : * pointers into the table remain valid.
1147 : */
1148 6699954 : unsigned generation() const { return impl.generation(); }
1149 :
1150 : /* Shorthand operations: */
1151 :
1152 1266904 : bool has(const Lookup &l) const {
1153 1266904 : return impl.lookup(l) != NULL;
1154 : }
1155 :
1156 : /* Overwrite existing value with v. Return false on oom. */
1157 : template<typename KeyInput, typename ValueInput>
1158 1781288 : bool put(const KeyInput &k, const ValueInput &v) {
1159 3562576 : AddPtr p = lookupForAdd(k);
1160 1781288 : if (p) {
1161 570 : p->value = v;
1162 570 : return true;
1163 : }
1164 1780718 : return add(p, k, v);
1165 : }
1166 :
1167 : /* Like put, but assert that the given key is not already present. */
1168 732807 : bool putNew(const Key &k, const Value &v) {
1169 1465614 : AddPtr p = lookupForAdd(k);
1170 732807 : JS_ASSERT(!p);
1171 732807 : return add(p, k, v);
1172 : }
1173 :
1174 : /* Add (k,defaultValue) if k no found. Return false-y Ptr on oom. */
1175 0 : Ptr lookupWithDefault(const Key &k, const Value &defaultValue) {
1176 0 : AddPtr p = lookupForAdd(k);
1177 0 : if (p)
1178 0 : return p;
1179 0 : (void)add(p, k, defaultValue); /* p is left false-y on oom. */
1180 0 : return p;
1181 : }
1182 :
1183 : /* Remove if present. */
1184 33095 : void remove(const Lookup &l) {
1185 33095 : if (Ptr p = lookup(l))
1186 33095 : remove(p);
1187 33095 : }
1188 : };
1189 :
1190 : /*
1191 : * JS-friendly, STL-like container providing a hash-based set of values. In
1192 : * particular, HashSet calls constructors and destructors of all objects added
1193 : * so non-PODs may be used safely.
1194 : *
1195 : * T requirements:
1196 : * - default constructible, copyable, destructible, assignable
1197 : * HashPolicy requirements:
1198 : * - see "Hash policy" above (default js::DefaultHasher<Key>)
1199 : * AllocPolicy:
1200 : * - see "Allocation policies" in jsalloc.h
1201 : *
1202 : * N.B: HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by
1203 : * HashSet must not call back into the same HashSet object.
1204 : * N.B: Due to the lack of exception handling, the user must call |init()|.
1205 : */
1206 : template <class T, class HashPolicy = DefaultHasher<T>, class AllocPolicy = TempAllocPolicy>
1207 : class HashSet
1208 2728667 : {
1209 : typedef typename HashPolicy::Lookup Lookup;
1210 :
1211 : /* Implement HashSet in terms of HashTable. */
1212 : struct SetOps : HashPolicy {
1213 : typedef T KeyType;
1214 88458304 : static const KeyType &getKey(const T &t) { return t; }
1215 149969 : static void setKey(T &t, KeyType &k) { t = k; }
1216 : };
1217 : typedef detail::HashTable<const T, SetOps, AllocPolicy> Impl;
1218 :
1219 : friend class Impl::Enum;
1220 :
1221 : /* Not implicitly copyable (expensive). May add explicit |clone| later. */
1222 : HashSet(const HashSet &);
1223 : HashSet &operator=(const HashSet &);
1224 :
1225 : Impl impl;
1226 :
1227 : public:
1228 : const static unsigned sDefaultInitSize = Impl::sDefaultInitSize;
1229 :
1230 : /*
1231 : * HashSet construction is fallible (due to OOM); thus the user must call
1232 : * init after constructing a HashSet and check the return value.
1233 : */
1234 2728667 : HashSet(AllocPolicy a = AllocPolicy()) : impl(a) {}
1235 2643410 : bool init(uint32_t len = sDefaultInitSize) { return impl.init(len); }
1236 48487146 : bool initialized() const { return impl.initialized(); }
1237 :
1238 : /*
1239 : * Return whether the given lookup value is present in the map. E.g.:
1240 : *
1241 : * typedef HashSet<int> HS;
1242 : * HS h;
1243 : * if (HS::Ptr p = h.lookup(3)) {
1244 : * assert(*p == 3); // p acts like a pointer to int
1245 : * }
1246 : *
1247 : * Also see the definition of Ptr in HashTable above.
1248 : */
1249 : typedef typename Impl::Ptr Ptr;
1250 8809360 : Ptr lookup(const Lookup &l) const { return impl.lookup(l); }
1251 :
1252 : /* Assuming |p.found()|, remove |*p|. */
1253 224390 : void remove(Ptr p) { impl.remove(p); }
1254 :
1255 : /*
1256 : * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
1257 : * insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using
1258 : * |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.:
1259 : *
1260 : * typedef HashSet<int> HS;
1261 : * HS h;
1262 : * HS::AddPtr p = h.lookupForAdd(3);
1263 : * if (!p) {
1264 : * if (!h.add(p, 3))
1265 : * return false;
1266 : * }
1267 : * assert(*p == 3); // p acts like a pointer to int
1268 : *
1269 : * Also see the definition of AddPtr in HashTable above.
1270 : *
1271 : * N.B. The caller must ensure that no mutating hash table operations
1272 : * occur between a pair of |lookupForAdd| and |add| calls. To avoid
1273 : * looking up the key a second time, the caller may use the more efficient
1274 : * relookupOrAdd method. This method reuses part of the hashing computation
1275 : * to more efficiently insert the key if it has not been added. For
1276 : * example, a mutation-handling version of the previous example:
1277 : *
1278 : * HS::AddPtr p = h.lookupForAdd(3);
1279 : * if (!p) {
1280 : * call_that_may_mutate_h();
1281 : * if (!h.relookupOrAdd(p, 3, 3))
1282 : * return false;
1283 : * }
1284 : * assert(*p == 3);
1285 : *
1286 : * Note that relookupOrAdd(p,l,t) performs Lookup using l and adds the
1287 : * entry t, where the caller ensures match(l,t).
1288 : */
1289 : typedef typename Impl::AddPtr AddPtr;
1290 153928586 : AddPtr lookupForAdd(const Lookup &l) const {
1291 153928586 : return impl.lookupForAdd(l);
1292 : }
1293 :
1294 66154907 : bool add(AddPtr &p, const T &t) {
1295 66154907 : return impl.add(p, t);
1296 : }
1297 :
1298 17931342 : bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) {
1299 17931342 : return impl.relookupOrAdd(p, l, t);
1300 : }
1301 :
1302 : /*
1303 : * |all()| returns a Range containing |count()| elements:
1304 : *
1305 : * typedef HashSet<int> HS;
1306 : * HS h;
1307 : * for (HS::Range r = h.all(); !r.empty(); r.popFront())
1308 : * int i = r.front();
1309 : *
1310 : * Also see the definition of Range in HashTable above.
1311 : */
1312 : typedef typename Impl::Range Range;
1313 736875 : Range all() const { return impl.all(); }
1314 124886 : uint32_t count() const { return impl.count(); }
1315 : size_t capacity() const { return impl.capacity(); }
1316 0 : size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
1317 0 : return impl.sizeOfExcludingThis(mallocSizeOf);
1318 : }
1319 : size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
1320 : /*
1321 : * Don't just call |impl.sizeOfExcludingThis()| because there's no
1322 : * guarantee that |impl| is the first field in HashSet.
1323 : */
1324 : return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
1325 : }
1326 :
1327 : /*
1328 : * Typedef for the enumeration class. An Enum may be used to examine and
1329 : * remove table entries:
1330 : *
1331 : * typedef HashSet<int> HS;
1332 : * HS s;
1333 : * for (HS::Enum e(s); !e.empty(); e.popFront())
1334 : * if (e.front() == 42)
1335 : * e.removeFront();
1336 : *
1337 : * Table resize may occur in Enum's destructor. Also see the definition of
1338 : * Enum in HashTable above.
1339 : */
1340 : typedef typename Impl::Enum Enum;
1341 :
1342 : /*
1343 : * Remove all entries. This does not shrink the table. For that consider
1344 : * using the finish() method.
1345 : */
1346 60106 : void clear() { impl.clear(); }
1347 :
1348 : /*
1349 : * Remove all the entries and release all internal buffers. The set must
1350 : * be initialized again before any use.
1351 : */
1352 0 : void finish() { impl.finish(); }
1353 :
1354 : /* Does the table contain any entries? */
1355 28206870 : bool empty() const { return impl.empty(); }
1356 :
1357 : /*
1358 : * If |generation()| is the same before and after a HashSet operation,
1359 : * pointers into the table remain valid.
1360 : */
1361 250056 : unsigned generation() const { return impl.generation(); }
1362 :
1363 : /* Shorthand operations: */
1364 :
1365 208584692 : bool has(const Lookup &l) const {
1366 208584692 : return impl.lookup(l) != NULL;
1367 : }
1368 :
1369 : /* Overwrite existing value with v. Return false on oom. */
1370 617771 : bool put(const T &t) {
1371 1235542 : AddPtr p = lookupForAdd(t);
1372 617771 : return p ? true : add(p, t);
1373 : }
1374 :
1375 : /* Like put, but assert that the given key is not already present. */
1376 699948 : bool putNew(const T &t) {
1377 1399896 : AddPtr p = lookupForAdd(t);
1378 699948 : JS_ASSERT(!p);
1379 699948 : return add(p, t);
1380 : }
1381 :
1382 1758231 : bool putNew(const Lookup &l, const T &t) {
1383 3516462 : AddPtr p = lookupForAdd(l);
1384 1758231 : JS_ASSERT(!p);
1385 1758231 : return add(p, t);
1386 : }
1387 :
1388 85417 : void remove(const Lookup &l) {
1389 85417 : if (Ptr p = lookup(l))
1390 85417 : remove(p);
1391 85417 : }
1392 : };
1393 :
1394 : } /* namespace js */
1395 :
1396 : #endif
|