| 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 |