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) 2011, Benjamin Poulain <ikipou@gmail.com> | |
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 | |
14 * Library 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_ListHashSet_h | 5 #include "platform/wtf/ListHashSet.h" |
24 #define WTF_ListHashSet_h | |
25 | 6 |
26 #include "wtf/HashSet.h" | 7 // The contents of this header was moved to platform/wtf as part of |
27 #include "wtf/allocator/PartitionAllocator.h" | 8 // WTF migration project. See the following post for details: |
28 #include <memory> | 9 // https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gY
CAAJ |
29 | |
30 namespace WTF { | |
31 | |
32 // ListHashSet: Just like HashSet, this class provides a Set interface - a | |
33 // collection of unique objects with O(1) insertion, removal and test for | |
34 // containership. However, it also has an order - iterating it will always give | |
35 // back values in the order in which they are added. | |
36 | |
37 // Unlike iteration of most WTF Hash data structures, iteration is guaranteed | |
38 // safe against mutation of the ListHashSet, except for removal of the item | |
39 // currently pointed to by a given iterator. | |
40 | |
41 template <typename Value, | |
42 size_t inlineCapacity, | |
43 typename HashFunctions, | |
44 typename Allocator> | |
45 class ListHashSet; | |
46 | |
47 template <typename Set> | |
48 class ListHashSetIterator; | |
49 template <typename Set> | |
50 class ListHashSetConstIterator; | |
51 template <typename Set> | |
52 class ListHashSetReverseIterator; | |
53 template <typename Set> | |
54 class ListHashSetConstReverseIterator; | |
55 | |
56 template <typename ValueArg> | |
57 class ListHashSetNodeBase; | |
58 template <typename ValueArg, typename Allocator> | |
59 class ListHashSetNode; | |
60 template <typename ValueArg, size_t inlineCapacity> | |
61 struct ListHashSetAllocator; | |
62 | |
63 template <typename HashArg> | |
64 struct ListHashSetNodeHashFunctions; | |
65 template <typename HashArg> | |
66 struct ListHashSetTranslator; | |
67 | |
68 // Note that for a ListHashSet you cannot specify the HashTraits as a template | |
69 // argument. It uses the default hash traits for the ValueArg type. | |
70 template <typename ValueArg, | |
71 size_t inlineCapacity = 256, | |
72 typename HashArg = typename DefaultHash<ValueArg>::Hash, | |
73 typename AllocatorArg = | |
74 ListHashSetAllocator<ValueArg, inlineCapacity>> | |
75 class ListHashSet | |
76 : public ConditionalDestructor< | |
77 ListHashSet<ValueArg, inlineCapacity, HashArg, AllocatorArg>, | |
78 AllocatorArg::isGarbageCollected> { | |
79 typedef AllocatorArg Allocator; | |
80 USE_ALLOCATOR(ListHashSet, Allocator); | |
81 | |
82 typedef ListHashSetNode<ValueArg, Allocator> Node; | |
83 typedef HashTraits<Node*> NodeTraits; | |
84 typedef ListHashSetNodeHashFunctions<HashArg> NodeHash; | |
85 typedef ListHashSetTranslator<HashArg> BaseTranslator; | |
86 | |
87 typedef HashTable<Node*, | |
88 Node*, | |
89 IdentityExtractor, | |
90 NodeHash, | |
91 NodeTraits, | |
92 NodeTraits, | |
93 typename Allocator::TableAllocator> | |
94 ImplType; | |
95 typedef HashTableIterator<Node*, | |
96 Node*, | |
97 IdentityExtractor, | |
98 NodeHash, | |
99 NodeTraits, | |
100 NodeTraits, | |
101 typename Allocator::TableAllocator> | |
102 ImplTypeIterator; | |
103 typedef HashTableConstIterator<Node*, | |
104 Node*, | |
105 IdentityExtractor, | |
106 NodeHash, | |
107 NodeTraits, | |
108 NodeTraits, | |
109 typename Allocator::TableAllocator> | |
110 ImplTypeConstIterator; | |
111 | |
112 typedef HashArg HashFunctions; | |
113 | |
114 public: | |
115 typedef ValueArg ValueType; | |
116 typedef HashTraits<ValueType> ValueTraits; | |
117 typedef typename ValueTraits::PeekInType ValuePeekInType; | |
118 | |
119 typedef ListHashSetIterator<ListHashSet> iterator; | |
120 typedef ListHashSetConstIterator<ListHashSet> const_iterator; | |
121 friend class ListHashSetIterator<ListHashSet>; | |
122 friend class ListHashSetConstIterator<ListHashSet>; | |
123 | |
124 typedef ListHashSetReverseIterator<ListHashSet> reverse_iterator; | |
125 typedef ListHashSetConstReverseIterator<ListHashSet> const_reverse_iterator; | |
126 friend class ListHashSetReverseIterator<ListHashSet>; | |
127 friend class ListHashSetConstReverseIterator<ListHashSet>; | |
128 | |
129 struct AddResult final { | |
130 STACK_ALLOCATED(); | |
131 friend class ListHashSet<ValueArg, inlineCapacity, HashArg, AllocatorArg>; | |
132 AddResult(Node* node, bool isNewEntry) | |
133 : storedValue(&node->m_value), isNewEntry(isNewEntry), m_node(node) {} | |
134 ValueType* storedValue; | |
135 bool isNewEntry; | |
136 | |
137 private: | |
138 Node* m_node; | |
139 }; | |
140 | |
141 ListHashSet(); | |
142 ListHashSet(const ListHashSet&); | |
143 ListHashSet(ListHashSet&&); | |
144 ListHashSet& operator=(const ListHashSet&); | |
145 ListHashSet& operator=(ListHashSet&&); | |
146 void finalize(); | |
147 | |
148 void swap(ListHashSet&); | |
149 | |
150 unsigned size() const { return m_impl.size(); } | |
151 unsigned capacity() const { return m_impl.capacity(); } | |
152 bool isEmpty() const { return m_impl.isEmpty(); } | |
153 | |
154 iterator begin() { return makeIterator(m_head); } | |
155 iterator end() { return makeIterator(0); } | |
156 const_iterator begin() const { return makeConstIterator(m_head); } | |
157 const_iterator end() const { return makeConstIterator(0); } | |
158 | |
159 reverse_iterator rbegin() { return makeReverseIterator(m_tail); } | |
160 reverse_iterator rend() { return makeReverseIterator(0); } | |
161 const_reverse_iterator rbegin() const { | |
162 return makeConstReverseIterator(m_tail); | |
163 } | |
164 const_reverse_iterator rend() const { return makeConstReverseIterator(0); } | |
165 | |
166 ValueType& front(); | |
167 const ValueType& front() const; | |
168 void removeFirst(); | |
169 | |
170 ValueType& back(); | |
171 const ValueType& back() const; | |
172 void pop_back(); | |
173 | |
174 iterator find(ValuePeekInType); | |
175 const_iterator find(ValuePeekInType) const; | |
176 bool contains(ValuePeekInType) const; | |
177 | |
178 // An alternate version of find() that finds the object by hashing and | |
179 // comparing with some other type, to avoid the cost of type conversion. | |
180 // The HashTranslator interface is defined in HashSet. | |
181 template <typename HashTranslator, typename T> | |
182 iterator find(const T&); | |
183 template <typename HashTranslator, typename T> | |
184 const_iterator find(const T&) const; | |
185 template <typename HashTranslator, typename T> | |
186 bool contains(const T&) const; | |
187 | |
188 // The return value of insert is a pair of a pointer to the stored value, and | |
189 // a bool that is true if an new entry was added. | |
190 template <typename IncomingValueType> | |
191 AddResult insert(IncomingValueType&&); | |
192 | |
193 // Same as insert() except that the return value is an iterator. Useful in | |
194 // cases where it's needed to have the same return value as find() and where | |
195 // it's not possible to use a pointer to the storedValue. | |
196 template <typename IncomingValueType> | |
197 iterator addReturnIterator(IncomingValueType&&); | |
198 | |
199 // Add the value to the end of the collection. If the value was already in | |
200 // the list, it is moved to the end. | |
201 template <typename IncomingValueType> | |
202 AddResult appendOrMoveToLast(IncomingValueType&&); | |
203 | |
204 // Add the value to the beginning of the collection. If the value was | |
205 // already in the list, it is moved to the beginning. | |
206 template <typename IncomingValueType> | |
207 AddResult prependOrMoveToFirst(IncomingValueType&&); | |
208 | |
209 template <typename IncomingValueType> | |
210 AddResult insertBefore(ValuePeekInType beforeValue, | |
211 IncomingValueType&& newValue); | |
212 template <typename IncomingValueType> | |
213 AddResult insertBefore(iterator, IncomingValueType&&); | |
214 | |
215 void erase(ValuePeekInType value) { return erase(find(value)); } | |
216 void erase(iterator); | |
217 void clear(); | |
218 template <typename Collection> | |
219 void removeAll(const Collection& other) { | |
220 WTF::removeAll(*this, other); | |
221 } | |
222 | |
223 ValueType take(iterator); | |
224 ValueType take(ValuePeekInType); | |
225 ValueType takeFirst(); | |
226 | |
227 template <typename VisitorDispatcher> | |
228 void trace(VisitorDispatcher); | |
229 | |
230 private: | |
231 void unlink(Node*); | |
232 void unlinkAndDelete(Node*); | |
233 void appendNode(Node*); | |
234 void prependNode(Node*); | |
235 void insertNodeBefore(Node* beforeNode, Node* newNode); | |
236 void deleteAllNodes(); | |
237 Allocator* getAllocator() const { return m_allocatorProvider.get(); } | |
238 void createAllocatorIfNeeded() { | |
239 m_allocatorProvider.createAllocatorIfNeeded(); | |
240 } | |
241 void deallocate(Node* node) const { m_allocatorProvider.deallocate(node); } | |
242 | |
243 iterator makeIterator(Node* position) { return iterator(this, position); } | |
244 const_iterator makeConstIterator(Node* position) const { | |
245 return const_iterator(this, position); | |
246 } | |
247 reverse_iterator makeReverseIterator(Node* position) { | |
248 return reverse_iterator(this, position); | |
249 } | |
250 const_reverse_iterator makeConstReverseIterator(Node* position) const { | |
251 return const_reverse_iterator(this, position); | |
252 } | |
253 | |
254 ImplType m_impl; | |
255 Node* m_head; | |
256 Node* m_tail; | |
257 typename Allocator::AllocatorProvider m_allocatorProvider; | |
258 }; | |
259 | |
260 // ListHashSetNode has this base class to hold the members because the MSVC | |
261 // compiler otherwise gets into circular template dependencies when trying to do | |
262 // sizeof on a node. | |
263 template <typename ValueArg> | |
264 class ListHashSetNodeBase { | |
265 DISALLOW_NEW(); | |
266 | |
267 protected: | |
268 template <typename U> | |
269 explicit ListHashSetNodeBase(U&& value) : m_value(std::forward<U>(value)) {} | |
270 | |
271 public: | |
272 ValueArg m_value; | |
273 ListHashSetNodeBase* m_prev = nullptr; | |
274 ListHashSetNodeBase* m_next = nullptr; | |
275 #if DCHECK_IS_ON() | |
276 bool m_isAllocated = true; | |
277 #endif | |
278 }; | |
279 | |
280 // This allocator is only used for non-Heap ListHashSets. | |
281 template <typename ValueArg, size_t inlineCapacity> | |
282 struct ListHashSetAllocator : public PartitionAllocator { | |
283 typedef PartitionAllocator TableAllocator; | |
284 typedef ListHashSetNode<ValueArg, ListHashSetAllocator> Node; | |
285 typedef ListHashSetNodeBase<ValueArg> NodeBase; | |
286 | |
287 class AllocatorProvider { | |
288 DISALLOW_NEW(); | |
289 | |
290 public: | |
291 AllocatorProvider() : m_allocator(nullptr) {} | |
292 void createAllocatorIfNeeded() { | |
293 if (!m_allocator) | |
294 m_allocator = new ListHashSetAllocator; | |
295 } | |
296 | |
297 void releaseAllocator() { | |
298 delete m_allocator; | |
299 m_allocator = nullptr; | |
300 } | |
301 | |
302 void swap(AllocatorProvider& other) { | |
303 std::swap(m_allocator, other.m_allocator); | |
304 } | |
305 | |
306 void deallocate(Node* node) const { | |
307 DCHECK(m_allocator); | |
308 m_allocator->deallocate(node); | |
309 } | |
310 | |
311 ListHashSetAllocator* get() const { | |
312 DCHECK(m_allocator); | |
313 return m_allocator; | |
314 } | |
315 | |
316 private: | |
317 // Not using std::unique_ptr as this pointer should be deleted at | |
318 // releaseAllocator() method rather than at destructor. | |
319 ListHashSetAllocator* m_allocator; | |
320 }; | |
321 | |
322 ListHashSetAllocator() | |
323 : m_freeList(pool()), m_isDoneWithInitialFreeList(false) { | |
324 memset(m_pool.buffer, 0, sizeof(m_pool.buffer)); | |
325 } | |
326 | |
327 Node* allocateNode() { | |
328 Node* result = m_freeList; | |
329 | |
330 if (!result) | |
331 return static_cast<Node*>(WTF::Partitions::fastMalloc( | |
332 sizeof(NodeBase), WTF_HEAP_PROFILER_TYPE_NAME(Node))); | |
333 | |
334 #if DCHECK_IS_ON() | |
335 DCHECK(!result->m_isAllocated); | |
336 #endif | |
337 | |
338 Node* next = result->next(); | |
339 #if DCHECK_IS_ON() | |
340 DCHECK(!next || !next->m_isAllocated); | |
341 #endif | |
342 if (!next && !m_isDoneWithInitialFreeList) { | |
343 next = result + 1; | |
344 if (next == pastPool()) { | |
345 m_isDoneWithInitialFreeList = true; | |
346 next = nullptr; | |
347 } else { | |
348 DCHECK(inPool(next)); | |
349 #if DCHECK_IS_ON() | |
350 DCHECK(!next->m_isAllocated); | |
351 #endif | |
352 } | |
353 } | |
354 m_freeList = next; | |
355 | |
356 return result; | |
357 } | |
358 | |
359 void deallocate(Node* node) { | |
360 if (inPool(node)) { | |
361 #if DCHECK_IS_ON() | |
362 node->m_isAllocated = false; | |
363 #endif | |
364 node->m_next = m_freeList; | |
365 m_freeList = node; | |
366 return; | |
367 } | |
368 | |
369 WTF::Partitions::fastFree(node); | |
370 } | |
371 | |
372 bool inPool(Node* node) { return node >= pool() && node < pastPool(); } | |
373 | |
374 static void traceValue(typename PartitionAllocator::Visitor* visitor, | |
375 Node* node) {} | |
376 | |
377 private: | |
378 Node* pool() { return reinterpret_cast_ptr<Node*>(m_pool.buffer); } | |
379 Node* pastPool() { return pool() + m_poolSize; } | |
380 | |
381 Node* m_freeList; | |
382 bool m_isDoneWithInitialFreeList; | |
383 #if defined(MEMORY_SANITIZER_INITIAL_SIZE) | |
384 // The allocation pool for nodes is one big chunk that ASAN has no insight | |
385 // into, so it can cloak errors. Make it as small as possible to force nodes | |
386 // to be allocated individually where ASAN can see them. | |
387 static const size_t m_poolSize = 1; | |
388 #else | |
389 static const size_t m_poolSize = inlineCapacity; | |
390 #endif | |
391 AlignedBuffer<sizeof(NodeBase) * m_poolSize, WTF_ALIGN_OF(NodeBase)> m_pool; | |
392 }; | |
393 | |
394 template <typename ValueArg, typename AllocatorArg> | |
395 class ListHashSetNode : public ListHashSetNodeBase<ValueArg> { | |
396 public: | |
397 typedef AllocatorArg NodeAllocator; | |
398 typedef ValueArg Value; | |
399 | |
400 template <typename U> | |
401 ListHashSetNode(U&& value) | |
402 : ListHashSetNodeBase<ValueArg>(std::forward<U>(value)) {} | |
403 | |
404 void* operator new(size_t, NodeAllocator* allocator) { | |
405 static_assert( | |
406 sizeof(ListHashSetNode) == sizeof(ListHashSetNodeBase<ValueArg>), | |
407 "please add any fields to the base"); | |
408 return allocator->allocateNode(); | |
409 } | |
410 | |
411 void setWasAlreadyDestructed() { | |
412 if (NodeAllocator::isGarbageCollected && | |
413 !IsTriviallyDestructible<ValueArg>::value) | |
414 this->m_prev = unlinkedNodePointer(); | |
415 } | |
416 | |
417 bool wasAlreadyDestructed() const { | |
418 DCHECK(NodeAllocator::isGarbageCollected); | |
419 return this->m_prev == unlinkedNodePointer(); | |
420 } | |
421 | |
422 static void finalize(void* pointer) { | |
423 // No need to waste time calling finalize if it's not needed. | |
424 DCHECK(!IsTriviallyDestructible<ValueArg>::value); | |
425 ListHashSetNode* self = reinterpret_cast_ptr<ListHashSetNode*>(pointer); | |
426 | |
427 // Check whether this node was already destructed before being unlinked | |
428 // from the collection. | |
429 if (self->wasAlreadyDestructed()) | |
430 return; | |
431 | |
432 self->m_value.~ValueArg(); | |
433 } | |
434 void finalizeGarbageCollectedObject() { finalize(this); } | |
435 | |
436 void destroy(NodeAllocator* allocator) { | |
437 this->~ListHashSetNode(); | |
438 setWasAlreadyDestructed(); | |
439 allocator->deallocate(this); | |
440 } | |
441 | |
442 // This is not called in normal tracing, but it is called if we find a | |
443 // pointer to a node on the stack using conservative scanning. Since the | |
444 // original ListHashSet may no longer exist we make sure to mark the | |
445 // neighbours in the chain too. | |
446 template <typename VisitorDispatcher> | |
447 void trace(VisitorDispatcher visitor) { | |
448 // The conservative stack scan can find nodes that have been removed | |
449 // from the set and destructed. We don't need to trace these, and it | |
450 // would be wrong to do so, because the class will not expect the trace | |
451 // method to be called after the destructor. It's an error to remove a | |
452 // node from the ListHashSet while an iterator is positioned at that | |
453 // node, so there should be no valid pointers from the stack to a | |
454 // destructed node. | |
455 if (wasAlreadyDestructed()) | |
456 return; | |
457 NodeAllocator::traceValue(visitor, this); | |
458 visitor->mark(next()); | |
459 visitor->mark(prev()); | |
460 } | |
461 | |
462 ListHashSetNode* next() const { | |
463 return reinterpret_cast<ListHashSetNode*>(this->m_next); | |
464 } | |
465 ListHashSetNode* prev() const { | |
466 return reinterpret_cast<ListHashSetNode*>(this->m_prev); | |
467 } | |
468 | |
469 // Don't add fields here, the ListHashSetNodeBase and this should have the | |
470 // same size. | |
471 | |
472 static ListHashSetNode* unlinkedNodePointer() { | |
473 return reinterpret_cast<ListHashSetNode*>(-1); | |
474 } | |
475 | |
476 template <typename HashArg> | |
477 friend struct ListHashSetNodeHashFunctions; | |
478 }; | |
479 | |
480 template <typename HashArg> | |
481 struct ListHashSetNodeHashFunctions { | |
482 STATIC_ONLY(ListHashSetNodeHashFunctions); | |
483 template <typename T> | |
484 static unsigned hash(const T& key) { | |
485 return HashArg::hash(key->m_value); | |
486 } | |
487 template <typename T> | |
488 static bool equal(const T& a, const T& b) { | |
489 return HashArg::equal(a->m_value, b->m_value); | |
490 } | |
491 static const bool safeToCompareToEmptyOrDeleted = false; | |
492 }; | |
493 | |
494 template <typename Set> | |
495 class ListHashSetIterator { | |
496 DISALLOW_NEW(); | |
497 | |
498 private: | |
499 typedef typename Set::const_iterator const_iterator; | |
500 typedef typename Set::Node Node; | |
501 typedef typename Set::ValueType ValueType; | |
502 typedef ValueType& ReferenceType; | |
503 typedef ValueType* PointerType; | |
504 | |
505 ListHashSetIterator(const Set* set, Node* position) | |
506 : m_iterator(set, position) {} | |
507 | |
508 public: | |
509 ListHashSetIterator() {} | |
510 | |
511 // default copy, assignment and destructor are OK | |
512 | |
513 PointerType get() const { return const_cast<PointerType>(m_iterator.get()); } | |
514 ReferenceType operator*() const { return *get(); } | |
515 PointerType operator->() const { return get(); } | |
516 | |
517 ListHashSetIterator& operator++() { | |
518 ++m_iterator; | |
519 return *this; | |
520 } | |
521 ListHashSetIterator& operator--() { | |
522 --m_iterator; | |
523 return *this; | |
524 } | |
525 | |
526 // Postfix ++ and -- intentionally omitted. | |
527 | |
528 // Comparison. | |
529 bool operator==(const ListHashSetIterator& other) const { | |
530 return m_iterator == other.m_iterator; | |
531 } | |
532 bool operator!=(const ListHashSetIterator& other) const { | |
533 return m_iterator != other.m_iterator; | |
534 } | |
535 | |
536 operator const_iterator() const { return m_iterator; } | |
537 | |
538 template <typename VisitorDispatcher> | |
539 void trace(VisitorDispatcher visitor) { | |
540 m_iterator.trace(visitor); | |
541 } | |
542 | |
543 private: | |
544 Node* getNode() { return m_iterator.getNode(); } | |
545 | |
546 const_iterator m_iterator; | |
547 | |
548 template <typename T, size_t inlineCapacity, typename U, typename V> | |
549 friend class ListHashSet; | |
550 }; | |
551 | |
552 template <typename Set> | |
553 class ListHashSetConstIterator { | |
554 DISALLOW_NEW(); | |
555 | |
556 private: | |
557 typedef typename Set::const_iterator const_iterator; | |
558 typedef typename Set::Node Node; | |
559 typedef typename Set::ValueType ValueType; | |
560 typedef const ValueType& ReferenceType; | |
561 typedef const ValueType* PointerType; | |
562 | |
563 friend class ListHashSetIterator<Set>; | |
564 | |
565 ListHashSetConstIterator(const Set* set, Node* position) | |
566 : m_set(set), m_position(position) {} | |
567 | |
568 public: | |
569 ListHashSetConstIterator() {} | |
570 | |
571 PointerType get() const { return &m_position->m_value; } | |
572 ReferenceType operator*() const { return *get(); } | |
573 PointerType operator->() const { return get(); } | |
574 | |
575 ListHashSetConstIterator& operator++() { | |
576 DCHECK(m_position); | |
577 m_position = m_position->next(); | |
578 return *this; | |
579 } | |
580 | |
581 ListHashSetConstIterator& operator--() { | |
582 DCHECK_NE(m_position, m_set->m_head); | |
583 if (!m_position) | |
584 m_position = m_set->m_tail; | |
585 else | |
586 m_position = m_position->prev(); | |
587 return *this; | |
588 } | |
589 | |
590 // Postfix ++ and -- intentionally omitted. | |
591 | |
592 // Comparison. | |
593 bool operator==(const ListHashSetConstIterator& other) const { | |
594 return m_position == other.m_position; | |
595 } | |
596 bool operator!=(const ListHashSetConstIterator& other) const { | |
597 return m_position != other.m_position; | |
598 } | |
599 | |
600 template <typename VisitorDispatcher> | |
601 void trace(VisitorDispatcher visitor) { | |
602 visitor->trace(*m_set); | |
603 visitor->trace(m_position); | |
604 } | |
605 | |
606 private: | |
607 Node* getNode() { return m_position; } | |
608 | |
609 const Set* m_set; | |
610 Node* m_position; | |
611 | |
612 template <typename T, size_t inlineCapacity, typename U, typename V> | |
613 friend class ListHashSet; | |
614 }; | |
615 | |
616 template <typename Set> | |
617 class ListHashSetReverseIterator { | |
618 DISALLOW_NEW(); | |
619 | |
620 private: | |
621 typedef typename Set::const_reverse_iterator const_reverse_iterator; | |
622 typedef typename Set::Node Node; | |
623 typedef typename Set::ValueType ValueType; | |
624 typedef ValueType& ReferenceType; | |
625 typedef ValueType* PointerType; | |
626 | |
627 ListHashSetReverseIterator(const Set* set, Node* position) | |
628 : m_iterator(set, position) {} | |
629 | |
630 public: | |
631 ListHashSetReverseIterator() {} | |
632 | |
633 // default copy, assignment and destructor are OK | |
634 | |
635 PointerType get() const { return const_cast<PointerType>(m_iterator.get()); } | |
636 ReferenceType operator*() const { return *get(); } | |
637 PointerType operator->() const { return get(); } | |
638 | |
639 ListHashSetReverseIterator& operator++() { | |
640 ++m_iterator; | |
641 return *this; | |
642 } | |
643 ListHashSetReverseIterator& operator--() { | |
644 --m_iterator; | |
645 return *this; | |
646 } | |
647 | |
648 // Postfix ++ and -- intentionally omitted. | |
649 | |
650 // Comparison. | |
651 bool operator==(const ListHashSetReverseIterator& other) const { | |
652 return m_iterator == other.m_iterator; | |
653 } | |
654 bool operator!=(const ListHashSetReverseIterator& other) const { | |
655 return m_iterator != other.m_iterator; | |
656 } | |
657 | |
658 operator const_reverse_iterator() const { return m_iterator; } | |
659 | |
660 template <typename VisitorDispatcher> | |
661 void trace(VisitorDispatcher visitor) { | |
662 m_iterator.trace(visitor); | |
663 } | |
664 | |
665 private: | |
666 Node* getNode() { return m_iterator.node(); } | |
667 | |
668 const_reverse_iterator m_iterator; | |
669 | |
670 template <typename T, size_t inlineCapacity, typename U, typename V> | |
671 friend class ListHashSet; | |
672 }; | |
673 | |
674 template <typename Set> | |
675 class ListHashSetConstReverseIterator { | |
676 DISALLOW_NEW(); | |
677 | |
678 private: | |
679 typedef typename Set::reverse_iterator reverse_iterator; | |
680 typedef typename Set::Node Node; | |
681 typedef typename Set::ValueType ValueType; | |
682 typedef const ValueType& ReferenceType; | |
683 typedef const ValueType* PointerType; | |
684 | |
685 friend class ListHashSetReverseIterator<Set>; | |
686 | |
687 ListHashSetConstReverseIterator(const Set* set, Node* position) | |
688 : m_set(set), m_position(position) {} | |
689 | |
690 public: | |
691 ListHashSetConstReverseIterator() {} | |
692 | |
693 PointerType get() const { return &m_position->m_value; } | |
694 ReferenceType operator*() const { return *get(); } | |
695 PointerType operator->() const { return get(); } | |
696 | |
697 ListHashSetConstReverseIterator& operator++() { | |
698 DCHECK(m_position); | |
699 m_position = m_position->prev(); | |
700 return *this; | |
701 } | |
702 | |
703 ListHashSetConstReverseIterator& operator--() { | |
704 DCHECK_NE(m_position, m_set->m_tail); | |
705 if (!m_position) | |
706 m_position = m_set->m_head; | |
707 else | |
708 m_position = m_position->next(); | |
709 return *this; | |
710 } | |
711 | |
712 // Postfix ++ and -- intentionally omitted. | |
713 | |
714 // Comparison. | |
715 bool operator==(const ListHashSetConstReverseIterator& other) const { | |
716 return m_position == other.m_position; | |
717 } | |
718 bool operator!=(const ListHashSetConstReverseIterator& other) const { | |
719 return m_position != other.m_position; | |
720 } | |
721 | |
722 template <typename VisitorDispatcher> | |
723 void trace(VisitorDispatcher visitor) { | |
724 visitor->trace(*m_set); | |
725 visitor->trace(m_position); | |
726 } | |
727 | |
728 private: | |
729 Node* getNode() { return m_position; } | |
730 | |
731 const Set* m_set; | |
732 Node* m_position; | |
733 | |
734 template <typename T, size_t inlineCapacity, typename U, typename V> | |
735 friend class ListHashSet; | |
736 }; | |
737 | |
738 template <typename HashFunctions> | |
739 struct ListHashSetTranslator { | |
740 STATIC_ONLY(ListHashSetTranslator); | |
741 template <typename T> | |
742 static unsigned hash(const T& key) { | |
743 return HashFunctions::hash(key); | |
744 } | |
745 template <typename T, typename U> | |
746 static bool equal(const T& a, const U& b) { | |
747 return HashFunctions::equal(a->m_value, b); | |
748 } | |
749 template <typename T, typename U, typename V> | |
750 static void translate(T*& location, U&& key, const V& allocator) { | |
751 location = new (const_cast<V*>(&allocator)) T(std::forward<U>(key)); | |
752 } | |
753 }; | |
754 | |
755 template <typename T, size_t inlineCapacity, typename U, typename Allocator> | |
756 inline ListHashSet<T, inlineCapacity, U, Allocator>::ListHashSet() | |
757 : m_head(nullptr), m_tail(nullptr) { | |
758 static_assert( | |
759 Allocator::isGarbageCollected || | |
760 !IsPointerToGarbageCollectedType<T>::value, | |
761 "Cannot put raw pointers to garbage-collected classes into " | |
762 "an off-heap ListHashSet. Use HeapListHashSet<Member<T>> instead."); | |
763 } | |
764 | |
765 template <typename T, size_t inlineCapacity, typename U, typename V> | |
766 inline ListHashSet<T, inlineCapacity, U, V>::ListHashSet( | |
767 const ListHashSet& other) | |
768 : m_head(nullptr), m_tail(nullptr) { | |
769 const_iterator end = other.end(); | |
770 for (const_iterator it = other.begin(); it != end; ++it) | |
771 insert(*it); | |
772 } | |
773 | |
774 template <typename T, size_t inlineCapacity, typename U, typename V> | |
775 inline ListHashSet<T, inlineCapacity, U, V>::ListHashSet(ListHashSet&& other) | |
776 : m_head(nullptr), m_tail(nullptr) { | |
777 swap(other); | |
778 } | |
779 | |
780 template <typename T, size_t inlineCapacity, typename U, typename V> | |
781 inline ListHashSet<T, inlineCapacity, U, V>& | |
782 ListHashSet<T, inlineCapacity, U, V>::operator=(const ListHashSet& other) { | |
783 ListHashSet tmp(other); | |
784 swap(tmp); | |
785 return *this; | |
786 } | |
787 | |
788 template <typename T, size_t inlineCapacity, typename U, typename V> | |
789 inline ListHashSet<T, inlineCapacity, U, V>& | |
790 ListHashSet<T, inlineCapacity, U, V>::operator=(ListHashSet&& other) { | |
791 swap(other); | |
792 return *this; | |
793 } | |
794 | |
795 template <typename T, size_t inlineCapacity, typename U, typename V> | |
796 inline void ListHashSet<T, inlineCapacity, U, V>::swap(ListHashSet& other) { | |
797 m_impl.swap(other.m_impl); | |
798 std::swap(m_head, other.m_head); | |
799 std::swap(m_tail, other.m_tail); | |
800 m_allocatorProvider.swap(other.m_allocatorProvider); | |
801 } | |
802 | |
803 template <typename T, size_t inlineCapacity, typename U, typename V> | |
804 inline void ListHashSet<T, inlineCapacity, U, V>::finalize() { | |
805 static_assert(!Allocator::isGarbageCollected, | |
806 "heap allocated ListHashSet should never call finalize()"); | |
807 deleteAllNodes(); | |
808 m_allocatorProvider.releaseAllocator(); | |
809 } | |
810 | |
811 template <typename T, size_t inlineCapacity, typename U, typename V> | |
812 inline T& ListHashSet<T, inlineCapacity, U, V>::front() { | |
813 DCHECK(!isEmpty()); | |
814 return m_head->m_value; | |
815 } | |
816 | |
817 template <typename T, size_t inlineCapacity, typename U, typename V> | |
818 inline void ListHashSet<T, inlineCapacity, U, V>::removeFirst() { | |
819 DCHECK(!isEmpty()); | |
820 m_impl.remove(m_head); | |
821 unlinkAndDelete(m_head); | |
822 } | |
823 | |
824 template <typename T, size_t inlineCapacity, typename U, typename V> | |
825 inline const T& ListHashSet<T, inlineCapacity, U, V>::front() const { | |
826 DCHECK(!isEmpty()); | |
827 return m_head->m_value; | |
828 } | |
829 | |
830 template <typename T, size_t inlineCapacity, typename U, typename V> | |
831 inline T& ListHashSet<T, inlineCapacity, U, V>::back() { | |
832 DCHECK(!isEmpty()); | |
833 return m_tail->m_value; | |
834 } | |
835 | |
836 template <typename T, size_t inlineCapacity, typename U, typename V> | |
837 inline const T& ListHashSet<T, inlineCapacity, U, V>::back() const { | |
838 DCHECK(!isEmpty()); | |
839 return m_tail->m_value; | |
840 } | |
841 | |
842 template <typename T, size_t inlineCapacity, typename U, typename V> | |
843 inline void ListHashSet<T, inlineCapacity, U, V>::pop_back() { | |
844 DCHECK(!isEmpty()); | |
845 m_impl.remove(m_tail); | |
846 unlinkAndDelete(m_tail); | |
847 } | |
848 | |
849 template <typename T, size_t inlineCapacity, typename U, typename V> | |
850 inline typename ListHashSet<T, inlineCapacity, U, V>::iterator | |
851 ListHashSet<T, inlineCapacity, U, V>::find(ValuePeekInType value) { | |
852 ImplTypeIterator it = m_impl.template find<BaseTranslator>(value); | |
853 if (it == m_impl.end()) | |
854 return end(); | |
855 return makeIterator(*it); | |
856 } | |
857 | |
858 template <typename T, size_t inlineCapacity, typename U, typename V> | |
859 inline typename ListHashSet<T, inlineCapacity, U, V>::const_iterator | |
860 ListHashSet<T, inlineCapacity, U, V>::find(ValuePeekInType value) const { | |
861 ImplTypeConstIterator it = m_impl.template find<BaseTranslator>(value); | |
862 if (it == m_impl.end()) | |
863 return end(); | |
864 return makeConstIterator(*it); | |
865 } | |
866 | |
867 template <typename Translator> | |
868 struct ListHashSetTranslatorAdapter { | |
869 STATIC_ONLY(ListHashSetTranslatorAdapter); | |
870 template <typename T> | |
871 static unsigned hash(const T& key) { | |
872 return Translator::hash(key); | |
873 } | |
874 template <typename T, typename U> | |
875 static bool equal(const T& a, const U& b) { | |
876 return Translator::equal(a->m_value, b); | |
877 } | |
878 }; | |
879 | |
880 template <typename ValueType, size_t inlineCapacity, typename U, typename V> | |
881 template <typename HashTranslator, typename T> | |
882 inline typename ListHashSet<ValueType, inlineCapacity, U, V>::iterator | |
883 ListHashSet<ValueType, inlineCapacity, U, V>::find(const T& value) { | |
884 ImplTypeConstIterator it = | |
885 m_impl.template find<ListHashSetTranslatorAdapter<HashTranslator>>(value); | |
886 if (it == m_impl.end()) | |
887 return end(); | |
888 return makeIterator(*it); | |
889 } | |
890 | |
891 template <typename ValueType, size_t inlineCapacity, typename U, typename V> | |
892 template <typename HashTranslator, typename T> | |
893 inline typename ListHashSet<ValueType, inlineCapacity, U, V>::const_iterator | |
894 ListHashSet<ValueType, inlineCapacity, U, V>::find(const T& value) const { | |
895 ImplTypeConstIterator it = | |
896 m_impl.template find<ListHashSetTranslatorAdapter<HashTranslator>>(value); | |
897 if (it == m_impl.end()) | |
898 return end(); | |
899 return makeConstIterator(*it); | |
900 } | |
901 | |
902 template <typename ValueType, size_t inlineCapacity, typename U, typename V> | |
903 template <typename HashTranslator, typename T> | |
904 inline bool ListHashSet<ValueType, inlineCapacity, U, V>::contains( | |
905 const T& value) const { | |
906 return m_impl.template contains<ListHashSetTranslatorAdapter<HashTranslator>>( | |
907 value); | |
908 } | |
909 | |
910 template <typename T, size_t inlineCapacity, typename U, typename V> | |
911 inline bool ListHashSet<T, inlineCapacity, U, V>::contains( | |
912 ValuePeekInType value) const { | |
913 return m_impl.template contains<BaseTranslator>(value); | |
914 } | |
915 | |
916 template <typename T, size_t inlineCapacity, typename U, typename V> | |
917 template <typename IncomingValueType> | |
918 typename ListHashSet<T, inlineCapacity, U, V>::AddResult | |
919 ListHashSet<T, inlineCapacity, U, V>::insert(IncomingValueType&& value) { | |
920 createAllocatorIfNeeded(); | |
921 // The second argument is a const ref. This is useful for the HashTable | |
922 // because it lets it take lvalues by reference, but for our purposes it's | |
923 // inconvenient, since it constrains us to be const, whereas the allocator | |
924 // actually changes when it does allocations. | |
925 auto result = m_impl.template add<BaseTranslator>( | |
926 std::forward<IncomingValueType>(value), *this->getAllocator()); | |
927 if (result.isNewEntry) | |
928 appendNode(*result.storedValue); | |
929 return AddResult(*result.storedValue, result.isNewEntry); | |
930 } | |
931 | |
932 template <typename T, size_t inlineCapacity, typename U, typename V> | |
933 template <typename IncomingValueType> | |
934 typename ListHashSet<T, inlineCapacity, U, V>::iterator | |
935 ListHashSet<T, inlineCapacity, U, V>::addReturnIterator( | |
936 IncomingValueType&& value) { | |
937 return makeIterator(insert(std::forward<IncomingValueType>(value)).m_node); | |
938 } | |
939 | |
940 template <typename T, size_t inlineCapacity, typename U, typename V> | |
941 template <typename IncomingValueType> | |
942 typename ListHashSet<T, inlineCapacity, U, V>::AddResult | |
943 ListHashSet<T, inlineCapacity, U, V>::appendOrMoveToLast( | |
944 IncomingValueType&& value) { | |
945 createAllocatorIfNeeded(); | |
946 auto result = m_impl.template add<BaseTranslator>( | |
947 std::forward<IncomingValueType>(value), *this->getAllocator()); | |
948 Node* node = *result.storedValue; | |
949 if (!result.isNewEntry) | |
950 unlink(node); | |
951 appendNode(node); | |
952 return AddResult(*result.storedValue, result.isNewEntry); | |
953 } | |
954 | |
955 template <typename T, size_t inlineCapacity, typename U, typename V> | |
956 template <typename IncomingValueType> | |
957 typename ListHashSet<T, inlineCapacity, U, V>::AddResult | |
958 ListHashSet<T, inlineCapacity, U, V>::prependOrMoveToFirst( | |
959 IncomingValueType&& value) { | |
960 createAllocatorIfNeeded(); | |
961 auto result = m_impl.template add<BaseTranslator>( | |
962 std::forward<IncomingValueType>(value), *this->getAllocator()); | |
963 Node* node = *result.storedValue; | |
964 if (!result.isNewEntry) | |
965 unlink(node); | |
966 prependNode(node); | |
967 return AddResult(*result.storedValue, result.isNewEntry); | |
968 } | |
969 | |
970 template <typename T, size_t inlineCapacity, typename U, typename V> | |
971 template <typename IncomingValueType> | |
972 typename ListHashSet<T, inlineCapacity, U, V>::AddResult | |
973 ListHashSet<T, inlineCapacity, U, V>::insertBefore( | |
974 iterator it, | |
975 IncomingValueType&& newValue) { | |
976 createAllocatorIfNeeded(); | |
977 auto result = m_impl.template add<BaseTranslator>( | |
978 std::forward<IncomingValueType>(newValue), *this->getAllocator()); | |
979 if (result.isNewEntry) | |
980 insertNodeBefore(it.getNode(), *result.storedValue); | |
981 return AddResult(*result.storedValue, result.isNewEntry); | |
982 } | |
983 | |
984 template <typename T, size_t inlineCapacity, typename U, typename V> | |
985 template <typename IncomingValueType> | |
986 typename ListHashSet<T, inlineCapacity, U, V>::AddResult | |
987 ListHashSet<T, inlineCapacity, U, V>::insertBefore( | |
988 ValuePeekInType beforeValue, | |
989 IncomingValueType&& newValue) { | |
990 createAllocatorIfNeeded(); | |
991 return insertBefore(find(beforeValue), | |
992 std::forward<IncomingValueType>(newValue)); | |
993 } | |
994 | |
995 template <typename T, size_t inlineCapacity, typename U, typename V> | |
996 inline void ListHashSet<T, inlineCapacity, U, V>::erase(iterator it) { | |
997 if (it == end()) | |
998 return; | |
999 m_impl.remove(it.getNode()); | |
1000 unlinkAndDelete(it.getNode()); | |
1001 } | |
1002 | |
1003 template <typename T, size_t inlineCapacity, typename U, typename V> | |
1004 inline void ListHashSet<T, inlineCapacity, U, V>::clear() { | |
1005 deleteAllNodes(); | |
1006 m_impl.clear(); | |
1007 m_head = nullptr; | |
1008 m_tail = nullptr; | |
1009 } | |
1010 | |
1011 template <typename T, size_t inlineCapacity, typename U, typename V> | |
1012 auto ListHashSet<T, inlineCapacity, U, V>::take(iterator it) -> ValueType { | |
1013 if (it == end()) | |
1014 return ValueTraits::emptyValue(); | |
1015 | |
1016 m_impl.remove(it.getNode()); | |
1017 ValueType result = std::move(it.getNode()->m_value); | |
1018 unlinkAndDelete(it.getNode()); | |
1019 | |
1020 return result; | |
1021 } | |
1022 | |
1023 template <typename T, size_t inlineCapacity, typename U, typename V> | |
1024 auto ListHashSet<T, inlineCapacity, U, V>::take(ValuePeekInType value) | |
1025 -> ValueType { | |
1026 return take(find(value)); | |
1027 } | |
1028 | |
1029 template <typename T, size_t inlineCapacity, typename U, typename V> | |
1030 auto ListHashSet<T, inlineCapacity, U, V>::takeFirst() -> ValueType { | |
1031 DCHECK(!isEmpty()); | |
1032 m_impl.remove(m_head); | |
1033 ValueType result = std::move(m_head->m_value); | |
1034 unlinkAndDelete(m_head); | |
1035 | |
1036 return result; | |
1037 } | |
1038 | |
1039 template <typename T, size_t inlineCapacity, typename U, typename Allocator> | |
1040 void ListHashSet<T, inlineCapacity, U, Allocator>::unlink(Node* node) { | |
1041 if (!node->m_prev) { | |
1042 DCHECK_EQ(node, m_head); | |
1043 m_head = node->next(); | |
1044 } else { | |
1045 DCHECK_NE(node, m_head); | |
1046 node->m_prev->m_next = node->m_next; | |
1047 } | |
1048 | |
1049 if (!node->m_next) { | |
1050 DCHECK_EQ(node, m_tail); | |
1051 m_tail = node->prev(); | |
1052 } else { | |
1053 DCHECK_NE(node, m_tail); | |
1054 node->m_next->m_prev = node->m_prev; | |
1055 } | |
1056 } | |
1057 | |
1058 template <typename T, size_t inlineCapacity, typename U, typename V> | |
1059 void ListHashSet<T, inlineCapacity, U, V>::unlinkAndDelete(Node* node) { | |
1060 unlink(node); | |
1061 node->destroy(this->getAllocator()); | |
1062 } | |
1063 | |
1064 template <typename T, size_t inlineCapacity, typename U, typename V> | |
1065 void ListHashSet<T, inlineCapacity, U, V>::appendNode(Node* node) { | |
1066 node->m_prev = m_tail; | |
1067 node->m_next = nullptr; | |
1068 | |
1069 if (m_tail) { | |
1070 DCHECK(m_head); | |
1071 m_tail->m_next = node; | |
1072 } else { | |
1073 DCHECK(!m_head); | |
1074 m_head = node; | |
1075 } | |
1076 | |
1077 m_tail = node; | |
1078 } | |
1079 | |
1080 template <typename T, size_t inlineCapacity, typename U, typename V> | |
1081 void ListHashSet<T, inlineCapacity, U, V>::prependNode(Node* node) { | |
1082 node->m_prev = nullptr; | |
1083 node->m_next = m_head; | |
1084 | |
1085 if (m_head) | |
1086 m_head->m_prev = node; | |
1087 else | |
1088 m_tail = node; | |
1089 | |
1090 m_head = node; | |
1091 } | |
1092 | |
1093 template <typename T, size_t inlineCapacity, typename U, typename V> | |
1094 void ListHashSet<T, inlineCapacity, U, V>::insertNodeBefore(Node* beforeNode, | |
1095 Node* newNode) { | |
1096 if (!beforeNode) | |
1097 return appendNode(newNode); | |
1098 | |
1099 newNode->m_next = beforeNode; | |
1100 newNode->m_prev = beforeNode->m_prev; | |
1101 if (beforeNode->m_prev) | |
1102 beforeNode->m_prev->m_next = newNode; | |
1103 beforeNode->m_prev = newNode; | |
1104 | |
1105 if (!newNode->m_prev) | |
1106 m_head = newNode; | |
1107 } | |
1108 | |
1109 template <typename T, size_t inlineCapacity, typename U, typename V> | |
1110 void ListHashSet<T, inlineCapacity, U, V>::deleteAllNodes() { | |
1111 if (!m_head) | |
1112 return; | |
1113 | |
1114 for (Node *node = m_head, *next = m_head->next(); node; | |
1115 node = next, next = node ? node->next() : 0) | |
1116 node->destroy(this->getAllocator()); | |
1117 } | |
1118 | |
1119 template <typename T, size_t inlineCapacity, typename U, typename V> | |
1120 template <typename VisitorDispatcher> | |
1121 void ListHashSet<T, inlineCapacity, U, V>::trace(VisitorDispatcher visitor) { | |
1122 static_assert(HashTraits<T>::weakHandlingFlag == NoWeakHandlingInCollections, | |
1123 "HeapListHashSet does not support weakness, consider using " | |
1124 "HeapLinkedHashSet instead."); | |
1125 // This marks all the nodes and their contents live that can be accessed | |
1126 // through the HashTable. That includes m_head and m_tail so we do not have | |
1127 // to explicitly trace them here. | |
1128 m_impl.trace(visitor); | |
1129 } | |
1130 | |
1131 } // namespace WTF | |
1132 | |
1133 using WTF::ListHashSet; | |
1134 | |
1135 #endif // WTF_ListHashSet_h | |
OLD | NEW |