OLD | NEW |
1 /* | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights | 2 // Use of this source code is governed by a BSD-style license that can be |
3 * reserved. | 3 // found in the LICENSE file. |
4 * Copyright (C) 2008 David Levin <levin@chromium.org> | |
5 * | |
6 * This library is free software; you can redistribute it and/or | |
7 * modify it under the terms of the GNU Library General Public | |
8 * License as published by the Free Software Foundation; either | |
9 * version 2 of the License, or (at your option) any later version. | |
10 * | |
11 * This library is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library | |
14 * General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU Library General Public License | |
17 * along with this library; see the file COPYING.LIB. If not, write to | |
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
19 * Boston, MA 02110-1301, USA. | |
20 * | |
21 */ | |
22 | 4 |
23 #ifndef WTF_HashTable_h | 5 #include "platform/wtf/HashTable.h" |
24 #define WTF_HashTable_h | |
25 | 6 |
26 #include "wtf/Alignment.h" | 7 // The contents of this header was moved to platform/wtf as part of |
27 #include "wtf/Allocator.h" | 8 // WTF migration project. See the following post for details: |
28 #include "wtf/Assertions.h" | 9 // https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gY
CAAJ |
29 #include "wtf/ConditionalDestructor.h" | |
30 #include "wtf/HashTraits.h" | |
31 #include "wtf/PtrUtil.h" | |
32 #include "wtf/allocator/PartitionAllocator.h" | |
33 #include <memory> | |
34 | |
35 #define DUMP_HASHTABLE_STATS 0 | |
36 #define DUMP_HASHTABLE_STATS_PER_TABLE 0 | |
37 | |
38 #if DUMP_HASHTABLE_STATS | |
39 #include "wtf/Atomics.h" | |
40 #include "wtf/Threading.h" | |
41 #endif | |
42 | |
43 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
44 #include "wtf/DataLog.h" | |
45 #include <type_traits> | |
46 #endif | |
47 | |
48 #if DUMP_HASHTABLE_STATS | |
49 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
50 | |
51 #define UPDATE_PROBE_COUNTS() \ | |
52 ++probeCount; \ | |
53 HashTableStats::instance().recordCollisionAtCount(probeCount); \ | |
54 ++perTableProbeCount; \ | |
55 m_stats->recordCollisionAtCount(perTableProbeCount) | |
56 #define UPDATE_ACCESS_COUNTS() \ | |
57 atomicIncrement(&HashTableStats::instance().numAccesses); \ | |
58 int probeCount = 0; \ | |
59 ++m_stats->numAccesses; \ | |
60 int perTableProbeCount = 0 | |
61 #else | |
62 #define UPDATE_PROBE_COUNTS() \ | |
63 ++probeCount; \ | |
64 HashTableStats::instance().recordCollisionAtCount(probeCount) | |
65 #define UPDATE_ACCESS_COUNTS() \ | |
66 atomicIncrement(&HashTableStats::instance().numAccesses); \ | |
67 int probeCount = 0 | |
68 #endif | |
69 #else | |
70 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
71 #define UPDATE_PROBE_COUNTS() \ | |
72 ++perTableProbeCount; \ | |
73 m_stats->recordCollisionAtCount(perTableProbeCount) | |
74 #define UPDATE_ACCESS_COUNTS() \ | |
75 ++m_stats->numAccesses; \ | |
76 int perTableProbeCount = 0 | |
77 #else | |
78 #define UPDATE_PROBE_COUNTS() \ | |
79 do { \ | |
80 } while (0) | |
81 #define UPDATE_ACCESS_COUNTS() \ | |
82 do { \ | |
83 } while (0) | |
84 #endif | |
85 #endif | |
86 | |
87 namespace WTF { | |
88 | |
89 // This is for tracing inside collections that have special support for weak | |
90 // pointers. The trait has a trace method which returns true if there are weak | |
91 // pointers to things that have not (yet) been marked live. Returning true | |
92 // indicates that the entry in the collection may yet be removed by weak | |
93 // handling. Default implementation for non-weak types is to use the regular | |
94 // non-weak TraceTrait. Default implementation for types with weakness is to | |
95 // call traceInCollection on the type's trait. | |
96 template <WeakHandlingFlag weakHandlingFlag, | |
97 ShouldWeakPointersBeMarkedStrongly strongify, | |
98 typename T, | |
99 typename Traits> | |
100 struct TraceInCollectionTrait; | |
101 | |
102 #if DUMP_HASHTABLE_STATS | |
103 struct WTF_EXPORT HashTableStats { | |
104 HashTableStats() | |
105 : numAccesses(0), | |
106 numRehashes(0), | |
107 numRemoves(0), | |
108 numReinserts(0), | |
109 maxCollisions(0), | |
110 numCollisions(0), | |
111 collisionGraph() {} | |
112 | |
113 // The following variables are all atomically incremented when modified. | |
114 int numAccesses; | |
115 int numRehashes; | |
116 int numRemoves; | |
117 int numReinserts; | |
118 | |
119 // The following variables are only modified in the recordCollisionAtCount | |
120 // method within a mutex. | |
121 int maxCollisions; | |
122 int numCollisions; | |
123 int collisionGraph[4096]; | |
124 | |
125 void copy(const HashTableStats* other); | |
126 void recordCollisionAtCount(int count); | |
127 void dumpStats(); | |
128 | |
129 static HashTableStats& instance(); | |
130 | |
131 template <typename VisitorDispatcher> | |
132 void trace(VisitorDispatcher) {} | |
133 }; | |
134 | |
135 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
136 template <typename Allocator, bool isGCType = Allocator::isGarbageCollected> | |
137 class HashTableStatsPtr; | |
138 | |
139 template <typename Allocator> | |
140 class HashTableStatsPtr<Allocator, false> final { | |
141 STATIC_ONLY(HashTableStatsPtr); | |
142 | |
143 public: | |
144 static std::unique_ptr<HashTableStats> create() { | |
145 return WTF::wrapUnique(new HashTableStats); | |
146 } | |
147 | |
148 static std::unique_ptr<HashTableStats> copy( | |
149 const std::unique_ptr<HashTableStats>& other) { | |
150 if (!other) | |
151 return nullptr; | |
152 return WTF::wrapUnique(new HashTableStats(*other)); | |
153 } | |
154 | |
155 static void swap(std::unique_ptr<HashTableStats>& stats, | |
156 std::unique_ptr<HashTableStats>& other) { | |
157 stats.swap(other); | |
158 } | |
159 }; | |
160 | |
161 template <typename Allocator> | |
162 class HashTableStatsPtr<Allocator, true> final { | |
163 STATIC_ONLY(HashTableStatsPtr); | |
164 | |
165 public: | |
166 static HashTableStats* create() { | |
167 // Resort to manually allocating this POD on the vector | |
168 // backing heap, as blink::GarbageCollected<> isn't in scope | |
169 // in WTF. | |
170 void* storage = reinterpret_cast<void*>( | |
171 Allocator::template allocateVectorBacking<unsigned char>( | |
172 sizeof(HashTableStats))); | |
173 return new (storage) HashTableStats; | |
174 } | |
175 | |
176 static HashTableStats* copy(const HashTableStats* other) { | |
177 if (!other) | |
178 return nullptr; | |
179 HashTableStats* obj = create(); | |
180 obj->copy(other); | |
181 return obj; | |
182 } | |
183 | |
184 static void swap(HashTableStats*& stats, HashTableStats*& other) { | |
185 std::swap(stats, other); | |
186 } | |
187 }; | |
188 #endif | |
189 #endif | |
190 | |
191 template <typename Key, | |
192 typename Value, | |
193 typename Extractor, | |
194 typename HashFunctions, | |
195 typename Traits, | |
196 typename KeyTraits, | |
197 typename Allocator> | |
198 class HashTable; | |
199 template <typename Key, | |
200 typename Value, | |
201 typename Extractor, | |
202 typename HashFunctions, | |
203 typename Traits, | |
204 typename KeyTraits, | |
205 typename Allocator> | |
206 class HashTableIterator; | |
207 template <typename Key, | |
208 typename Value, | |
209 typename Extractor, | |
210 typename HashFunctions, | |
211 typename Traits, | |
212 typename KeyTraits, | |
213 typename Allocator> | |
214 class HashTableConstIterator; | |
215 template <typename Value, | |
216 typename HashFunctions, | |
217 typename HashTraits, | |
218 typename Allocator> | |
219 class LinkedHashSet; | |
220 template <WeakHandlingFlag x, | |
221 typename T, | |
222 typename U, | |
223 typename V, | |
224 typename W, | |
225 typename X, | |
226 typename Y, | |
227 typename Z> | |
228 struct WeakProcessingHashTableHelper; | |
229 | |
230 typedef enum { HashItemKnownGood } HashItemKnownGoodTag; | |
231 | |
232 template <typename Key, | |
233 typename Value, | |
234 typename Extractor, | |
235 typename HashFunctions, | |
236 typename Traits, | |
237 typename KeyTraits, | |
238 typename Allocator> | |
239 class HashTableConstIterator final { | |
240 DISALLOW_NEW(); | |
241 | |
242 private: | |
243 typedef HashTable<Key, | |
244 Value, | |
245 Extractor, | |
246 HashFunctions, | |
247 Traits, | |
248 KeyTraits, | |
249 Allocator> | |
250 HashTableType; | |
251 typedef HashTableIterator<Key, | |
252 Value, | |
253 Extractor, | |
254 HashFunctions, | |
255 Traits, | |
256 KeyTraits, | |
257 Allocator> | |
258 iterator; | |
259 typedef HashTableConstIterator<Key, | |
260 Value, | |
261 Extractor, | |
262 HashFunctions, | |
263 Traits, | |
264 KeyTraits, | |
265 Allocator> | |
266 const_iterator; | |
267 typedef Value ValueType; | |
268 using value_type = ValueType; | |
269 typedef typename Traits::IteratorConstGetType GetType; | |
270 typedef const ValueType* PointerType; | |
271 | |
272 friend class HashTable<Key, | |
273 Value, | |
274 Extractor, | |
275 HashFunctions, | |
276 Traits, | |
277 KeyTraits, | |
278 Allocator>; | |
279 friend class HashTableIterator<Key, | |
280 Value, | |
281 Extractor, | |
282 HashFunctions, | |
283 Traits, | |
284 KeyTraits, | |
285 Allocator>; | |
286 | |
287 void skipEmptyBuckets() { | |
288 while (m_position != m_endPosition && | |
289 HashTableType::isEmptyOrDeletedBucket(*m_position)) | |
290 ++m_position; | |
291 } | |
292 | |
293 HashTableConstIterator(PointerType position, | |
294 PointerType endPosition, | |
295 const HashTableType* container) | |
296 : m_position(position), | |
297 m_endPosition(endPosition) | |
298 #if DCHECK_IS_ON() | |
299 , | |
300 m_container(container), | |
301 m_containerModifications(container->modifications()) | |
302 #endif | |
303 { | |
304 skipEmptyBuckets(); | |
305 } | |
306 | |
307 HashTableConstIterator(PointerType position, | |
308 PointerType endPosition, | |
309 const HashTableType* container, | |
310 HashItemKnownGoodTag) | |
311 : m_position(position), | |
312 m_endPosition(endPosition) | |
313 #if DCHECK_IS_ON() | |
314 , | |
315 m_container(container), | |
316 m_containerModifications(container->modifications()) | |
317 #endif | |
318 { | |
319 #if DCHECK_IS_ON() | |
320 DCHECK_EQ(m_containerModifications, m_container->modifications()); | |
321 #endif | |
322 } | |
323 | |
324 void checkModifications() const { | |
325 #if DCHECK_IS_ON() | |
326 // HashTable and collections that build on it do not support | |
327 // modifications while there is an iterator in use. The exception is | |
328 // ListHashSet, which has its own iterators that tolerate modification | |
329 // of the underlying set. | |
330 DCHECK_EQ(m_containerModifications, m_container->modifications()); | |
331 DCHECK(!m_container->accessForbidden()); | |
332 #endif | |
333 } | |
334 | |
335 public: | |
336 HashTableConstIterator() {} | |
337 | |
338 GetType get() const { | |
339 checkModifications(); | |
340 return m_position; | |
341 } | |
342 typename Traits::IteratorConstReferenceType operator*() const { | |
343 return Traits::getToReferenceConstConversion(get()); | |
344 } | |
345 GetType operator->() const { return get(); } | |
346 | |
347 const_iterator& operator++() { | |
348 DCHECK_NE(m_position, m_endPosition); | |
349 checkModifications(); | |
350 ++m_position; | |
351 skipEmptyBuckets(); | |
352 return *this; | |
353 } | |
354 | |
355 // postfix ++ intentionally omitted | |
356 | |
357 // Comparison. | |
358 bool operator==(const const_iterator& other) const { | |
359 return m_position == other.m_position; | |
360 } | |
361 bool operator!=(const const_iterator& other) const { | |
362 return m_position != other.m_position; | |
363 } | |
364 bool operator==(const iterator& other) const { | |
365 return *this == static_cast<const_iterator>(other); | |
366 } | |
367 bool operator!=(const iterator& other) const { | |
368 return *this != static_cast<const_iterator>(other); | |
369 } | |
370 | |
371 std::ostream& printTo(std::ostream& stream) const { | |
372 if (m_position == m_endPosition) | |
373 return stream << "iterator representing <end>"; | |
374 // TODO(tkent): Change |m_position| to |*m_position| to show the | |
375 // pointed object. It requires a lot of new stream printer functions. | |
376 return stream << "iterator pointing to " << m_position; | |
377 } | |
378 | |
379 private: | |
380 PointerType m_position; | |
381 PointerType m_endPosition; | |
382 #if DCHECK_IS_ON() | |
383 const HashTableType* m_container; | |
384 int64_t m_containerModifications; | |
385 #endif | |
386 }; | |
387 | |
388 template <typename Key, | |
389 typename Value, | |
390 typename Extractor, | |
391 typename Hash, | |
392 typename Traits, | |
393 typename KeyTraits, | |
394 typename Allocator> | |
395 std::ostream& operator<<(std::ostream& stream, | |
396 const HashTableConstIterator<Key, | |
397 Value, | |
398 Extractor, | |
399 Hash, | |
400 Traits, | |
401 KeyTraits, | |
402 Allocator>& iterator) { | |
403 return iterator.printTo(stream); | |
404 } | |
405 | |
406 template <typename Key, | |
407 typename Value, | |
408 typename Extractor, | |
409 typename HashFunctions, | |
410 typename Traits, | |
411 typename KeyTraits, | |
412 typename Allocator> | |
413 class HashTableIterator final { | |
414 DISALLOW_NEW(); | |
415 | |
416 private: | |
417 typedef HashTable<Key, | |
418 Value, | |
419 Extractor, | |
420 HashFunctions, | |
421 Traits, | |
422 KeyTraits, | |
423 Allocator> | |
424 HashTableType; | |
425 typedef HashTableIterator<Key, | |
426 Value, | |
427 Extractor, | |
428 HashFunctions, | |
429 Traits, | |
430 KeyTraits, | |
431 Allocator> | |
432 iterator; | |
433 typedef HashTableConstIterator<Key, | |
434 Value, | |
435 Extractor, | |
436 HashFunctions, | |
437 Traits, | |
438 KeyTraits, | |
439 Allocator> | |
440 const_iterator; | |
441 typedef Value ValueType; | |
442 typedef typename Traits::IteratorGetType GetType; | |
443 typedef ValueType* PointerType; | |
444 | |
445 friend class HashTable<Key, | |
446 Value, | |
447 Extractor, | |
448 HashFunctions, | |
449 Traits, | |
450 KeyTraits, | |
451 Allocator>; | |
452 | |
453 HashTableIterator(PointerType pos, | |
454 PointerType end, | |
455 const HashTableType* container) | |
456 : m_iterator(pos, end, container) {} | |
457 HashTableIterator(PointerType pos, | |
458 PointerType end, | |
459 const HashTableType* container, | |
460 HashItemKnownGoodTag tag) | |
461 : m_iterator(pos, end, container, tag) {} | |
462 | |
463 public: | |
464 HashTableIterator() {} | |
465 | |
466 // default copy, assignment and destructor are OK | |
467 | |
468 GetType get() const { return const_cast<GetType>(m_iterator.get()); } | |
469 typename Traits::IteratorReferenceType operator*() const { | |
470 return Traits::getToReferenceConversion(get()); | |
471 } | |
472 GetType operator->() const { return get(); } | |
473 | |
474 iterator& operator++() { | |
475 ++m_iterator; | |
476 return *this; | |
477 } | |
478 | |
479 // postfix ++ intentionally omitted | |
480 | |
481 // Comparison. | |
482 bool operator==(const iterator& other) const { | |
483 return m_iterator == other.m_iterator; | |
484 } | |
485 bool operator!=(const iterator& other) const { | |
486 return m_iterator != other.m_iterator; | |
487 } | |
488 bool operator==(const const_iterator& other) const { | |
489 return m_iterator == other; | |
490 } | |
491 bool operator!=(const const_iterator& other) const { | |
492 return m_iterator != other; | |
493 } | |
494 | |
495 operator const_iterator() const { return m_iterator; } | |
496 std::ostream& printTo(std::ostream& stream) const { | |
497 return m_iterator.printTo(stream); | |
498 } | |
499 | |
500 private: | |
501 const_iterator m_iterator; | |
502 }; | |
503 | |
504 template <typename Key, | |
505 typename Value, | |
506 typename Extractor, | |
507 typename Hash, | |
508 typename Traits, | |
509 typename KeyTraits, | |
510 typename Allocator> | |
511 std::ostream& operator<<(std::ostream& stream, | |
512 const HashTableIterator<Key, | |
513 Value, | |
514 Extractor, | |
515 Hash, | |
516 Traits, | |
517 KeyTraits, | |
518 Allocator>& iterator) { | |
519 return iterator.printTo(stream); | |
520 } | |
521 | |
522 using std::swap; | |
523 | |
524 template <typename T, typename Allocator, bool enterGCForbiddenScope> | |
525 struct Mover { | |
526 STATIC_ONLY(Mover); | |
527 static void move(T&& from, T& to) { | |
528 to.~T(); | |
529 new (NotNull, &to) T(std::move(from)); | |
530 } | |
531 }; | |
532 | |
533 template <typename T, typename Allocator> | |
534 struct Mover<T, Allocator, true> { | |
535 STATIC_ONLY(Mover); | |
536 static void move(T&& from, T& to) { | |
537 to.~T(); | |
538 Allocator::enterGCForbiddenScope(); | |
539 new (NotNull, &to) T(std::move(from)); | |
540 Allocator::leaveGCForbiddenScope(); | |
541 } | |
542 }; | |
543 | |
544 template <typename HashFunctions> | |
545 class IdentityHashTranslator { | |
546 STATIC_ONLY(IdentityHashTranslator); | |
547 | |
548 public: | |
549 template <typename T> | |
550 static unsigned hash(const T& key) { | |
551 return HashFunctions::hash(key); | |
552 } | |
553 template <typename T, typename U> | |
554 static bool equal(const T& a, const U& b) { | |
555 return HashFunctions::equal(a, b); | |
556 } | |
557 template <typename T, typename U, typename V> | |
558 static void translate(T& location, U&&, V&& value) { | |
559 location = std::forward<V>(value); | |
560 } | |
561 }; | |
562 | |
563 template <typename HashTableType, typename ValueType> | |
564 struct HashTableAddResult final { | |
565 STACK_ALLOCATED(); | |
566 HashTableAddResult(const HashTableType* container, | |
567 ValueType* storedValue, | |
568 bool isNewEntry) | |
569 : storedValue(storedValue), | |
570 isNewEntry(isNewEntry) | |
571 #if ENABLE(SECURITY_ASSERT) | |
572 , | |
573 m_container(container), | |
574 m_containerModifications(container->modifications()) | |
575 #endif | |
576 { | |
577 ALLOW_UNUSED_LOCAL(container); | |
578 DCHECK(container); | |
579 } | |
580 | |
581 ValueType* storedValue; | |
582 bool isNewEntry; | |
583 | |
584 #if ENABLE(SECURITY_ASSERT) | |
585 ~HashTableAddResult() { | |
586 // If rehash happened before accessing storedValue, it's | |
587 // use-after-free. Any modification may cause a rehash, so we check for | |
588 // modifications here. | |
589 | |
590 // Rehash after accessing storedValue is harmless but will assert if the | |
591 // AddResult destructor takes place after a modification. You may need | |
592 // to limit the scope of the AddResult. | |
593 SECURITY_DCHECK(m_containerModifications == m_container->modifications()); | |
594 } | |
595 | |
596 private: | |
597 const HashTableType* m_container; | |
598 const int64_t m_containerModifications; | |
599 #endif | |
600 }; | |
601 | |
602 template <typename Value, typename Extractor, typename KeyTraits> | |
603 struct HashTableHelper { | |
604 STATIC_ONLY(HashTableHelper); | |
605 static bool isEmptyBucket(const Value& value) { | |
606 return isHashTraitsEmptyValue<KeyTraits>(Extractor::extract(value)); | |
607 } | |
608 static bool isDeletedBucket(const Value& value) { | |
609 return KeyTraits::isDeletedValue(Extractor::extract(value)); | |
610 } | |
611 static bool isEmptyOrDeletedBucket(const Value& value) { | |
612 return isEmptyBucket(value) || isDeletedBucket(value); | |
613 } | |
614 }; | |
615 | |
616 template <typename HashTranslator, | |
617 typename KeyTraits, | |
618 bool safeToCompareToEmptyOrDeleted> | |
619 struct HashTableKeyChecker { | |
620 STATIC_ONLY(HashTableKeyChecker); | |
621 // There's no simple generic way to make this check if | |
622 // safeToCompareToEmptyOrDeleted is false, so the check always passes. | |
623 template <typename T> | |
624 static bool checkKey(const T&) { | |
625 return true; | |
626 } | |
627 }; | |
628 | |
629 template <typename HashTranslator, typename KeyTraits> | |
630 struct HashTableKeyChecker<HashTranslator, KeyTraits, true> { | |
631 STATIC_ONLY(HashTableKeyChecker); | |
632 template <typename T> | |
633 static bool checkKey(const T& key) { | |
634 // FIXME : Check also equality to the deleted value. | |
635 return !HashTranslator::equal(KeyTraits::emptyValue(), key); | |
636 } | |
637 }; | |
638 | |
639 // Note: empty or deleted key values are not allowed, using them may lead to | |
640 // undefined behavior. For pointer keys this means that null pointers are not | |
641 // allowed unless you supply custom key traits. | |
642 template <typename Key, | |
643 typename Value, | |
644 typename Extractor, | |
645 typename HashFunctions, | |
646 typename Traits, | |
647 typename KeyTraits, | |
648 typename Allocator> | |
649 class HashTable final | |
650 : public ConditionalDestructor<HashTable<Key, | |
651 Value, | |
652 Extractor, | |
653 HashFunctions, | |
654 Traits, | |
655 KeyTraits, | |
656 Allocator>, | |
657 Allocator::isGarbageCollected> { | |
658 DISALLOW_NEW(); | |
659 | |
660 public: | |
661 typedef HashTableIterator<Key, | |
662 Value, | |
663 Extractor, | |
664 HashFunctions, | |
665 Traits, | |
666 KeyTraits, | |
667 Allocator> | |
668 iterator; | |
669 typedef HashTableConstIterator<Key, | |
670 Value, | |
671 Extractor, | |
672 HashFunctions, | |
673 Traits, | |
674 KeyTraits, | |
675 Allocator> | |
676 const_iterator; | |
677 typedef Traits ValueTraits; | |
678 typedef Key KeyType; | |
679 typedef typename KeyTraits::PeekInType KeyPeekInType; | |
680 typedef Value ValueType; | |
681 typedef Extractor ExtractorType; | |
682 typedef KeyTraits KeyTraitsType; | |
683 typedef IdentityHashTranslator<HashFunctions> IdentityTranslatorType; | |
684 typedef HashTableAddResult<HashTable, ValueType> AddResult; | |
685 | |
686 HashTable(); | |
687 void finalize() { | |
688 DCHECK(!Allocator::isGarbageCollected); | |
689 if (LIKELY(!m_table)) | |
690 return; | |
691 enterAccessForbiddenScope(); | |
692 deleteAllBucketsAndDeallocate(m_table, m_tableSize); | |
693 leaveAccessForbiddenScope(); | |
694 m_table = nullptr; | |
695 } | |
696 | |
697 HashTable(const HashTable&); | |
698 HashTable(HashTable&&); | |
699 void swap(HashTable&); | |
700 HashTable& operator=(const HashTable&); | |
701 HashTable& operator=(HashTable&&); | |
702 | |
703 // When the hash table is empty, just return the same iterator for end as | |
704 // for begin. This is more efficient because we don't have to skip all the | |
705 // empty and deleted buckets, and iterating an empty table is a common case | |
706 // that's worth optimizing. | |
707 iterator begin() { return isEmpty() ? end() : makeIterator(m_table); } | |
708 iterator end() { return makeKnownGoodIterator(m_table + m_tableSize); } | |
709 const_iterator begin() const { | |
710 return isEmpty() ? end() : makeConstIterator(m_table); | |
711 } | |
712 const_iterator end() const { | |
713 return makeKnownGoodConstIterator(m_table + m_tableSize); | |
714 } | |
715 | |
716 unsigned size() const { | |
717 DCHECK(!accessForbidden()); | |
718 return m_keyCount; | |
719 } | |
720 unsigned capacity() const { | |
721 DCHECK(!accessForbidden()); | |
722 return m_tableSize; | |
723 } | |
724 bool isEmpty() const { | |
725 DCHECK(!accessForbidden()); | |
726 return !m_keyCount; | |
727 } | |
728 | |
729 void reserveCapacityForSize(unsigned size); | |
730 | |
731 template <typename IncomingValueType> | |
732 AddResult add(IncomingValueType&& value) { | |
733 return add<IdentityTranslatorType>(Extractor::extract(value), | |
734 std::forward<IncomingValueType>(value)); | |
735 } | |
736 | |
737 // A special version of add() that finds the object by hashing and comparing | |
738 // with some other type, to avoid the cost of type conversion if the object | |
739 // is already in the table. | |
740 template <typename HashTranslator, typename T, typename Extra> | |
741 AddResult add(T&& key, Extra&&); | |
742 template <typename HashTranslator, typename T, typename Extra> | |
743 AddResult addPassingHashCode(T&& key, Extra&&); | |
744 | |
745 iterator find(KeyPeekInType key) { return find<IdentityTranslatorType>(key); } | |
746 const_iterator find(KeyPeekInType key) const { | |
747 return find<IdentityTranslatorType>(key); | |
748 } | |
749 bool contains(KeyPeekInType key) const { | |
750 return contains<IdentityTranslatorType>(key); | |
751 } | |
752 | |
753 template <typename HashTranslator, typename T> | |
754 iterator find(const T&); | |
755 template <typename HashTranslator, typename T> | |
756 const_iterator find(const T&) const; | |
757 template <typename HashTranslator, typename T> | |
758 bool contains(const T&) const; | |
759 | |
760 void remove(KeyPeekInType); | |
761 void remove(iterator); | |
762 void remove(const_iterator); | |
763 void clear(); | |
764 | |
765 static bool isEmptyBucket(const ValueType& value) { | |
766 return isHashTraitsEmptyValue<KeyTraits>(Extractor::extract(value)); | |
767 } | |
768 static bool isDeletedBucket(const ValueType& value) { | |
769 return KeyTraits::isDeletedValue(Extractor::extract(value)); | |
770 } | |
771 static bool isEmptyOrDeletedBucket(const ValueType& value) { | |
772 return HashTableHelper<ValueType, Extractor, | |
773 KeyTraits>::isEmptyOrDeletedBucket(value); | |
774 } | |
775 | |
776 ValueType* lookup(KeyPeekInType key) { | |
777 return lookup<IdentityTranslatorType, KeyPeekInType>(key); | |
778 } | |
779 template <typename HashTranslator, typename T> | |
780 ValueType* lookup(const T&); | |
781 template <typename HashTranslator, typename T> | |
782 const ValueType* lookup(const T&) const; | |
783 | |
784 template <typename VisitorDispatcher> | |
785 void trace(VisitorDispatcher); | |
786 | |
787 #if DCHECK_IS_ON() | |
788 void enterAccessForbiddenScope() { | |
789 DCHECK(!m_accessForbidden); | |
790 m_accessForbidden = true; | |
791 } | |
792 void leaveAccessForbiddenScope() { m_accessForbidden = false; } | |
793 bool accessForbidden() const { return m_accessForbidden; } | |
794 int64_t modifications() const { return m_modifications; } | |
795 void registerModification() { m_modifications++; } | |
796 // HashTable and collections that build on it do not support modifications | |
797 // while there is an iterator in use. The exception is ListHashSet, which | |
798 // has its own iterators that tolerate modification of the underlying set. | |
799 void checkModifications(int64_t mods) const { | |
800 DCHECK_EQ(mods, m_modifications); | |
801 } | |
802 #else | |
803 ALWAYS_INLINE void enterAccessForbiddenScope() {} | |
804 ALWAYS_INLINE void leaveAccessForbiddenScope() {} | |
805 ALWAYS_INLINE bool accessForbidden() const { return false; } | |
806 ALWAYS_INLINE int64_t modifications() const { return 0; } | |
807 ALWAYS_INLINE void registerModification() {} | |
808 ALWAYS_INLINE void checkModifications(int64_t mods) const {} | |
809 #endif | |
810 | |
811 private: | |
812 static ValueType* allocateTable(unsigned size); | |
813 static void deleteAllBucketsAndDeallocate(ValueType* table, unsigned size); | |
814 | |
815 typedef std::pair<ValueType*, bool> LookupType; | |
816 typedef std::pair<LookupType, unsigned> FullLookupType; | |
817 | |
818 LookupType lookupForWriting(const Key& key) { | |
819 return lookupForWriting<IdentityTranslatorType>(key); | |
820 } | |
821 template <typename HashTranslator, typename T> | |
822 FullLookupType fullLookupForWriting(const T&); | |
823 template <typename HashTranslator, typename T> | |
824 LookupType lookupForWriting(const T&); | |
825 | |
826 void remove(ValueType*); | |
827 | |
828 bool shouldExpand() const { | |
829 return (m_keyCount + m_deletedCount) * m_maxLoad >= m_tableSize; | |
830 } | |
831 bool mustRehashInPlace() const { | |
832 return m_keyCount * m_minLoad < m_tableSize * 2; | |
833 } | |
834 bool shouldShrink() const { | |
835 // isAllocationAllowed check should be at the last because it's | |
836 // expensive. | |
837 return m_keyCount * m_minLoad < m_tableSize && | |
838 m_tableSize > KeyTraits::minimumTableSize && | |
839 Allocator::isAllocationAllowed(); | |
840 } | |
841 ValueType* expand(ValueType* entry = 0); | |
842 void shrink() { rehash(m_tableSize / 2, 0); } | |
843 | |
844 ValueType* expandBuffer(unsigned newTableSize, ValueType* entry, bool&); | |
845 ValueType* rehashTo(ValueType* newTable, | |
846 unsigned newTableSize, | |
847 ValueType* entry); | |
848 ValueType* rehash(unsigned newTableSize, ValueType* entry); | |
849 ValueType* reinsert(ValueType&&); | |
850 | |
851 static void initializeBucket(ValueType& bucket); | |
852 static void deleteBucket(ValueType& bucket) { | |
853 bucket.~ValueType(); | |
854 Traits::constructDeletedValue(bucket, Allocator::isGarbageCollected); | |
855 } | |
856 | |
857 FullLookupType makeLookupResult(ValueType* position, | |
858 bool found, | |
859 unsigned hash) { | |
860 return FullLookupType(LookupType(position, found), hash); | |
861 } | |
862 | |
863 iterator makeIterator(ValueType* pos) { | |
864 return iterator(pos, m_table + m_tableSize, this); | |
865 } | |
866 const_iterator makeConstIterator(ValueType* pos) const { | |
867 return const_iterator(pos, m_table + m_tableSize, this); | |
868 } | |
869 iterator makeKnownGoodIterator(ValueType* pos) { | |
870 return iterator(pos, m_table + m_tableSize, this, HashItemKnownGood); | |
871 } | |
872 const_iterator makeKnownGoodConstIterator(ValueType* pos) const { | |
873 return const_iterator(pos, m_table + m_tableSize, this, HashItemKnownGood); | |
874 } | |
875 | |
876 static const unsigned m_maxLoad = 2; | |
877 static const unsigned m_minLoad = 6; | |
878 | |
879 unsigned tableSizeMask() const { | |
880 size_t mask = m_tableSize - 1; | |
881 DCHECK_EQ((mask & m_tableSize), 0u); | |
882 return mask; | |
883 } | |
884 | |
885 void setEnqueued() { m_queueFlag = true; } | |
886 void clearEnqueued() { m_queueFlag = false; } | |
887 bool enqueued() { return m_queueFlag; } | |
888 | |
889 ValueType* m_table; | |
890 unsigned m_tableSize; | |
891 unsigned m_keyCount; | |
892 #if DCHECK_IS_ON() | |
893 unsigned m_deletedCount : 30; | |
894 unsigned m_queueFlag : 1; | |
895 unsigned m_accessForbidden : 1; | |
896 unsigned m_modifications; | |
897 #else | |
898 unsigned m_deletedCount : 31; | |
899 unsigned m_queueFlag : 1; | |
900 #endif | |
901 | |
902 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
903 public: | |
904 mutable | |
905 typename std::conditional<Allocator::isGarbageCollected, | |
906 HashTableStats*, | |
907 std::unique_ptr<HashTableStats>>::type m_stats; | |
908 #endif | |
909 | |
910 template <WeakHandlingFlag x, | |
911 typename T, | |
912 typename U, | |
913 typename V, | |
914 typename W, | |
915 typename X, | |
916 typename Y, | |
917 typename Z> | |
918 friend struct WeakProcessingHashTableHelper; | |
919 template <typename T, typename U, typename V, typename W> | |
920 friend class LinkedHashSet; | |
921 }; | |
922 | |
923 template <typename Key, | |
924 typename Value, | |
925 typename Extractor, | |
926 typename HashFunctions, | |
927 typename Traits, | |
928 typename KeyTraits, | |
929 typename Allocator> | |
930 inline HashTable<Key, | |
931 Value, | |
932 Extractor, | |
933 HashFunctions, | |
934 Traits, | |
935 KeyTraits, | |
936 Allocator>::HashTable() | |
937 : m_table(nullptr), | |
938 m_tableSize(0), | |
939 m_keyCount(0), | |
940 m_deletedCount(0), | |
941 m_queueFlag(false) | |
942 #if DCHECK_IS_ON() | |
943 , | |
944 m_accessForbidden(false), | |
945 m_modifications(0) | |
946 #endif | |
947 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
948 , | |
949 m_stats(nullptr) | |
950 #endif | |
951 { | |
952 static_assert(Allocator::isGarbageCollected || | |
953 (!IsPointerToGarbageCollectedType<Key>::value && | |
954 !IsPointerToGarbageCollectedType<Value>::value), | |
955 "Cannot put raw pointers to garbage-collected classes into an " | |
956 "off-heap collection."); | |
957 } | |
958 | |
959 inline unsigned doubleHash(unsigned key) { | |
960 key = ~key + (key >> 23); | |
961 key ^= (key << 12); | |
962 key ^= (key >> 7); | |
963 key ^= (key << 2); | |
964 key ^= (key >> 20); | |
965 return key; | |
966 } | |
967 | |
968 inline unsigned calculateCapacity(unsigned size) { | |
969 for (unsigned mask = size; mask; mask >>= 1) | |
970 size |= mask; // 00110101010 -> 00111111111 | |
971 return (size + 1) * 2; // 00111111111 -> 10000000000 | |
972 } | |
973 | |
974 template <typename Key, | |
975 typename Value, | |
976 typename Extractor, | |
977 typename HashFunctions, | |
978 typename Traits, | |
979 typename KeyTraits, | |
980 typename Allocator> | |
981 void HashTable<Key, | |
982 Value, | |
983 Extractor, | |
984 HashFunctions, | |
985 Traits, | |
986 KeyTraits, | |
987 Allocator>::reserveCapacityForSize(unsigned newSize) { | |
988 unsigned newCapacity = calculateCapacity(newSize); | |
989 if (newCapacity < KeyTraits::minimumTableSize) | |
990 newCapacity = KeyTraits::minimumTableSize; | |
991 | |
992 if (newCapacity > capacity()) { | |
993 RELEASE_ASSERT(!static_cast<int>( | |
994 newCapacity >> | |
995 31)); // HashTable capacity should not overflow 32bit int. | |
996 rehash(newCapacity, 0); | |
997 } | |
998 } | |
999 | |
1000 template <typename Key, | |
1001 typename Value, | |
1002 typename Extractor, | |
1003 typename HashFunctions, | |
1004 typename Traits, | |
1005 typename KeyTraits, | |
1006 typename Allocator> | |
1007 template <typename HashTranslator, typename T> | |
1008 inline Value* | |
1009 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1010 lookup(const T& key) { | |
1011 return const_cast<Value*>( | |
1012 const_cast<const HashTable*>(this)->lookup<HashTranslator>(key)); | |
1013 } | |
1014 | |
1015 template <typename Key, | |
1016 typename Value, | |
1017 typename Extractor, | |
1018 typename HashFunctions, | |
1019 typename Traits, | |
1020 typename KeyTraits, | |
1021 typename Allocator> | |
1022 template <typename HashTranslator, typename T> | |
1023 inline const Value* | |
1024 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1025 lookup(const T& key) const { | |
1026 DCHECK(!accessForbidden()); | |
1027 DCHECK((HashTableKeyChecker< | |
1028 HashTranslator, KeyTraits, | |
1029 HashFunctions::safeToCompareToEmptyOrDeleted>::checkKey(key))); | |
1030 const ValueType* table = m_table; | |
1031 if (!table) | |
1032 return nullptr; | |
1033 | |
1034 size_t k = 0; | |
1035 size_t sizeMask = tableSizeMask(); | |
1036 unsigned h = HashTranslator::hash(key); | |
1037 size_t i = h & sizeMask; | |
1038 | |
1039 UPDATE_ACCESS_COUNTS(); | |
1040 | |
1041 while (1) { | |
1042 const ValueType* entry = table + i; | |
1043 | |
1044 if (HashFunctions::safeToCompareToEmptyOrDeleted) { | |
1045 if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
1046 return entry; | |
1047 | |
1048 if (isEmptyBucket(*entry)) | |
1049 return nullptr; | |
1050 } else { | |
1051 if (isEmptyBucket(*entry)) | |
1052 return nullptr; | |
1053 | |
1054 if (!isDeletedBucket(*entry) && | |
1055 HashTranslator::equal(Extractor::extract(*entry), key)) | |
1056 return entry; | |
1057 } | |
1058 UPDATE_PROBE_COUNTS(); | |
1059 if (!k) | |
1060 k = 1 | doubleHash(h); | |
1061 i = (i + k) & sizeMask; | |
1062 } | |
1063 } | |
1064 | |
1065 template <typename Key, | |
1066 typename Value, | |
1067 typename Extractor, | |
1068 typename HashFunctions, | |
1069 typename Traits, | |
1070 typename KeyTraits, | |
1071 typename Allocator> | |
1072 template <typename HashTranslator, typename T> | |
1073 inline typename HashTable<Key, | |
1074 Value, | |
1075 Extractor, | |
1076 HashFunctions, | |
1077 Traits, | |
1078 KeyTraits, | |
1079 Allocator>::LookupType | |
1080 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1081 lookupForWriting(const T& key) { | |
1082 DCHECK(!accessForbidden()); | |
1083 DCHECK(m_table); | |
1084 registerModification(); | |
1085 | |
1086 ValueType* table = m_table; | |
1087 size_t k = 0; | |
1088 size_t sizeMask = tableSizeMask(); | |
1089 unsigned h = HashTranslator::hash(key); | |
1090 size_t i = h & sizeMask; | |
1091 | |
1092 UPDATE_ACCESS_COUNTS(); | |
1093 | |
1094 ValueType* deletedEntry = nullptr; | |
1095 | |
1096 while (1) { | |
1097 ValueType* entry = table + i; | |
1098 | |
1099 if (isEmptyBucket(*entry)) | |
1100 return LookupType(deletedEntry ? deletedEntry : entry, false); | |
1101 | |
1102 if (HashFunctions::safeToCompareToEmptyOrDeleted) { | |
1103 if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
1104 return LookupType(entry, true); | |
1105 | |
1106 if (isDeletedBucket(*entry)) | |
1107 deletedEntry = entry; | |
1108 } else { | |
1109 if (isDeletedBucket(*entry)) | |
1110 deletedEntry = entry; | |
1111 else if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
1112 return LookupType(entry, true); | |
1113 } | |
1114 UPDATE_PROBE_COUNTS(); | |
1115 if (!k) | |
1116 k = 1 | doubleHash(h); | |
1117 i = (i + k) & sizeMask; | |
1118 } | |
1119 } | |
1120 | |
1121 template <typename Key, | |
1122 typename Value, | |
1123 typename Extractor, | |
1124 typename HashFunctions, | |
1125 typename Traits, | |
1126 typename KeyTraits, | |
1127 typename Allocator> | |
1128 template <typename HashTranslator, typename T> | |
1129 inline typename HashTable<Key, | |
1130 Value, | |
1131 Extractor, | |
1132 HashFunctions, | |
1133 Traits, | |
1134 KeyTraits, | |
1135 Allocator>::FullLookupType | |
1136 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1137 fullLookupForWriting(const T& key) { | |
1138 DCHECK(!accessForbidden()); | |
1139 DCHECK(m_table); | |
1140 registerModification(); | |
1141 | |
1142 ValueType* table = m_table; | |
1143 size_t k = 0; | |
1144 size_t sizeMask = tableSizeMask(); | |
1145 unsigned h = HashTranslator::hash(key); | |
1146 size_t i = h & sizeMask; | |
1147 | |
1148 UPDATE_ACCESS_COUNTS(); | |
1149 | |
1150 ValueType* deletedEntry = nullptr; | |
1151 | |
1152 while (1) { | |
1153 ValueType* entry = table + i; | |
1154 | |
1155 if (isEmptyBucket(*entry)) | |
1156 return makeLookupResult(deletedEntry ? deletedEntry : entry, false, h); | |
1157 | |
1158 if (HashFunctions::safeToCompareToEmptyOrDeleted) { | |
1159 if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
1160 return makeLookupResult(entry, true, h); | |
1161 | |
1162 if (isDeletedBucket(*entry)) | |
1163 deletedEntry = entry; | |
1164 } else { | |
1165 if (isDeletedBucket(*entry)) | |
1166 deletedEntry = entry; | |
1167 else if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
1168 return makeLookupResult(entry, true, h); | |
1169 } | |
1170 UPDATE_PROBE_COUNTS(); | |
1171 if (!k) | |
1172 k = 1 | doubleHash(h); | |
1173 i = (i + k) & sizeMask; | |
1174 } | |
1175 } | |
1176 | |
1177 template <bool emptyValueIsZero> | |
1178 struct HashTableBucketInitializer; | |
1179 | |
1180 template <> | |
1181 struct HashTableBucketInitializer<false> { | |
1182 STATIC_ONLY(HashTableBucketInitializer); | |
1183 template <typename Traits, typename Value> | |
1184 static void initialize(Value& bucket) { | |
1185 new (NotNull, &bucket) Value(Traits::emptyValue()); | |
1186 } | |
1187 }; | |
1188 | |
1189 template <> | |
1190 struct HashTableBucketInitializer<true> { | |
1191 STATIC_ONLY(HashTableBucketInitializer); | |
1192 template <typename Traits, typename Value> | |
1193 static void initialize(Value& bucket) { | |
1194 // This initializes the bucket without copying the empty value. That | |
1195 // makes it possible to use this with types that don't support copying. | |
1196 // The memset to 0 looks like a slow operation but is optimized by the | |
1197 // compilers. | |
1198 memset(&bucket, 0, sizeof(bucket)); | |
1199 } | |
1200 }; | |
1201 | |
1202 template <typename Key, | |
1203 typename Value, | |
1204 typename Extractor, | |
1205 typename HashFunctions, | |
1206 typename Traits, | |
1207 typename KeyTraits, | |
1208 typename Allocator> | |
1209 inline void | |
1210 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1211 initializeBucket(ValueType& bucket) { | |
1212 HashTableBucketInitializer<Traits::emptyValueIsZero>::template initialize< | |
1213 Traits>(bucket); | |
1214 } | |
1215 | |
1216 template <typename Key, | |
1217 typename Value, | |
1218 typename Extractor, | |
1219 typename HashFunctions, | |
1220 typename Traits, | |
1221 typename KeyTraits, | |
1222 typename Allocator> | |
1223 template <typename HashTranslator, typename T, typename Extra> | |
1224 typename HashTable<Key, | |
1225 Value, | |
1226 Extractor, | |
1227 HashFunctions, | |
1228 Traits, | |
1229 KeyTraits, | |
1230 Allocator>::AddResult | |
1231 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1232 add(T&& key, Extra&& extra) { | |
1233 DCHECK(!accessForbidden()); | |
1234 DCHECK(Allocator::isAllocationAllowed()); | |
1235 if (!m_table) | |
1236 expand(); | |
1237 | |
1238 DCHECK(m_table); | |
1239 | |
1240 ValueType* table = m_table; | |
1241 size_t k = 0; | |
1242 size_t sizeMask = tableSizeMask(); | |
1243 unsigned h = HashTranslator::hash(key); | |
1244 size_t i = h & sizeMask; | |
1245 | |
1246 UPDATE_ACCESS_COUNTS(); | |
1247 | |
1248 ValueType* deletedEntry = nullptr; | |
1249 ValueType* entry; | |
1250 while (1) { | |
1251 entry = table + i; | |
1252 | |
1253 if (isEmptyBucket(*entry)) | |
1254 break; | |
1255 | |
1256 if (HashFunctions::safeToCompareToEmptyOrDeleted) { | |
1257 if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
1258 return AddResult(this, entry, false); | |
1259 | |
1260 if (isDeletedBucket(*entry)) | |
1261 deletedEntry = entry; | |
1262 } else { | |
1263 if (isDeletedBucket(*entry)) | |
1264 deletedEntry = entry; | |
1265 else if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
1266 return AddResult(this, entry, false); | |
1267 } | |
1268 UPDATE_PROBE_COUNTS(); | |
1269 if (!k) | |
1270 k = 1 | doubleHash(h); | |
1271 i = (i + k) & sizeMask; | |
1272 } | |
1273 | |
1274 registerModification(); | |
1275 | |
1276 if (deletedEntry) { | |
1277 // Overwrite any data left over from last use, using placement new or | |
1278 // memset. | |
1279 initializeBucket(*deletedEntry); | |
1280 entry = deletedEntry; | |
1281 --m_deletedCount; | |
1282 } | |
1283 | |
1284 HashTranslator::translate(*entry, std::forward<T>(key), | |
1285 std::forward<Extra>(extra)); | |
1286 DCHECK(!isEmptyOrDeletedBucket(*entry)); | |
1287 | |
1288 ++m_keyCount; | |
1289 | |
1290 if (shouldExpand()) { | |
1291 entry = expand(entry); | |
1292 } else if (Traits::weakHandlingFlag == WeakHandlingInCollections && | |
1293 shouldShrink()) { | |
1294 // When weak hash tables are processed by the garbage collector, | |
1295 // elements with no other strong references to them will have their | |
1296 // table entries cleared. But no shrinking of the backing store is | |
1297 // allowed at that time, as allocations are prohibited during that | |
1298 // GC phase. | |
1299 // | |
1300 // With that weak processing taking care of removals, explicit | |
1301 // remove()s of elements is rarely done. Which implies that the | |
1302 // weak hash table will never be checked if it can be shrunk. | |
1303 // | |
1304 // To prevent weak hash tables with very low load factors from | |
1305 // developing, we perform it when adding elements instead. | |
1306 entry = rehash(m_tableSize / 2, entry); | |
1307 } | |
1308 | |
1309 return AddResult(this, entry, true); | |
1310 } | |
1311 | |
1312 template <typename Key, | |
1313 typename Value, | |
1314 typename Extractor, | |
1315 typename HashFunctions, | |
1316 typename Traits, | |
1317 typename KeyTraits, | |
1318 typename Allocator> | |
1319 template <typename HashTranslator, typename T, typename Extra> | |
1320 typename HashTable<Key, | |
1321 Value, | |
1322 Extractor, | |
1323 HashFunctions, | |
1324 Traits, | |
1325 KeyTraits, | |
1326 Allocator>::AddResult | |
1327 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1328 addPassingHashCode(T&& key, Extra&& extra) { | |
1329 DCHECK(!accessForbidden()); | |
1330 DCHECK(Allocator::isAllocationAllowed()); | |
1331 if (!m_table) | |
1332 expand(); | |
1333 | |
1334 FullLookupType lookupResult = fullLookupForWriting<HashTranslator>(key); | |
1335 | |
1336 ValueType* entry = lookupResult.first.first; | |
1337 bool found = lookupResult.first.second; | |
1338 unsigned h = lookupResult.second; | |
1339 | |
1340 if (found) | |
1341 return AddResult(this, entry, false); | |
1342 | |
1343 registerModification(); | |
1344 | |
1345 if (isDeletedBucket(*entry)) { | |
1346 initializeBucket(*entry); | |
1347 --m_deletedCount; | |
1348 } | |
1349 | |
1350 HashTranslator::translate(*entry, std::forward<T>(key), | |
1351 std::forward<Extra>(extra), h); | |
1352 DCHECK(!isEmptyOrDeletedBucket(*entry)); | |
1353 | |
1354 ++m_keyCount; | |
1355 if (shouldExpand()) | |
1356 entry = expand(entry); | |
1357 | |
1358 return AddResult(this, entry, true); | |
1359 } | |
1360 | |
1361 template <typename Key, | |
1362 typename Value, | |
1363 typename Extractor, | |
1364 typename HashFunctions, | |
1365 typename Traits, | |
1366 typename KeyTraits, | |
1367 typename Allocator> | |
1368 Value* | |
1369 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1370 reinsert(ValueType&& entry) { | |
1371 DCHECK(m_table); | |
1372 registerModification(); | |
1373 DCHECK(!lookupForWriting(Extractor::extract(entry)).second); | |
1374 DCHECK( | |
1375 !isDeletedBucket(*(lookupForWriting(Extractor::extract(entry)).first))); | |
1376 #if DUMP_HASHTABLE_STATS | |
1377 atomicIncrement(&HashTableStats::instance().numReinserts); | |
1378 #endif | |
1379 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
1380 ++m_stats->numReinserts; | |
1381 #endif | |
1382 Value* newEntry = lookupForWriting(Extractor::extract(entry)).first; | |
1383 Mover<ValueType, Allocator, | |
1384 Traits::template NeedsToForbidGCOnMove<>::value>::move(std::move(entry), | |
1385 *newEntry); | |
1386 | |
1387 return newEntry; | |
1388 } | |
1389 | |
1390 template <typename Key, | |
1391 typename Value, | |
1392 typename Extractor, | |
1393 typename HashFunctions, | |
1394 typename Traits, | |
1395 typename KeyTraits, | |
1396 typename Allocator> | |
1397 template <typename HashTranslator, typename T> | |
1398 inline typename HashTable<Key, | |
1399 Value, | |
1400 Extractor, | |
1401 HashFunctions, | |
1402 Traits, | |
1403 KeyTraits, | |
1404 Allocator>::iterator | |
1405 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1406 find(const T& key) { | |
1407 ValueType* entry = lookup<HashTranslator>(key); | |
1408 if (!entry) | |
1409 return end(); | |
1410 | |
1411 return makeKnownGoodIterator(entry); | |
1412 } | |
1413 | |
1414 template <typename Key, | |
1415 typename Value, | |
1416 typename Extractor, | |
1417 typename HashFunctions, | |
1418 typename Traits, | |
1419 typename KeyTraits, | |
1420 typename Allocator> | |
1421 template <typename HashTranslator, typename T> | |
1422 inline typename HashTable<Key, | |
1423 Value, | |
1424 Extractor, | |
1425 HashFunctions, | |
1426 Traits, | |
1427 KeyTraits, | |
1428 Allocator>::const_iterator | |
1429 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1430 find(const T& key) const { | |
1431 ValueType* entry = const_cast<HashTable*>(this)->lookup<HashTranslator>(key); | |
1432 if (!entry) | |
1433 return end(); | |
1434 | |
1435 return makeKnownGoodConstIterator(entry); | |
1436 } | |
1437 | |
1438 template <typename Key, | |
1439 typename Value, | |
1440 typename Extractor, | |
1441 typename HashFunctions, | |
1442 typename Traits, | |
1443 typename KeyTraits, | |
1444 typename Allocator> | |
1445 template <typename HashTranslator, typename T> | |
1446 bool HashTable<Key, | |
1447 Value, | |
1448 Extractor, | |
1449 HashFunctions, | |
1450 Traits, | |
1451 KeyTraits, | |
1452 Allocator>::contains(const T& key) const { | |
1453 return const_cast<HashTable*>(this)->lookup<HashTranslator>(key); | |
1454 } | |
1455 | |
1456 template <typename Key, | |
1457 typename Value, | |
1458 typename Extractor, | |
1459 typename HashFunctions, | |
1460 typename Traits, | |
1461 typename KeyTraits, | |
1462 typename Allocator> | |
1463 void HashTable<Key, | |
1464 Value, | |
1465 Extractor, | |
1466 HashFunctions, | |
1467 Traits, | |
1468 KeyTraits, | |
1469 Allocator>::remove(ValueType* pos) { | |
1470 registerModification(); | |
1471 #if DUMP_HASHTABLE_STATS | |
1472 atomicIncrement(&HashTableStats::instance().numRemoves); | |
1473 #endif | |
1474 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
1475 ++m_stats->numRemoves; | |
1476 #endif | |
1477 | |
1478 enterAccessForbiddenScope(); | |
1479 deleteBucket(*pos); | |
1480 leaveAccessForbiddenScope(); | |
1481 ++m_deletedCount; | |
1482 --m_keyCount; | |
1483 | |
1484 if (shouldShrink()) | |
1485 shrink(); | |
1486 } | |
1487 | |
1488 template <typename Key, | |
1489 typename Value, | |
1490 typename Extractor, | |
1491 typename HashFunctions, | |
1492 typename Traits, | |
1493 typename KeyTraits, | |
1494 typename Allocator> | |
1495 inline void | |
1496 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1497 remove(iterator it) { | |
1498 if (it == end()) | |
1499 return; | |
1500 remove(const_cast<ValueType*>(it.m_iterator.m_position)); | |
1501 } | |
1502 | |
1503 template <typename Key, | |
1504 typename Value, | |
1505 typename Extractor, | |
1506 typename HashFunctions, | |
1507 typename Traits, | |
1508 typename KeyTraits, | |
1509 typename Allocator> | |
1510 inline void | |
1511 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1512 remove(const_iterator it) { | |
1513 if (it == end()) | |
1514 return; | |
1515 remove(const_cast<ValueType*>(it.m_position)); | |
1516 } | |
1517 | |
1518 template <typename Key, | |
1519 typename Value, | |
1520 typename Extractor, | |
1521 typename HashFunctions, | |
1522 typename Traits, | |
1523 typename KeyTraits, | |
1524 typename Allocator> | |
1525 inline void | |
1526 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1527 remove(KeyPeekInType key) { | |
1528 remove(find(key)); | |
1529 } | |
1530 | |
1531 template <typename Key, | |
1532 typename Value, | |
1533 typename Extractor, | |
1534 typename HashFunctions, | |
1535 typename Traits, | |
1536 typename KeyTraits, | |
1537 typename Allocator> | |
1538 Value* | |
1539 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1540 allocateTable(unsigned size) { | |
1541 size_t allocSize = size * sizeof(ValueType); | |
1542 ValueType* result; | |
1543 // Assert that we will not use memset on things with a vtable entry. The | |
1544 // compiler will also check this on some platforms. We would like to check | |
1545 // this on the whole value (key-value pair), but std::is_polymorphic will | |
1546 // return false for a pair of two types, even if one of the components is | |
1547 // polymorphic. | |
1548 static_assert( | |
1549 !Traits::emptyValueIsZero || !std::is_polymorphic<KeyType>::value, | |
1550 "empty value cannot be zero for things with a vtable"); | |
1551 static_assert(Allocator::isGarbageCollected || | |
1552 ((!AllowsOnlyPlacementNew<KeyType>::value || | |
1553 !IsTraceable<KeyType>::value) && | |
1554 (!AllowsOnlyPlacementNew<ValueType>::value || | |
1555 !IsTraceable<ValueType>::value)), | |
1556 "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that " | |
1557 "have trace methods into an off-heap HashTable"); | |
1558 | |
1559 if (Traits::emptyValueIsZero) { | |
1560 result = Allocator::template allocateZeroedHashTableBacking<ValueType, | |
1561 HashTable>( | |
1562 allocSize); | |
1563 } else { | |
1564 result = Allocator::template allocateHashTableBacking<ValueType, HashTable>( | |
1565 allocSize); | |
1566 for (unsigned i = 0; i < size; i++) | |
1567 initializeBucket(result[i]); | |
1568 } | |
1569 return result; | |
1570 } | |
1571 | |
1572 template <typename Key, | |
1573 typename Value, | |
1574 typename Extractor, | |
1575 typename HashFunctions, | |
1576 typename Traits, | |
1577 typename KeyTraits, | |
1578 typename Allocator> | |
1579 void HashTable<Key, | |
1580 Value, | |
1581 Extractor, | |
1582 HashFunctions, | |
1583 Traits, | |
1584 KeyTraits, | |
1585 Allocator>::deleteAllBucketsAndDeallocate(ValueType* table, | |
1586 unsigned size) { | |
1587 if (!IsTriviallyDestructible<ValueType>::value) { | |
1588 for (unsigned i = 0; i < size; ++i) { | |
1589 // This code is called when the hash table is cleared or resized. We | |
1590 // have allocated a new backing store and we need to run the | |
1591 // destructors on the old backing store, as it is being freed. If we | |
1592 // are GCing we need to both call the destructor and mark the bucket | |
1593 // as deleted, otherwise the destructor gets called again when the | |
1594 // GC finds the backing store. With the default allocator it's | |
1595 // enough to call the destructor, since we will free the memory | |
1596 // explicitly and we won't see the memory with the bucket again. | |
1597 if (Allocator::isGarbageCollected) { | |
1598 if (!isEmptyOrDeletedBucket(table[i])) | |
1599 deleteBucket(table[i]); | |
1600 } else { | |
1601 if (!isDeletedBucket(table[i])) | |
1602 table[i].~ValueType(); | |
1603 } | |
1604 } | |
1605 } | |
1606 Allocator::freeHashTableBacking(table); | |
1607 } | |
1608 | |
1609 template <typename Key, | |
1610 typename Value, | |
1611 typename Extractor, | |
1612 typename HashFunctions, | |
1613 typename Traits, | |
1614 typename KeyTraits, | |
1615 typename Allocator> | |
1616 Value* | |
1617 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1618 expand(Value* entry) { | |
1619 unsigned newSize; | |
1620 if (!m_tableSize) { | |
1621 newSize = KeyTraits::minimumTableSize; | |
1622 } else if (mustRehashInPlace()) { | |
1623 newSize = m_tableSize; | |
1624 } else { | |
1625 newSize = m_tableSize * 2; | |
1626 RELEASE_ASSERT(newSize > m_tableSize); | |
1627 } | |
1628 | |
1629 return rehash(newSize, entry); | |
1630 } | |
1631 | |
1632 template <typename Key, | |
1633 typename Value, | |
1634 typename Extractor, | |
1635 typename HashFunctions, | |
1636 typename Traits, | |
1637 typename KeyTraits, | |
1638 typename Allocator> | |
1639 Value* | |
1640 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1641 expandBuffer(unsigned newTableSize, Value* entry, bool& success) { | |
1642 success = false; | |
1643 DCHECK_LT(m_tableSize, newTableSize); | |
1644 if (!Allocator::expandHashTableBacking(m_table, | |
1645 newTableSize * sizeof(ValueType))) | |
1646 return nullptr; | |
1647 | |
1648 success = true; | |
1649 | |
1650 Value* newEntry = nullptr; | |
1651 unsigned oldTableSize = m_tableSize; | |
1652 ValueType* originalTable = m_table; | |
1653 | |
1654 ValueType* temporaryTable = allocateTable(oldTableSize); | |
1655 for (unsigned i = 0; i < oldTableSize; i++) { | |
1656 if (&m_table[i] == entry) | |
1657 newEntry = &temporaryTable[i]; | |
1658 if (isEmptyOrDeletedBucket(m_table[i])) { | |
1659 DCHECK_NE(&m_table[i], entry); | |
1660 if (Traits::emptyValueIsZero) { | |
1661 memset(&temporaryTable[i], 0, sizeof(ValueType)); | |
1662 } else { | |
1663 initializeBucket(temporaryTable[i]); | |
1664 } | |
1665 } else { | |
1666 Mover<ValueType, Allocator, | |
1667 Traits::template NeedsToForbidGCOnMove<>::value>:: | |
1668 move(std::move(m_table[i]), temporaryTable[i]); | |
1669 } | |
1670 } | |
1671 m_table = temporaryTable; | |
1672 | |
1673 if (Traits::emptyValueIsZero) { | |
1674 memset(originalTable, 0, newTableSize * sizeof(ValueType)); | |
1675 } else { | |
1676 for (unsigned i = 0; i < newTableSize; i++) | |
1677 initializeBucket(originalTable[i]); | |
1678 } | |
1679 newEntry = rehashTo(originalTable, newTableSize, newEntry); | |
1680 | |
1681 enterAccessForbiddenScope(); | |
1682 deleteAllBucketsAndDeallocate(temporaryTable, oldTableSize); | |
1683 leaveAccessForbiddenScope(); | |
1684 | |
1685 return newEntry; | |
1686 } | |
1687 | |
1688 template <typename Key, | |
1689 typename Value, | |
1690 typename Extractor, | |
1691 typename HashFunctions, | |
1692 typename Traits, | |
1693 typename KeyTraits, | |
1694 typename Allocator> | |
1695 Value* | |
1696 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1697 rehashTo(ValueType* newTable, unsigned newTableSize, Value* entry) { | |
1698 unsigned oldTableSize = m_tableSize; | |
1699 ValueType* oldTable = m_table; | |
1700 | |
1701 #if DUMP_HASHTABLE_STATS | |
1702 if (oldTableSize != 0) | |
1703 atomicIncrement(&HashTableStats::instance().numRehashes); | |
1704 #endif | |
1705 | |
1706 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
1707 if (oldTableSize != 0) | |
1708 ++m_stats->numRehashes; | |
1709 #endif | |
1710 | |
1711 m_table = newTable; | |
1712 m_tableSize = newTableSize; | |
1713 | |
1714 Value* newEntry = nullptr; | |
1715 for (unsigned i = 0; i != oldTableSize; ++i) { | |
1716 if (isEmptyOrDeletedBucket(oldTable[i])) { | |
1717 DCHECK_NE(&oldTable[i], entry); | |
1718 continue; | |
1719 } | |
1720 Value* reinsertedEntry = reinsert(std::move(oldTable[i])); | |
1721 if (&oldTable[i] == entry) { | |
1722 DCHECK(!newEntry); | |
1723 newEntry = reinsertedEntry; | |
1724 } | |
1725 } | |
1726 | |
1727 m_deletedCount = 0; | |
1728 | |
1729 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
1730 if (!m_stats) | |
1731 m_stats = HashTableStatsPtr<Allocator>::create(); | |
1732 #endif | |
1733 | |
1734 return newEntry; | |
1735 } | |
1736 | |
1737 template <typename Key, | |
1738 typename Value, | |
1739 typename Extractor, | |
1740 typename HashFunctions, | |
1741 typename Traits, | |
1742 typename KeyTraits, | |
1743 typename Allocator> | |
1744 Value* | |
1745 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1746 rehash(unsigned newTableSize, Value* entry) { | |
1747 unsigned oldTableSize = m_tableSize; | |
1748 ValueType* oldTable = m_table; | |
1749 | |
1750 #if DUMP_HASHTABLE_STATS | |
1751 if (oldTableSize != 0) | |
1752 atomicIncrement(&HashTableStats::instance().numRehashes); | |
1753 #endif | |
1754 | |
1755 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
1756 if (oldTableSize != 0) | |
1757 ++m_stats->numRehashes; | |
1758 #endif | |
1759 | |
1760 // The Allocator::isGarbageCollected check is not needed. The check is just | |
1761 // a static hint for a compiler to indicate that Base::expandBuffer returns | |
1762 // false if Allocator is a PartitionAllocator. | |
1763 if (Allocator::isGarbageCollected && newTableSize > oldTableSize) { | |
1764 bool success; | |
1765 Value* newEntry = expandBuffer(newTableSize, entry, success); | |
1766 if (success) | |
1767 return newEntry; | |
1768 } | |
1769 | |
1770 ValueType* newTable = allocateTable(newTableSize); | |
1771 Value* newEntry = rehashTo(newTable, newTableSize, entry); | |
1772 | |
1773 enterAccessForbiddenScope(); | |
1774 deleteAllBucketsAndDeallocate(oldTable, oldTableSize); | |
1775 leaveAccessForbiddenScope(); | |
1776 | |
1777 return newEntry; | |
1778 } | |
1779 | |
1780 template <typename Key, | |
1781 typename Value, | |
1782 typename Extractor, | |
1783 typename HashFunctions, | |
1784 typename Traits, | |
1785 typename KeyTraits, | |
1786 typename Allocator> | |
1787 void HashTable<Key, | |
1788 Value, | |
1789 Extractor, | |
1790 HashFunctions, | |
1791 Traits, | |
1792 KeyTraits, | |
1793 Allocator>::clear() { | |
1794 registerModification(); | |
1795 if (!m_table) | |
1796 return; | |
1797 | |
1798 enterAccessForbiddenScope(); | |
1799 deleteAllBucketsAndDeallocate(m_table, m_tableSize); | |
1800 leaveAccessForbiddenScope(); | |
1801 m_table = nullptr; | |
1802 m_tableSize = 0; | |
1803 m_keyCount = 0; | |
1804 } | |
1805 | |
1806 template <typename Key, | |
1807 typename Value, | |
1808 typename Extractor, | |
1809 typename HashFunctions, | |
1810 typename Traits, | |
1811 typename KeyTraits, | |
1812 typename Allocator> | |
1813 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1814 HashTable(const HashTable& other) | |
1815 : m_table(nullptr), | |
1816 m_tableSize(0), | |
1817 m_keyCount(0), | |
1818 m_deletedCount(0), | |
1819 m_queueFlag(false) | |
1820 #if DCHECK_IS_ON() | |
1821 , | |
1822 m_accessForbidden(false), | |
1823 m_modifications(0) | |
1824 #endif | |
1825 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
1826 , | |
1827 m_stats(HashTableStatsPtr<Allocator>::copy(other.m_stats)) | |
1828 #endif | |
1829 { | |
1830 if (other.size()) | |
1831 reserveCapacityForSize(other.size()); | |
1832 // Copy the hash table the dumb way, by adding each element to the new | |
1833 // table. It might be more efficient to copy the table slots, but it's not | |
1834 // clear that efficiency is needed. | |
1835 for (const auto& element : other) | |
1836 add(element); | |
1837 } | |
1838 | |
1839 template <typename Key, | |
1840 typename Value, | |
1841 typename Extractor, | |
1842 typename HashFunctions, | |
1843 typename Traits, | |
1844 typename KeyTraits, | |
1845 typename Allocator> | |
1846 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1847 HashTable(HashTable&& other) | |
1848 : m_table(nullptr), | |
1849 m_tableSize(0), | |
1850 m_keyCount(0), | |
1851 m_deletedCount(0), | |
1852 m_queueFlag(false) | |
1853 #if DCHECK_IS_ON() | |
1854 , | |
1855 m_accessForbidden(false), | |
1856 m_modifications(0) | |
1857 #endif | |
1858 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
1859 , | |
1860 m_stats(HashTableStatsPtr<Allocator>::copy(other.m_stats)) | |
1861 #endif | |
1862 { | |
1863 swap(other); | |
1864 } | |
1865 | |
1866 template <typename Key, | |
1867 typename Value, | |
1868 typename Extractor, | |
1869 typename HashFunctions, | |
1870 typename Traits, | |
1871 typename KeyTraits, | |
1872 typename Allocator> | |
1873 void HashTable<Key, | |
1874 Value, | |
1875 Extractor, | |
1876 HashFunctions, | |
1877 Traits, | |
1878 KeyTraits, | |
1879 Allocator>::swap(HashTable& other) { | |
1880 DCHECK(!accessForbidden()); | |
1881 std::swap(m_table, other.m_table); | |
1882 std::swap(m_tableSize, other.m_tableSize); | |
1883 std::swap(m_keyCount, other.m_keyCount); | |
1884 // std::swap does not work for bit fields. | |
1885 unsigned deleted = m_deletedCount; | |
1886 m_deletedCount = other.m_deletedCount; | |
1887 other.m_deletedCount = deleted; | |
1888 DCHECK(!m_queueFlag); | |
1889 DCHECK(!other.m_queueFlag); | |
1890 | |
1891 #if DCHECK_IS_ON() | |
1892 std::swap(m_modifications, other.m_modifications); | |
1893 #endif | |
1894 | |
1895 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
1896 HashTableStatsPtr<Allocator>::swap(m_stats, other.m_stats); | |
1897 #endif | |
1898 } | |
1899 | |
1900 template <typename Key, | |
1901 typename Value, | |
1902 typename Extractor, | |
1903 typename HashFunctions, | |
1904 typename Traits, | |
1905 typename KeyTraits, | |
1906 typename Allocator> | |
1907 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>& | |
1908 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1909 operator=(const HashTable& other) { | |
1910 HashTable tmp(other); | |
1911 swap(tmp); | |
1912 return *this; | |
1913 } | |
1914 | |
1915 template <typename Key, | |
1916 typename Value, | |
1917 typename Extractor, | |
1918 typename HashFunctions, | |
1919 typename Traits, | |
1920 typename KeyTraits, | |
1921 typename Allocator> | |
1922 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>& | |
1923 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: | |
1924 operator=(HashTable&& other) { | |
1925 swap(other); | |
1926 return *this; | |
1927 } | |
1928 | |
1929 template <WeakHandlingFlag weakHandlingFlag, | |
1930 typename Key, | |
1931 typename Value, | |
1932 typename Extractor, | |
1933 typename HashFunctions, | |
1934 typename Traits, | |
1935 typename KeyTraits, | |
1936 typename Allocator> | |
1937 struct WeakProcessingHashTableHelper; | |
1938 | |
1939 template <typename Key, | |
1940 typename Value, | |
1941 typename Extractor, | |
1942 typename HashFunctions, | |
1943 typename Traits, | |
1944 typename KeyTraits, | |
1945 typename Allocator> | |
1946 struct WeakProcessingHashTableHelper<NoWeakHandlingInCollections, | |
1947 Key, | |
1948 Value, | |
1949 Extractor, | |
1950 HashFunctions, | |
1951 Traits, | |
1952 KeyTraits, | |
1953 Allocator> { | |
1954 STATIC_ONLY(WeakProcessingHashTableHelper); | |
1955 static void process(typename Allocator::Visitor* visitor, void* closure) {} | |
1956 static void ephemeronIteration(typename Allocator::Visitor* visitor, | |
1957 void* closure) {} | |
1958 static void ephemeronIterationDone(typename Allocator::Visitor* visitor, | |
1959 void* closure) {} | |
1960 }; | |
1961 | |
1962 template <typename Key, | |
1963 typename Value, | |
1964 typename Extractor, | |
1965 typename HashFunctions, | |
1966 typename Traits, | |
1967 typename KeyTraits, | |
1968 typename Allocator> | |
1969 struct WeakProcessingHashTableHelper<WeakHandlingInCollections, | |
1970 Key, | |
1971 Value, | |
1972 Extractor, | |
1973 HashFunctions, | |
1974 Traits, | |
1975 KeyTraits, | |
1976 Allocator> { | |
1977 STATIC_ONLY(WeakProcessingHashTableHelper); | |
1978 | |
1979 using HashTableType = HashTable<Key, | |
1980 Value, | |
1981 Extractor, | |
1982 HashFunctions, | |
1983 Traits, | |
1984 KeyTraits, | |
1985 Allocator>; | |
1986 using ValueType = typename HashTableType::ValueType; | |
1987 | |
1988 // Used for purely weak and for weak-and-strong tables (ephemerons). | |
1989 static void process(typename Allocator::Visitor* visitor, void* closure) { | |
1990 HashTableType* table = reinterpret_cast<HashTableType*>(closure); | |
1991 if (!table->m_table) | |
1992 return; | |
1993 // Now perform weak processing (this is a no-op if the backing was | |
1994 // accessible through an iterator and was already marked strongly). | |
1995 for (ValueType* element = table->m_table + table->m_tableSize - 1; | |
1996 element >= table->m_table; element--) { | |
1997 if (!HashTableType::isEmptyOrDeletedBucket(*element)) { | |
1998 // At this stage calling trace can make no difference | |
1999 // (everything is already traced), but we use the return value | |
2000 // to remove things from the collection. | |
2001 | |
2002 // FIXME: This should be rewritten so that this can check if the | |
2003 // element is dead without calling trace, which is semantically | |
2004 // not correct to be called in weak processing stage. | |
2005 if (TraceInCollectionTrait<WeakHandlingInCollections, | |
2006 WeakPointersActWeak, ValueType, | |
2007 Traits>::trace(visitor, *element)) { | |
2008 table->registerModification(); | |
2009 HashTableType::deleteBucket(*element); // Also calls the destructor. | |
2010 table->m_deletedCount++; | |
2011 table->m_keyCount--; | |
2012 // We don't rehash the backing until the next add or delete, | |
2013 // because that would cause allocation during GC. | |
2014 } | |
2015 } | |
2016 } | |
2017 } | |
2018 | |
2019 // Called repeatedly for tables that have both weak and strong pointers. | |
2020 static void ephemeronIteration(typename Allocator::Visitor* visitor, | |
2021 void* closure) { | |
2022 HashTableType* table = reinterpret_cast<HashTableType*>(closure); | |
2023 DCHECK(table->m_table); | |
2024 // Check the hash table for elements that we now know will not be | |
2025 // removed by weak processing. Those elements need to have their strong | |
2026 // pointers traced. | |
2027 for (ValueType* element = table->m_table + table->m_tableSize - 1; | |
2028 element >= table->m_table; element--) { | |
2029 if (!HashTableType::isEmptyOrDeletedBucket(*element)) | |
2030 TraceInCollectionTrait<WeakHandlingInCollections, WeakPointersActWeak, | |
2031 ValueType, Traits>::trace(visitor, *element); | |
2032 } | |
2033 } | |
2034 | |
2035 // Called when the ephemeron iteration is done and before running the per | |
2036 // thread weak processing. It is guaranteed to be called before any thread | |
2037 // is resumed. | |
2038 static void ephemeronIterationDone(typename Allocator::Visitor* visitor, | |
2039 void* closure) { | |
2040 HashTableType* table = reinterpret_cast<HashTableType*>(closure); | |
2041 #if DCHECK_IS_ON() | |
2042 DCHECK(Allocator::weakTableRegistered(visitor, table)); | |
2043 #endif | |
2044 table->clearEnqueued(); | |
2045 } | |
2046 }; | |
2047 | |
2048 template <typename Key, | |
2049 typename Value, | |
2050 typename Extractor, | |
2051 typename HashFunctions, | |
2052 typename Traits, | |
2053 typename KeyTraits, | |
2054 typename Allocator> | |
2055 template <typename VisitorDispatcher> | |
2056 void HashTable<Key, | |
2057 Value, | |
2058 Extractor, | |
2059 HashFunctions, | |
2060 Traits, | |
2061 KeyTraits, | |
2062 Allocator>::trace(VisitorDispatcher visitor) { | |
2063 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
2064 Allocator::markNoTracing(visitor, m_stats); | |
2065 #endif | |
2066 | |
2067 // If someone else already marked the backing and queued up the trace and/or | |
2068 // weak callback then we are done. This optimization does not happen for | |
2069 // ListHashSet since its iterator does not point at the backing. | |
2070 if (!m_table || Allocator::isHeapObjectAlive(m_table)) | |
2071 return; | |
2072 | |
2073 // Normally, we mark the backing store without performing trace. This means | |
2074 // it is marked live, but the pointers inside it are not marked. Instead we | |
2075 // will mark the pointers below. However, for backing stores that contain | |
2076 // weak pointers the handling is rather different. We don't mark the | |
2077 // backing store here, so the marking GC will leave the backing unmarked. If | |
2078 // the backing is found in any other way than through its HashTable (ie from | |
2079 // an iterator) then the mark bit will be set and the pointers will be | |
2080 // marked strongly, avoiding problems with iterating over things that | |
2081 // disappear due to weak processing while we are iterating over them. We | |
2082 // register the backing store pointer for delayed marking which will take | |
2083 // place after we know if the backing is reachable from elsewhere. We also | |
2084 // register a weakProcessing callback which will perform weak processing if | |
2085 // needed. | |
2086 if (Traits::weakHandlingFlag == NoWeakHandlingInCollections) { | |
2087 Allocator::markNoTracing(visitor, m_table); | |
2088 } else { | |
2089 Allocator::registerDelayedMarkNoTracing(visitor, m_table); | |
2090 // Since we're delaying marking this HashTable, it is possible that the | |
2091 // registerWeakMembers is called multiple times (in rare | |
2092 // cases). However, it shouldn't cause any issue. | |
2093 Allocator::registerWeakMembers( | |
2094 visitor, this, | |
2095 WeakProcessingHashTableHelper<Traits::weakHandlingFlag, Key, Value, | |
2096 Extractor, HashFunctions, Traits, | |
2097 KeyTraits, Allocator>::process); | |
2098 } | |
2099 // If the backing store will be moved by sweep compaction, register the | |
2100 // table reference pointing to the backing store object, so that the | |
2101 // reference is updated upon object relocation. A no-op if not enabled | |
2102 // by the visitor. | |
2103 Allocator::registerBackingStoreReference(visitor, &m_table); | |
2104 if (!IsTraceableInCollectionTrait<Traits>::value) | |
2105 return; | |
2106 if (Traits::weakHandlingFlag == WeakHandlingInCollections) { | |
2107 // If we have both strong and weak pointers in the collection then | |
2108 // we queue up the collection for fixed point iteration a la | |
2109 // Ephemerons: | |
2110 // http://dl.acm.org/citation.cfm?doid=263698.263733 - see also | |
2111 // http://www.jucs.org/jucs_14_21/eliminating_cycles_in_weak | |
2112 #if DCHECK_IS_ON() | |
2113 DCHECK(!enqueued() || Allocator::weakTableRegistered(visitor, this)); | |
2114 #endif | |
2115 if (!enqueued()) { | |
2116 Allocator::registerWeakTable( | |
2117 visitor, this, | |
2118 WeakProcessingHashTableHelper< | |
2119 Traits::weakHandlingFlag, Key, Value, Extractor, HashFunctions, | |
2120 Traits, KeyTraits, Allocator>::ephemeronIteration, | |
2121 WeakProcessingHashTableHelper< | |
2122 Traits::weakHandlingFlag, Key, Value, Extractor, HashFunctions, | |
2123 Traits, KeyTraits, Allocator>::ephemeronIterationDone); | |
2124 setEnqueued(); | |
2125 } | |
2126 // We don't need to trace the elements here, since registering as a | |
2127 // weak table above will cause them to be traced (perhaps several | |
2128 // times). It's better to wait until everything else is traced | |
2129 // before tracing the elements for the first time; this may reduce | |
2130 // (by one) the number of iterations needed to get to a fixed point. | |
2131 return; | |
2132 } | |
2133 for (ValueType* element = m_table + m_tableSize - 1; element >= m_table; | |
2134 element--) { | |
2135 if (!isEmptyOrDeletedBucket(*element)) | |
2136 Allocator::template trace<VisitorDispatcher, ValueType, Traits>(visitor, | |
2137 *element); | |
2138 } | |
2139 } | |
2140 | |
2141 // iterator adapters | |
2142 | |
2143 template <typename HashTableType, typename Traits> | |
2144 struct HashTableConstIteratorAdapter { | |
2145 STACK_ALLOCATED(); | |
2146 HashTableConstIteratorAdapter() {} | |
2147 HashTableConstIteratorAdapter( | |
2148 const typename HashTableType::const_iterator& impl) | |
2149 : m_impl(impl) {} | |
2150 typedef typename Traits::IteratorConstGetType GetType; | |
2151 typedef | |
2152 typename HashTableType::ValueTraits::IteratorConstGetType SourceGetType; | |
2153 | |
2154 GetType get() const { | |
2155 return const_cast<GetType>(SourceGetType(m_impl.get())); | |
2156 } | |
2157 typename Traits::IteratorConstReferenceType operator*() const { | |
2158 return Traits::getToReferenceConstConversion(get()); | |
2159 } | |
2160 GetType operator->() const { return get(); } | |
2161 | |
2162 HashTableConstIteratorAdapter& operator++() { | |
2163 ++m_impl; | |
2164 return *this; | |
2165 } | |
2166 // postfix ++ intentionally omitted | |
2167 | |
2168 typename HashTableType::const_iterator m_impl; | |
2169 }; | |
2170 | |
2171 template <typename HashTable, typename Traits> | |
2172 std::ostream& operator<<( | |
2173 std::ostream& stream, | |
2174 const HashTableConstIteratorAdapter<HashTable, Traits>& iterator) { | |
2175 return stream << iterator.m_impl; | |
2176 } | |
2177 | |
2178 template <typename HashTableType, typename Traits> | |
2179 struct HashTableIteratorAdapter { | |
2180 STACK_ALLOCATED(); | |
2181 typedef typename Traits::IteratorGetType GetType; | |
2182 typedef typename HashTableType::ValueTraits::IteratorGetType SourceGetType; | |
2183 | |
2184 HashTableIteratorAdapter() {} | |
2185 HashTableIteratorAdapter(const typename HashTableType::iterator& impl) | |
2186 : m_impl(impl) {} | |
2187 | |
2188 GetType get() const { | |
2189 return const_cast<GetType>(SourceGetType(m_impl.get())); | |
2190 } | |
2191 typename Traits::IteratorReferenceType operator*() const { | |
2192 return Traits::getToReferenceConversion(get()); | |
2193 } | |
2194 GetType operator->() const { return get(); } | |
2195 | |
2196 HashTableIteratorAdapter& operator++() { | |
2197 ++m_impl; | |
2198 return *this; | |
2199 } | |
2200 // postfix ++ intentionally omitted | |
2201 | |
2202 operator HashTableConstIteratorAdapter<HashTableType, Traits>() { | |
2203 typename HashTableType::const_iterator i = m_impl; | |
2204 return i; | |
2205 } | |
2206 | |
2207 typename HashTableType::iterator m_impl; | |
2208 }; | |
2209 | |
2210 template <typename HashTable, typename Traits> | |
2211 std::ostream& operator<<( | |
2212 std::ostream& stream, | |
2213 const HashTableIteratorAdapter<HashTable, Traits>& iterator) { | |
2214 return stream << iterator.m_impl; | |
2215 } | |
2216 | |
2217 template <typename T, typename U> | |
2218 inline bool operator==(const HashTableConstIteratorAdapter<T, U>& a, | |
2219 const HashTableConstIteratorAdapter<T, U>& b) { | |
2220 return a.m_impl == b.m_impl; | |
2221 } | |
2222 | |
2223 template <typename T, typename U> | |
2224 inline bool operator!=(const HashTableConstIteratorAdapter<T, U>& a, | |
2225 const HashTableConstIteratorAdapter<T, U>& b) { | |
2226 return a.m_impl != b.m_impl; | |
2227 } | |
2228 | |
2229 template <typename T, typename U> | |
2230 inline bool operator==(const HashTableIteratorAdapter<T, U>& a, | |
2231 const HashTableIteratorAdapter<T, U>& b) { | |
2232 return a.m_impl == b.m_impl; | |
2233 } | |
2234 | |
2235 template <typename T, typename U> | |
2236 inline bool operator!=(const HashTableIteratorAdapter<T, U>& a, | |
2237 const HashTableIteratorAdapter<T, U>& b) { | |
2238 return a.m_impl != b.m_impl; | |
2239 } | |
2240 | |
2241 // All 4 combinations of ==, != and Const,non const. | |
2242 template <typename T, typename U> | |
2243 inline bool operator==(const HashTableConstIteratorAdapter<T, U>& a, | |
2244 const HashTableIteratorAdapter<T, U>& b) { | |
2245 return a.m_impl == b.m_impl; | |
2246 } | |
2247 | |
2248 template <typename T, typename U> | |
2249 inline bool operator!=(const HashTableConstIteratorAdapter<T, U>& a, | |
2250 const HashTableIteratorAdapter<T, U>& b) { | |
2251 return a.m_impl != b.m_impl; | |
2252 } | |
2253 | |
2254 template <typename T, typename U> | |
2255 inline bool operator==(const HashTableIteratorAdapter<T, U>& a, | |
2256 const HashTableConstIteratorAdapter<T, U>& b) { | |
2257 return a.m_impl == b.m_impl; | |
2258 } | |
2259 | |
2260 template <typename T, typename U> | |
2261 inline bool operator!=(const HashTableIteratorAdapter<T, U>& a, | |
2262 const HashTableConstIteratorAdapter<T, U>& b) { | |
2263 return a.m_impl != b.m_impl; | |
2264 } | |
2265 | |
2266 template <typename Collection1, typename Collection2> | |
2267 inline void removeAll(Collection1& collection, const Collection2& toBeRemoved) { | |
2268 if (collection.isEmpty() || toBeRemoved.isEmpty()) | |
2269 return; | |
2270 typedef typename Collection2::const_iterator CollectionIterator; | |
2271 CollectionIterator end(toBeRemoved.end()); | |
2272 for (CollectionIterator it(toBeRemoved.begin()); it != end; ++it) | |
2273 collection.erase(*it); | |
2274 } | |
2275 | |
2276 } // namespace WTF | |
2277 | |
2278 #include "wtf/HashIterators.h" | |
2279 | |
2280 #endif // WTF_HashTable_h | |
OLD | NEW |