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_LinkedHashSet_h | 5 #include "platform/wtf/LinkedHashSet.h" |
24 #define WTF_LinkedHashSet_h | |
25 | 6 |
26 #include "wtf/AddressSanitizer.h" | 7 // The contents of this header was moved to platform/wtf as part of |
27 #include "wtf/HashSet.h" | 8 // WTF migration project. See the following post for details: |
28 #include "wtf/allocator/PartitionAllocator.h" | 9 // https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gY
CAAJ |
29 | |
30 namespace WTF { | |
31 | |
32 // LinkedHashSet: Just like HashSet, this class provides a Set | |
33 // interface - a collection of unique objects with O(1) insertion, | |
34 // removal and test for containership. However, it also has an | |
35 // order - iterating it will always give back values in the order | |
36 // in which they are added. | |
37 | |
38 // Unlike ListHashSet, but like most WTF collections, iteration is NOT safe | |
39 // against mutation of the LinkedHashSet. | |
40 | |
41 template <typename Value, | |
42 typename HashFunctions, | |
43 typename HashTraits, | |
44 typename Allocator> | |
45 class LinkedHashSet; | |
46 | |
47 template <typename LinkedHashSet> | |
48 class LinkedHashSetIterator; | |
49 template <typename LinkedHashSet> | |
50 class LinkedHashSetConstIterator; | |
51 template <typename LinkedHashSet> | |
52 class LinkedHashSetReverseIterator; | |
53 template <typename LinkedHashSet> | |
54 class LinkedHashSetConstReverseIterator; | |
55 | |
56 template <typename Value, typename HashFunctions, typename Allocator> | |
57 struct LinkedHashSetTranslator; | |
58 template <typename Value, typename Allocator> | |
59 struct LinkedHashSetExtractor; | |
60 template <typename Value, typename ValueTraits, typename Allocator> | |
61 struct LinkedHashSetTraits; | |
62 | |
63 class LinkedHashSetNodeBase { | |
64 DISALLOW_NEW(); | |
65 | |
66 public: | |
67 LinkedHashSetNodeBase() : m_prev(this), m_next(this) {} | |
68 | |
69 NO_SANITIZE_ADDRESS | |
70 void unlink() { | |
71 if (!m_next) | |
72 return; | |
73 DCHECK(m_prev); | |
74 DCHECK(m_next->m_prev == this); | |
75 DCHECK(m_prev->m_next == this); | |
76 m_next->m_prev = m_prev; | |
77 m_prev->m_next = m_next; | |
78 } | |
79 | |
80 ~LinkedHashSetNodeBase() { unlink(); } | |
81 | |
82 void insertBefore(LinkedHashSetNodeBase& other) { | |
83 other.m_next = this; | |
84 other.m_prev = m_prev; | |
85 m_prev->m_next = &other; | |
86 m_prev = &other; | |
87 DCHECK(other.m_next); | |
88 DCHECK(other.m_prev); | |
89 DCHECK(m_next); | |
90 DCHECK(m_prev); | |
91 } | |
92 | |
93 void insertAfter(LinkedHashSetNodeBase& other) { | |
94 other.m_prev = this; | |
95 other.m_next = m_next; | |
96 m_next->m_prev = &other; | |
97 m_next = &other; | |
98 DCHECK(other.m_next); | |
99 DCHECK(other.m_prev); | |
100 DCHECK(m_next); | |
101 DCHECK(m_prev); | |
102 } | |
103 | |
104 LinkedHashSetNodeBase(LinkedHashSetNodeBase* prev, | |
105 LinkedHashSetNodeBase* next) | |
106 : m_prev(prev), m_next(next) { | |
107 DCHECK((prev && next) || (!prev && !next)); | |
108 } | |
109 | |
110 LinkedHashSetNodeBase* m_prev; | |
111 LinkedHashSetNodeBase* m_next; | |
112 | |
113 protected: | |
114 // If we take a copy of a node we can't copy the next and prev pointers, | |
115 // since they point to something that does not point at us. This is used | |
116 // inside the shouldExpand() "if" in HashTable::add. | |
117 LinkedHashSetNodeBase(const LinkedHashSetNodeBase& other) | |
118 : m_prev(0), m_next(0) {} | |
119 | |
120 LinkedHashSetNodeBase(LinkedHashSetNodeBase&& other) | |
121 : m_prev(other.m_prev), m_next(other.m_next) { | |
122 other.m_prev = nullptr; | |
123 other.m_next = nullptr; | |
124 if (m_next) { | |
125 m_prev->m_next = this; | |
126 m_next->m_prev = this; | |
127 } | |
128 } | |
129 | |
130 private: | |
131 // Should not be used. | |
132 LinkedHashSetNodeBase& operator=(const LinkedHashSetNodeBase& other); | |
133 }; | |
134 | |
135 template <typename ValueArg, typename Allocator> | |
136 class LinkedHashSetNode : public LinkedHashSetNodeBase { | |
137 DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); | |
138 | |
139 public: | |
140 LinkedHashSetNode(const ValueArg& value, | |
141 LinkedHashSetNodeBase* prev, | |
142 LinkedHashSetNodeBase* next) | |
143 : LinkedHashSetNodeBase(prev, next), m_value(value) {} | |
144 | |
145 LinkedHashSetNode(LinkedHashSetNode&& other) | |
146 : LinkedHashSetNodeBase(std::move(other)), | |
147 m_value(std::move(other.m_value)) {} | |
148 | |
149 ValueArg m_value; | |
150 | |
151 private: | |
152 WTF_MAKE_NONCOPYABLE(LinkedHashSetNode); | |
153 }; | |
154 | |
155 template <typename ValueArg, | |
156 typename HashFunctions = typename DefaultHash<ValueArg>::Hash, | |
157 typename TraitsArg = HashTraits<ValueArg>, | |
158 typename Allocator = PartitionAllocator> | |
159 class LinkedHashSet { | |
160 USE_ALLOCATOR(LinkedHashSet, Allocator); | |
161 | |
162 private: | |
163 typedef ValueArg Value; | |
164 typedef TraitsArg Traits; | |
165 typedef LinkedHashSetNode<Value, Allocator> Node; | |
166 typedef LinkedHashSetNodeBase NodeBase; | |
167 typedef LinkedHashSetTranslator<Value, HashFunctions, Allocator> | |
168 NodeHashFunctions; | |
169 typedef LinkedHashSetTraits<Value, Traits, Allocator> NodeHashTraits; | |
170 | |
171 typedef HashTable<Node, | |
172 Node, | |
173 IdentityExtractor, | |
174 NodeHashFunctions, | |
175 NodeHashTraits, | |
176 NodeHashTraits, | |
177 Allocator> | |
178 ImplType; | |
179 | |
180 public: | |
181 typedef LinkedHashSetIterator<LinkedHashSet> iterator; | |
182 friend class LinkedHashSetIterator<LinkedHashSet>; | |
183 typedef LinkedHashSetConstIterator<LinkedHashSet> const_iterator; | |
184 friend class LinkedHashSetConstIterator<LinkedHashSet>; | |
185 | |
186 typedef LinkedHashSetReverseIterator<LinkedHashSet> reverse_iterator; | |
187 friend class LinkedHashSetReverseIterator<LinkedHashSet>; | |
188 typedef LinkedHashSetConstReverseIterator<LinkedHashSet> | |
189 const_reverse_iterator; | |
190 friend class LinkedHashSetConstReverseIterator<LinkedHashSet>; | |
191 | |
192 struct AddResult final { | |
193 STACK_ALLOCATED(); | |
194 AddResult(const typename ImplType::AddResult& hashTableAddResult) | |
195 : storedValue(&hashTableAddResult.storedValue->m_value), | |
196 isNewEntry(hashTableAddResult.isNewEntry) {} | |
197 | |
198 Value* storedValue; | |
199 bool isNewEntry; | |
200 }; | |
201 | |
202 typedef typename HashTraits<Value>::PeekInType ValuePeekInType; | |
203 | |
204 LinkedHashSet(); | |
205 LinkedHashSet(const LinkedHashSet&); | |
206 LinkedHashSet(LinkedHashSet&&); | |
207 LinkedHashSet& operator=(const LinkedHashSet&); | |
208 LinkedHashSet& operator=(LinkedHashSet&&); | |
209 | |
210 // Needs finalization. The anchor needs to unlink itself from the chain. | |
211 ~LinkedHashSet(); | |
212 | |
213 static void finalize(void* pointer) { | |
214 reinterpret_cast<LinkedHashSet*>(pointer)->~LinkedHashSet(); | |
215 } | |
216 void finalizeGarbageCollectedObject() { finalize(this); } | |
217 | |
218 void swap(LinkedHashSet&); | |
219 | |
220 unsigned size() const { return m_impl.size(); } | |
221 unsigned capacity() const { return m_impl.capacity(); } | |
222 bool isEmpty() const { return m_impl.isEmpty(); } | |
223 | |
224 iterator begin() { return makeIterator(firstNode()); } | |
225 iterator end() { return makeIterator(anchor()); } | |
226 const_iterator begin() const { return makeConstIterator(firstNode()); } | |
227 const_iterator end() const { return makeConstIterator(anchor()); } | |
228 | |
229 reverse_iterator rbegin() { return makeReverseIterator(lastNode()); } | |
230 reverse_iterator rend() { return makeReverseIterator(anchor()); } | |
231 const_reverse_iterator rbegin() const { | |
232 return makeConstReverseIterator(lastNode()); | |
233 } | |
234 const_reverse_iterator rend() const { | |
235 return makeConstReverseIterator(anchor()); | |
236 } | |
237 | |
238 Value& front(); | |
239 const Value& front() const; | |
240 void removeFirst(); | |
241 | |
242 Value& back(); | |
243 const Value& back() const; | |
244 void pop_back(); | |
245 | |
246 iterator find(ValuePeekInType); | |
247 const_iterator find(ValuePeekInType) const; | |
248 bool contains(ValuePeekInType) const; | |
249 | |
250 // An alternate version of find() that finds the object by hashing and | |
251 // comparing with some other type, to avoid the cost of type conversion. | |
252 // The HashTranslator interface is defined in HashSet. | |
253 template <typename HashTranslator, typename T> | |
254 iterator find(const T&); | |
255 template <typename HashTranslator, typename T> | |
256 const_iterator find(const T&) const; | |
257 template <typename HashTranslator, typename T> | |
258 bool contains(const T&) const; | |
259 | |
260 // The return value of insert is a pair of a pointer to the stored value, | |
261 // and a bool that is true if an new entry was added. | |
262 template <typename IncomingValueType> | |
263 AddResult insert(IncomingValueType&&); | |
264 | |
265 // Same as insert() except that the return value is an | |
266 // iterator. Useful in cases where it's needed to have the | |
267 // same return value as find() and where it's not possible to | |
268 // use a pointer to the storedValue. | |
269 template <typename IncomingValueType> | |
270 iterator addReturnIterator(IncomingValueType&&); | |
271 | |
272 // Add the value to the end of the collection. If the value was already in | |
273 // the list, it is moved to the end. | |
274 template <typename IncomingValueType> | |
275 AddResult appendOrMoveToLast(IncomingValueType&&); | |
276 | |
277 // Add the value to the beginning of the collection. If the value was already | |
278 // in the list, it is moved to the beginning. | |
279 template <typename IncomingValueType> | |
280 AddResult prependOrMoveToFirst(IncomingValueType&&); | |
281 | |
282 template <typename IncomingValueType> | |
283 AddResult insertBefore(ValuePeekInType beforeValue, | |
284 IncomingValueType&& newValue); | |
285 template <typename IncomingValueType> | |
286 AddResult insertBefore(iterator it, IncomingValueType&& newValue) { | |
287 return m_impl.template add<NodeHashFunctions>( | |
288 std::forward<IncomingValueType>(newValue), it.getNode()); | |
289 } | |
290 | |
291 void erase(ValuePeekInType); | |
292 void erase(iterator); | |
293 void clear() { m_impl.clear(); } | |
294 template <typename Collection> | |
295 void removeAll(const Collection& other) { | |
296 WTF::removeAll(*this, other); | |
297 } | |
298 | |
299 template <typename VisitorDispatcher> | |
300 void trace(VisitorDispatcher visitor) { | |
301 m_impl.trace(visitor); | |
302 // Should the underlying table be moved by GC, register a callback | |
303 // that fixes up the interior pointers that the (Heap)LinkedHashSet keeps. | |
304 if (m_impl.m_table) { | |
305 Allocator::registerBackingStoreCallback( | |
306 visitor, m_impl.m_table, moveBackingCallback, | |
307 reinterpret_cast<void*>(&m_anchor)); | |
308 } | |
309 } | |
310 | |
311 int64_t modifications() const { return m_impl.modifications(); } | |
312 void checkModifications(int64_t mods) const { | |
313 m_impl.checkModifications(mods); | |
314 } | |
315 | |
316 private: | |
317 Node* anchor() { return reinterpret_cast<Node*>(&m_anchor); } | |
318 const Node* anchor() const { | |
319 return reinterpret_cast<const Node*>(&m_anchor); | |
320 } | |
321 Node* firstNode() { return reinterpret_cast<Node*>(m_anchor.m_next); } | |
322 const Node* firstNode() const { | |
323 return reinterpret_cast<const Node*>(m_anchor.m_next); | |
324 } | |
325 Node* lastNode() { return reinterpret_cast<Node*>(m_anchor.m_prev); } | |
326 const Node* lastNode() const { | |
327 return reinterpret_cast<const Node*>(m_anchor.m_prev); | |
328 } | |
329 | |
330 iterator makeIterator(const Node* position) { | |
331 return iterator(position, this); | |
332 } | |
333 const_iterator makeConstIterator(const Node* position) const { | |
334 return const_iterator(position, this); | |
335 } | |
336 reverse_iterator makeReverseIterator(const Node* position) { | |
337 return reverse_iterator(position, this); | |
338 } | |
339 const_reverse_iterator makeConstReverseIterator(const Node* position) const { | |
340 return const_reverse_iterator(position, this); | |
341 } | |
342 | |
343 static void moveBackingCallback(void* anchor, | |
344 void* from, | |
345 void* to, | |
346 size_t size) { | |
347 // Note: the hash table move may have been overlapping; linearly scan the | |
348 // entire table and fixup interior pointers into the old region with | |
349 // correspondingly offset ones into the new. | |
350 size_t tableSize = size / sizeof(Node); | |
351 Node* table = reinterpret_cast<Node*>(to); | |
352 NodeBase* fromStart = reinterpret_cast<NodeBase*>(from); | |
353 NodeBase* fromEnd = | |
354 reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(from) + size); | |
355 for (Node* element = table + tableSize - 1; element >= table; element--) { | |
356 Node& node = *element; | |
357 if (ImplType::isEmptyOrDeletedBucket(node)) | |
358 continue; | |
359 if (node.m_next >= fromStart && node.m_next < fromEnd) { | |
360 size_t diff = reinterpret_cast<uintptr_t>(node.m_next) - | |
361 reinterpret_cast<uintptr_t>(from); | |
362 node.m_next = | |
363 reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff); | |
364 } | |
365 if (node.m_prev >= fromStart && node.m_prev < fromEnd) { | |
366 size_t diff = reinterpret_cast<uintptr_t>(node.m_prev) - | |
367 reinterpret_cast<uintptr_t>(from); | |
368 node.m_prev = | |
369 reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff); | |
370 } | |
371 } | |
372 NodeBase* anchorNode = reinterpret_cast<NodeBase*>(anchor); | |
373 if (anchorNode->m_next >= fromStart && anchorNode->m_next < fromEnd) { | |
374 size_t diff = reinterpret_cast<uintptr_t>(anchorNode->m_next) - | |
375 reinterpret_cast<uintptr_t>(from); | |
376 anchorNode->m_next = | |
377 reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff); | |
378 } | |
379 if (anchorNode->m_prev >= fromStart && anchorNode->m_prev < fromEnd) { | |
380 size_t diff = reinterpret_cast<uintptr_t>(anchorNode->m_prev) - | |
381 reinterpret_cast<uintptr_t>(from); | |
382 anchorNode->m_prev = | |
383 reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff); | |
384 } | |
385 } | |
386 | |
387 ImplType m_impl; | |
388 NodeBase m_anchor; | |
389 }; | |
390 | |
391 template <typename Value, typename HashFunctions, typename Allocator> | |
392 struct LinkedHashSetTranslator { | |
393 STATIC_ONLY(LinkedHashSetTranslator); | |
394 typedef LinkedHashSetNode<Value, Allocator> Node; | |
395 typedef LinkedHashSetNodeBase NodeBase; | |
396 typedef typename HashTraits<Value>::PeekInType ValuePeekInType; | |
397 static unsigned hash(const Node& node) { | |
398 return HashFunctions::hash(node.m_value); | |
399 } | |
400 static unsigned hash(const ValuePeekInType& key) { | |
401 return HashFunctions::hash(key); | |
402 } | |
403 static bool equal(const Node& a, const ValuePeekInType& b) { | |
404 return HashFunctions::equal(a.m_value, b); | |
405 } | |
406 static bool equal(const Node& a, const Node& b) { | |
407 return HashFunctions::equal(a.m_value, b.m_value); | |
408 } | |
409 template <typename IncomingValueType> | |
410 static void translate(Node& location, | |
411 IncomingValueType&& key, | |
412 NodeBase* anchor) { | |
413 anchor->insertBefore(location); | |
414 location.m_value = std::forward<IncomingValueType>(key); | |
415 } | |
416 | |
417 // Empty (or deleted) slots have the m_next pointer set to null, but we | |
418 // don't do anything to the other fields, which may contain junk. | |
419 // Therefore you can't compare a newly constructed empty value with a | |
420 // slot and get the right answer. | |
421 static const bool safeToCompareToEmptyOrDeleted = false; | |
422 }; | |
423 | |
424 template <typename Value, typename Allocator> | |
425 struct LinkedHashSetExtractor { | |
426 STATIC_ONLY(LinkedHashSetExtractor); | |
427 static const Value& extract(const LinkedHashSetNode<Value, Allocator>& node) { | |
428 return node.m_value; | |
429 } | |
430 }; | |
431 | |
432 template <typename Value, typename ValueTraitsArg, typename Allocator> | |
433 struct LinkedHashSetTraits | |
434 : public SimpleClassHashTraits<LinkedHashSetNode<Value, Allocator>> { | |
435 STATIC_ONLY(LinkedHashSetTraits); | |
436 typedef LinkedHashSetNode<Value, Allocator> Node; | |
437 typedef ValueTraitsArg ValueTraits; | |
438 | |
439 // The slot is empty when the m_next field is zero so it's safe to zero | |
440 // the backing. | |
441 static const bool emptyValueIsZero = true; | |
442 | |
443 static const bool hasIsEmptyValueFunction = true; | |
444 static bool isEmptyValue(const Node& node) { return !node.m_next; } | |
445 | |
446 static const int deletedValue = -1; | |
447 | |
448 static void constructDeletedValue(Node& slot, bool) { | |
449 slot.m_next = reinterpret_cast<Node*>(deletedValue); | |
450 } | |
451 static bool isDeletedValue(const Node& slot) { | |
452 return slot.m_next == reinterpret_cast<Node*>(deletedValue); | |
453 } | |
454 | |
455 // Whether we need to trace and do weak processing depends on the traits of | |
456 // the type inside the node. | |
457 template <typename U = void> | |
458 struct IsTraceableInCollection { | |
459 STATIC_ONLY(IsTraceableInCollection); | |
460 static const bool value = | |
461 ValueTraits::template IsTraceableInCollection<>::value; | |
462 }; | |
463 static const WeakHandlingFlag weakHandlingFlag = | |
464 ValueTraits::weakHandlingFlag; | |
465 }; | |
466 | |
467 template <typename LinkedHashSetType> | |
468 class LinkedHashSetIterator { | |
469 DISALLOW_NEW(); | |
470 | |
471 private: | |
472 typedef typename LinkedHashSetType::Node Node; | |
473 typedef typename LinkedHashSetType::Traits Traits; | |
474 | |
475 typedef typename LinkedHashSetType::Value& ReferenceType; | |
476 typedef typename LinkedHashSetType::Value* PointerType; | |
477 | |
478 typedef LinkedHashSetConstIterator<LinkedHashSetType> const_iterator; | |
479 | |
480 Node* getNode() { return const_cast<Node*>(m_iterator.getNode()); } | |
481 | |
482 protected: | |
483 LinkedHashSetIterator(const Node* position, LinkedHashSetType* m_container) | |
484 : m_iterator(position, m_container) {} | |
485 | |
486 public: | |
487 // Default copy, assignment and destructor are OK. | |
488 | |
489 PointerType get() const { return const_cast<PointerType>(m_iterator.get()); } | |
490 ReferenceType operator*() const { return *get(); } | |
491 PointerType operator->() const { return get(); } | |
492 | |
493 LinkedHashSetIterator& operator++() { | |
494 ++m_iterator; | |
495 return *this; | |
496 } | |
497 LinkedHashSetIterator& operator--() { | |
498 --m_iterator; | |
499 return *this; | |
500 } | |
501 | |
502 // Postfix ++ and -- intentionally omitted. | |
503 | |
504 // Comparison. | |
505 bool operator==(const LinkedHashSetIterator& other) const { | |
506 return m_iterator == other.m_iterator; | |
507 } | |
508 bool operator!=(const LinkedHashSetIterator& other) const { | |
509 return m_iterator != other.m_iterator; | |
510 } | |
511 | |
512 operator const_iterator() const { return m_iterator; } | |
513 | |
514 protected: | |
515 const_iterator m_iterator; | |
516 template <typename T, typename U, typename V, typename W> | |
517 friend class LinkedHashSet; | |
518 }; | |
519 | |
520 template <typename LinkedHashSetType> | |
521 class LinkedHashSetConstIterator { | |
522 DISALLOW_NEW(); | |
523 | |
524 private: | |
525 typedef typename LinkedHashSetType::Node Node; | |
526 typedef typename LinkedHashSetType::Traits Traits; | |
527 | |
528 typedef const typename LinkedHashSetType::Value& ReferenceType; | |
529 typedef const typename LinkedHashSetType::Value* PointerType; | |
530 | |
531 const Node* getNode() const { return static_cast<const Node*>(m_position); } | |
532 | |
533 protected: | |
534 LinkedHashSetConstIterator(const LinkedHashSetNodeBase* position, | |
535 const LinkedHashSetType* container) | |
536 : m_position(position) | |
537 #if DCHECK_IS_ON() | |
538 , | |
539 m_container(container), | |
540 m_containerModifications(container->modifications()) | |
541 #endif | |
542 { | |
543 } | |
544 | |
545 public: | |
546 PointerType get() const { | |
547 checkModifications(); | |
548 return &static_cast<const Node*>(m_position)->m_value; | |
549 } | |
550 ReferenceType operator*() const { return *get(); } | |
551 PointerType operator->() const { return get(); } | |
552 | |
553 LinkedHashSetConstIterator& operator++() { | |
554 DCHECK(m_position); | |
555 checkModifications(); | |
556 m_position = m_position->m_next; | |
557 return *this; | |
558 } | |
559 | |
560 LinkedHashSetConstIterator& operator--() { | |
561 DCHECK(m_position); | |
562 checkModifications(); | |
563 m_position = m_position->m_prev; | |
564 return *this; | |
565 } | |
566 | |
567 // Postfix ++ and -- intentionally omitted. | |
568 | |
569 // Comparison. | |
570 bool operator==(const LinkedHashSetConstIterator& other) const { | |
571 return m_position == other.m_position; | |
572 } | |
573 bool operator!=(const LinkedHashSetConstIterator& other) const { | |
574 return m_position != other.m_position; | |
575 } | |
576 | |
577 private: | |
578 const LinkedHashSetNodeBase* m_position; | |
579 #if DCHECK_IS_ON() | |
580 void checkModifications() const { | |
581 m_container->checkModifications(m_containerModifications); | |
582 } | |
583 const LinkedHashSetType* m_container; | |
584 int64_t m_containerModifications; | |
585 #else | |
586 void checkModifications() const {} | |
587 #endif | |
588 template <typename T, typename U, typename V, typename W> | |
589 friend class LinkedHashSet; | |
590 friend class LinkedHashSetIterator<LinkedHashSetType>; | |
591 }; | |
592 | |
593 template <typename LinkedHashSetType> | |
594 class LinkedHashSetReverseIterator | |
595 : public LinkedHashSetIterator<LinkedHashSetType> { | |
596 typedef LinkedHashSetIterator<LinkedHashSetType> Superclass; | |
597 typedef LinkedHashSetConstReverseIterator<LinkedHashSetType> | |
598 const_reverse_iterator; | |
599 typedef typename LinkedHashSetType::Node Node; | |
600 | |
601 protected: | |
602 LinkedHashSetReverseIterator(const Node* position, | |
603 LinkedHashSetType* container) | |
604 : Superclass(position, container) {} | |
605 | |
606 public: | |
607 LinkedHashSetReverseIterator& operator++() { | |
608 Superclass::operator--(); | |
609 return *this; | |
610 } | |
611 LinkedHashSetReverseIterator& operator--() { | |
612 Superclass::operator++(); | |
613 return *this; | |
614 } | |
615 | |
616 // Postfix ++ and -- intentionally omitted. | |
617 | |
618 operator const_reverse_iterator() const { | |
619 return *reinterpret_cast<const_reverse_iterator*>(this); | |
620 } | |
621 | |
622 template <typename T, typename U, typename V, typename W> | |
623 friend class LinkedHashSet; | |
624 }; | |
625 | |
626 template <typename LinkedHashSetType> | |
627 class LinkedHashSetConstReverseIterator | |
628 : public LinkedHashSetConstIterator<LinkedHashSetType> { | |
629 typedef LinkedHashSetConstIterator<LinkedHashSetType> Superclass; | |
630 typedef typename LinkedHashSetType::Node Node; | |
631 | |
632 public: | |
633 LinkedHashSetConstReverseIterator(const Node* position, | |
634 const LinkedHashSetType* container) | |
635 : Superclass(position, container) {} | |
636 | |
637 LinkedHashSetConstReverseIterator& operator++() { | |
638 Superclass::operator--(); | |
639 return *this; | |
640 } | |
641 LinkedHashSetConstReverseIterator& operator--() { | |
642 Superclass::operator++(); | |
643 return *this; | |
644 } | |
645 | |
646 // Postfix ++ and -- intentionally omitted. | |
647 | |
648 template <typename T, typename U, typename V, typename W> | |
649 friend class LinkedHashSet; | |
650 }; | |
651 | |
652 template <typename T, typename U, typename V, typename Allocator> | |
653 inline LinkedHashSet<T, U, V, Allocator>::LinkedHashSet() { | |
654 static_assert( | |
655 Allocator::isGarbageCollected || | |
656 !IsPointerToGarbageCollectedType<T>::value, | |
657 "Cannot put raw pointers to garbage-collected classes into " | |
658 "an off-heap LinkedHashSet. Use HeapLinkedHashSet<Member<T>> instead."); | |
659 } | |
660 | |
661 template <typename T, typename U, typename V, typename W> | |
662 inline LinkedHashSet<T, U, V, W>::LinkedHashSet(const LinkedHashSet& other) | |
663 : m_anchor() { | |
664 const_iterator end = other.end(); | |
665 for (const_iterator it = other.begin(); it != end; ++it) | |
666 insert(*it); | |
667 } | |
668 | |
669 template <typename T, typename U, typename V, typename W> | |
670 inline LinkedHashSet<T, U, V, W>::LinkedHashSet(LinkedHashSet&& other) | |
671 : m_anchor() { | |
672 swap(other); | |
673 } | |
674 | |
675 template <typename T, typename U, typename V, typename W> | |
676 inline LinkedHashSet<T, U, V, W>& LinkedHashSet<T, U, V, W>::operator=( | |
677 const LinkedHashSet& other) { | |
678 LinkedHashSet tmp(other); | |
679 swap(tmp); | |
680 return *this; | |
681 } | |
682 | |
683 template <typename T, typename U, typename V, typename W> | |
684 inline LinkedHashSet<T, U, V, W>& LinkedHashSet<T, U, V, W>::operator=( | |
685 LinkedHashSet&& other) { | |
686 swap(other); | |
687 return *this; | |
688 } | |
689 | |
690 template <typename T, typename U, typename V, typename W> | |
691 inline void LinkedHashSet<T, U, V, W>::swap(LinkedHashSet& other) { | |
692 m_impl.swap(other.m_impl); | |
693 swapAnchor(m_anchor, other.m_anchor); | |
694 } | |
695 | |
696 template <typename T, typename U, typename V, typename Allocator> | |
697 inline LinkedHashSet<T, U, V, Allocator>::~LinkedHashSet() { | |
698 // The destructor of m_anchor will implicitly be called here, which will | |
699 // unlink the anchor from the collection. | |
700 } | |
701 | |
702 template <typename T, typename U, typename V, typename W> | |
703 inline T& LinkedHashSet<T, U, V, W>::front() { | |
704 DCHECK(!isEmpty()); | |
705 return firstNode()->m_value; | |
706 } | |
707 | |
708 template <typename T, typename U, typename V, typename W> | |
709 inline const T& LinkedHashSet<T, U, V, W>::front() const { | |
710 DCHECK(!isEmpty()); | |
711 return firstNode()->m_value; | |
712 } | |
713 | |
714 template <typename T, typename U, typename V, typename W> | |
715 inline void LinkedHashSet<T, U, V, W>::removeFirst() { | |
716 DCHECK(!isEmpty()); | |
717 m_impl.remove(static_cast<Node*>(m_anchor.m_next)); | |
718 } | |
719 | |
720 template <typename T, typename U, typename V, typename W> | |
721 inline T& LinkedHashSet<T, U, V, W>::back() { | |
722 DCHECK(!isEmpty()); | |
723 return lastNode()->m_value; | |
724 } | |
725 | |
726 template <typename T, typename U, typename V, typename W> | |
727 inline const T& LinkedHashSet<T, U, V, W>::back() const { | |
728 DCHECK(!isEmpty()); | |
729 return lastNode()->m_value; | |
730 } | |
731 | |
732 template <typename T, typename U, typename V, typename W> | |
733 inline void LinkedHashSet<T, U, V, W>::pop_back() { | |
734 DCHECK(!isEmpty()); | |
735 m_impl.remove(static_cast<Node*>(m_anchor.m_prev)); | |
736 } | |
737 | |
738 template <typename T, typename U, typename V, typename W> | |
739 inline typename LinkedHashSet<T, U, V, W>::iterator | |
740 LinkedHashSet<T, U, V, W>::find(ValuePeekInType value) { | |
741 LinkedHashSet::Node* node = | |
742 m_impl.template lookup<LinkedHashSet::NodeHashFunctions, ValuePeekInType>( | |
743 value); | |
744 if (!node) | |
745 return end(); | |
746 return makeIterator(node); | |
747 } | |
748 | |
749 template <typename T, typename U, typename V, typename W> | |
750 inline typename LinkedHashSet<T, U, V, W>::const_iterator | |
751 LinkedHashSet<T, U, V, W>::find(ValuePeekInType value) const { | |
752 const LinkedHashSet::Node* node = | |
753 m_impl.template lookup<LinkedHashSet::NodeHashFunctions, ValuePeekInType>( | |
754 value); | |
755 if (!node) | |
756 return end(); | |
757 return makeConstIterator(node); | |
758 } | |
759 | |
760 template <typename Translator> | |
761 struct LinkedHashSetTranslatorAdapter { | |
762 STATIC_ONLY(LinkedHashSetTranslatorAdapter); | |
763 template <typename T> | |
764 static unsigned hash(const T& key) { | |
765 return Translator::hash(key); | |
766 } | |
767 template <typename T, typename U> | |
768 static bool equal(const T& a, const U& b) { | |
769 return Translator::equal(a.m_value, b); | |
770 } | |
771 }; | |
772 | |
773 template <typename Value, typename U, typename V, typename W> | |
774 template <typename HashTranslator, typename T> | |
775 inline typename LinkedHashSet<Value, U, V, W>::iterator | |
776 LinkedHashSet<Value, U, V, W>::find(const T& value) { | |
777 typedef LinkedHashSetTranslatorAdapter<HashTranslator> TranslatedFunctions; | |
778 const LinkedHashSet::Node* node = | |
779 m_impl.template lookup<TranslatedFunctions, const T&>(value); | |
780 if (!node) | |
781 return end(); | |
782 return makeIterator(node); | |
783 } | |
784 | |
785 template <typename Value, typename U, typename V, typename W> | |
786 template <typename HashTranslator, typename T> | |
787 inline typename LinkedHashSet<Value, U, V, W>::const_iterator | |
788 LinkedHashSet<Value, U, V, W>::find(const T& value) const { | |
789 typedef LinkedHashSetTranslatorAdapter<HashTranslator> TranslatedFunctions; | |
790 const LinkedHashSet::Node* node = | |
791 m_impl.template lookup<TranslatedFunctions, const T&>(value); | |
792 if (!node) | |
793 return end(); | |
794 return makeConstIterator(node); | |
795 } | |
796 | |
797 template <typename Value, typename U, typename V, typename W> | |
798 template <typename HashTranslator, typename T> | |
799 inline bool LinkedHashSet<Value, U, V, W>::contains(const T& value) const { | |
800 return m_impl | |
801 .template contains<LinkedHashSetTranslatorAdapter<HashTranslator>>(value); | |
802 } | |
803 | |
804 template <typename T, typename U, typename V, typename W> | |
805 inline bool LinkedHashSet<T, U, V, W>::contains(ValuePeekInType value) const { | |
806 return m_impl.template contains<NodeHashFunctions>(value); | |
807 } | |
808 | |
809 template <typename Value, | |
810 typename HashFunctions, | |
811 typename Traits, | |
812 typename Allocator> | |
813 template <typename IncomingValueType> | |
814 typename LinkedHashSet<Value, HashFunctions, Traits, Allocator>::AddResult | |
815 LinkedHashSet<Value, HashFunctions, Traits, Allocator>::insert( | |
816 IncomingValueType&& value) { | |
817 return m_impl.template add<NodeHashFunctions>( | |
818 std::forward<IncomingValueType>(value), &m_anchor); | |
819 } | |
820 | |
821 template <typename T, typename U, typename V, typename W> | |
822 template <typename IncomingValueType> | |
823 typename LinkedHashSet<T, U, V, W>::iterator | |
824 LinkedHashSet<T, U, V, W>::addReturnIterator(IncomingValueType&& value) { | |
825 typename ImplType::AddResult result = m_impl.template add<NodeHashFunctions>( | |
826 std::forward<IncomingValueType>(value), &m_anchor); | |
827 return makeIterator(result.storedValue); | |
828 } | |
829 | |
830 template <typename T, typename U, typename V, typename W> | |
831 template <typename IncomingValueType> | |
832 typename LinkedHashSet<T, U, V, W>::AddResult | |
833 LinkedHashSet<T, U, V, W>::appendOrMoveToLast(IncomingValueType&& value) { | |
834 typename ImplType::AddResult result = m_impl.template add<NodeHashFunctions>( | |
835 std::forward<IncomingValueType>(value), &m_anchor); | |
836 Node* node = result.storedValue; | |
837 if (!result.isNewEntry) { | |
838 node->unlink(); | |
839 m_anchor.insertBefore(*node); | |
840 } | |
841 return result; | |
842 } | |
843 | |
844 template <typename T, typename U, typename V, typename W> | |
845 template <typename IncomingValueType> | |
846 typename LinkedHashSet<T, U, V, W>::AddResult | |
847 LinkedHashSet<T, U, V, W>::prependOrMoveToFirst(IncomingValueType&& value) { | |
848 typename ImplType::AddResult result = m_impl.template add<NodeHashFunctions>( | |
849 std::forward<IncomingValueType>(value), m_anchor.m_next); | |
850 Node* node = result.storedValue; | |
851 if (!result.isNewEntry) { | |
852 node->unlink(); | |
853 m_anchor.insertAfter(*node); | |
854 } | |
855 return result; | |
856 } | |
857 | |
858 template <typename T, typename U, typename V, typename W> | |
859 template <typename IncomingValueType> | |
860 typename LinkedHashSet<T, U, V, W>::AddResult | |
861 LinkedHashSet<T, U, V, W>::insertBefore(ValuePeekInType beforeValue, | |
862 IncomingValueType&& newValue) { | |
863 return insertBefore(find(beforeValue), | |
864 std::forward<IncomingValueType>(newValue)); | |
865 } | |
866 | |
867 template <typename T, typename U, typename V, typename W> | |
868 inline void LinkedHashSet<T, U, V, W>::erase(iterator it) { | |
869 if (it == end()) | |
870 return; | |
871 m_impl.remove(it.getNode()); | |
872 } | |
873 | |
874 template <typename T, typename U, typename V, typename W> | |
875 inline void LinkedHashSet<T, U, V, W>::erase(ValuePeekInType value) { | |
876 erase(find(value)); | |
877 } | |
878 | |
879 inline void swapAnchor(LinkedHashSetNodeBase& a, LinkedHashSetNodeBase& b) { | |
880 DCHECK(a.m_prev); | |
881 DCHECK(a.m_next); | |
882 DCHECK(b.m_prev); | |
883 DCHECK(b.m_next); | |
884 swap(a.m_prev, b.m_prev); | |
885 swap(a.m_next, b.m_next); | |
886 if (b.m_next == &a) { | |
887 DCHECK_EQ(b.m_prev, &a); | |
888 b.m_next = &b; | |
889 b.m_prev = &b; | |
890 } else { | |
891 b.m_next->m_prev = &b; | |
892 b.m_prev->m_next = &b; | |
893 } | |
894 if (a.m_next == &b) { | |
895 DCHECK_EQ(a.m_prev, &b); | |
896 a.m_next = &a; | |
897 a.m_prev = &a; | |
898 } else { | |
899 a.m_next->m_prev = &a; | |
900 a.m_prev->m_next = &a; | |
901 } | |
902 } | |
903 | |
904 inline void swap(LinkedHashSetNodeBase& a, LinkedHashSetNodeBase& b) { | |
905 DCHECK_NE(a.m_next, &a); | |
906 DCHECK_NE(b.m_next, &b); | |
907 swap(a.m_prev, b.m_prev); | |
908 swap(a.m_next, b.m_next); | |
909 if (b.m_next) { | |
910 b.m_next->m_prev = &b; | |
911 b.m_prev->m_next = &b; | |
912 } | |
913 if (a.m_next) { | |
914 a.m_next->m_prev = &a; | |
915 a.m_prev->m_next = &a; | |
916 } | |
917 } | |
918 | |
919 template <typename T, typename Allocator> | |
920 inline void swap(LinkedHashSetNode<T, Allocator>& a, | |
921 LinkedHashSetNode<T, Allocator>& b) { | |
922 typedef LinkedHashSetNodeBase Base; | |
923 // The key and value cannot be swapped atomically, and it would be | |
924 // wrong to have a GC when only one was swapped and the other still | |
925 // contained garbage (eg. from a previous use of the same slot). | |
926 // Therefore we forbid a GC until both the key and the value are | |
927 // swapped. | |
928 Allocator::enterGCForbiddenScope(); | |
929 swap(static_cast<Base&>(a), static_cast<Base&>(b)); | |
930 swap(a.m_value, b.m_value); | |
931 Allocator::leaveGCForbiddenScope(); | |
932 } | |
933 | |
934 } // namespace WTF | |
935 | |
936 using WTF::LinkedHashSet; | |
937 | |
938 #endif /* WTF_LinkedHashSet_h */ | |
OLD | NEW |