/* * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights reserved. * Copyright (C) 2011, Benjamin Poulain * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #ifndef WTF_LinkedHashSet_h #define WTF_LinkedHashSet_h #include "wtf/DefaultAllocator.h" #include "wtf/HashSet.h" #include "wtf/OwnPtr.h" #include "wtf/PassOwnPtr.h" namespace WTF { // LinkedHashSet: Just like HashSet, this class provides a Set // interface - a collection of unique objects with O(1) insertion, // removal and test for containership. However, it also has an // order - iterating it will always give back values in the order // in which they are added. // Unlike ListHashSet, but like most WTF collections, iteration is NOT safe // against mutation of the LinkedHashSet. template class LinkedHashSet; template class LinkedHashSetIterator; template class LinkedHashSetConstIterator; template class LinkedHashSetReverseIterator; template class LinkedHashSetConstReverseIterator; template struct LinkedHashSetTranslator; template struct LinkedHashSetExtractor; template struct LinkedHashSetTraits; class LinkedHashSetNodeBase { public: LinkedHashSetNodeBase() : m_prev(this), m_next(this) { } void unlink() { if (!m_next) return; ASSERT(m_prev); ASSERT(m_next->m_prev == this); ASSERT(m_prev->m_next == this); m_next->m_prev = m_prev; m_prev->m_next = m_next; } ~LinkedHashSetNodeBase() { unlink(); } void insertBefore(LinkedHashSetNodeBase& other) { other.m_next = this; other.m_prev = m_prev; m_prev->m_next = &other; m_prev = &other; ASSERT(other.m_next); ASSERT(other.m_prev); ASSERT(m_next); ASSERT(m_prev); } void insertAfter(LinkedHashSetNodeBase& other) { other.m_prev = this; other.m_next = m_next; m_next->m_prev = &other; m_next = &other; ASSERT(other.m_next); ASSERT(other.m_prev); ASSERT(m_next); ASSERT(m_prev); } LinkedHashSetNodeBase(LinkedHashSetNodeBase* prev, LinkedHashSetNodeBase* next) : m_prev(prev) , m_next(next) { ASSERT((prev && next) || (!prev && !next)); } LinkedHashSetNodeBase* m_prev; LinkedHashSetNodeBase* m_next; protected: // If we take a copy of a node we can't copy the next and prev pointers, // since they point to something that does not point at us. This is used // inside the shouldExpand() "if" in HashTable::add. LinkedHashSetNodeBase(const LinkedHashSetNodeBase& other) : m_prev(0) , m_next(0) { } private: // Should not be used. LinkedHashSetNodeBase& operator=(const LinkedHashSetNodeBase& other); }; template class LinkedHashSetNode : public LinkedHashSetNodeBase { public: LinkedHashSetNode(const ValueArg& value, LinkedHashSetNodeBase* prev, LinkedHashSetNodeBase* next) : LinkedHashSetNodeBase(prev, next) , m_value(value) { } ValueArg m_value; private: // Not used. LinkedHashSetNode(const LinkedHashSetNode&); }; template< typename ValueArg, typename HashFunctions = typename DefaultHash::Hash, typename TraitsArg = HashTraits, typename Allocator = DefaultAllocator> class LinkedHashSet { WTF_USE_ALLOCATOR(LinkedHashSet, Allocator); private: typedef ValueArg Value; typedef TraitsArg Traits; typedef LinkedHashSetNode Node; typedef LinkedHashSetNodeBase NodeBase; typedef LinkedHashSetTranslator NodeHashFunctions; typedef LinkedHashSetTraits NodeHashTraits; typedef HashTable ImplType; public: typedef LinkedHashSetIterator iterator; friend class LinkedHashSetIterator; typedef LinkedHashSetConstIterator const_iterator; friend class LinkedHashSetConstIterator; typedef LinkedHashSetReverseIterator reverse_iterator; friend class LinkedHashSetReverseIterator; typedef LinkedHashSetConstReverseIterator const_reverse_iterator; friend class LinkedHashSetConstReverseIterator; struct AddResult { AddResult(const typename ImplType::AddResult& hashTableAddResult) : storedValue(&hashTableAddResult.storedValue->m_value) , isNewEntry(hashTableAddResult.isNewEntry) { } Value* storedValue; bool isNewEntry; }; typedef typename HashTraits::PeekInType ValuePeekInType; LinkedHashSet(); LinkedHashSet(const LinkedHashSet&); LinkedHashSet& operator=(const LinkedHashSet&); ~LinkedHashSet(); static void finalize(void* pointer) { reinterpret_cast(pointer)->~LinkedHashSet(); } void swap(LinkedHashSet&); unsigned size() const { return m_impl.size(); } unsigned capacity() const { return m_impl.capacity(); } bool isEmpty() const { return m_impl.isEmpty(); } iterator begin() { return makeIterator(firstNode()); } iterator end() { return makeIterator(anchor()); } const_iterator begin() const { return makeConstIterator(firstNode()); } const_iterator end() const { return makeConstIterator(anchor()); } reverse_iterator rbegin() { return makeReverseIterator(lastNode()); } reverse_iterator rend() { return makeReverseIterator(anchor()); } const_reverse_iterator rbegin() const { return makeConstReverseIterator(lastNode()); } const_reverse_iterator rend() const { return makeConstReverseIterator(anchor()); } Value& first(); const Value& first() const; void removeFirst(); Value& last(); const Value& last() const; void removeLast(); iterator find(ValuePeekInType); const_iterator find(ValuePeekInType) const; bool contains(ValuePeekInType) const; // An alternate version of find() that finds the object by hashing and comparing // with some other type, to avoid the cost of type conversion. // The HashTranslator interface is defined in HashSet. template iterator find(const T&); template const_iterator find(const T&) const; template bool contains(const T&) const; // The return value of add is a pair of a pointer to the stored value, // and a bool that is true if an new entry was added. AddResult add(ValuePeekInType); // Same as add() except that the return value is an // iterator. Useful in cases where it's needed to have the // same return value as find() and where it's not possible to // use a pointer to the storedValue. iterator addReturnIterator(ValuePeekInType); // Add the value to the end of the collection. If the value was already in // the list, it is moved to the end. AddResult appendOrMoveToLast(ValuePeekInType); // Add the value to the beginning of the collection. If the value was already in // the list, it is moved to the beginning. AddResult prependOrMoveToFirst(ValuePeekInType); AddResult insertBefore(ValuePeekInType beforeValue, ValuePeekInType newValue); AddResult insertBefore(iterator it, ValuePeekInType newValue) { return m_impl.template add(newValue, it.node()); } void remove(ValuePeekInType); void remove(iterator); void clear() { m_impl.clear(); } template void removeAll(const Collection& other) { WTF::removeAll(*this, other); } void trace(typename Allocator::Visitor* visitor) { m_impl.trace(visitor); } int64_t modifications() const { return m_impl.modifications(); } void checkModifications(int64_t mods) const { m_impl.checkModifications(mods); } private: Node* anchor() { return reinterpret_cast(&m_anchor); } const Node* anchor() const { return reinterpret_cast(&m_anchor); } Node* firstNode() { return reinterpret_cast(m_anchor.m_next); } const Node* firstNode() const { return reinterpret_cast(m_anchor.m_next); } Node* lastNode() { return reinterpret_cast(m_anchor.m_prev); } const Node* lastNode() const { return reinterpret_cast(m_anchor.m_prev); } iterator makeIterator(const Node* position) { return iterator(position, this); } const_iterator makeConstIterator(const Node* position) const { return const_iterator(position, this); } reverse_iterator makeReverseIterator(const Node* position) { return reverse_iterator(position, this); } const_reverse_iterator makeConstReverseIterator(const Node* position) const { return const_reverse_iterator(position, this); } ImplType m_impl; NodeBase m_anchor; #ifndef ASSERT_ENABLED uint64_t m_modifications; #endif }; template struct LinkedHashSetTranslator { typedef LinkedHashSetNode Node; typedef LinkedHashSetNodeBase NodeBase; typedef typename HashTraits::PeekInType ValuePeekInType; static unsigned hash(const Node& node) { return HashFunctions::hash(node.m_value); } static unsigned hash(const ValuePeekInType& key) { return HashFunctions::hash(key); } static bool equal(const Node& a, const ValuePeekInType& b) { return HashFunctions::equal(a.m_value, b); } static bool equal(const Node& a, const Node& b) { return HashFunctions::equal(a.m_value, b.m_value); } static void translate(Node& location, ValuePeekInType key, NodeBase* anchor) { location.m_value = key; anchor->insertBefore(location); } // Empty (or deleted) slots have the m_next pointer set to null, but we // don't do anything to the other fields, which may contain junk. // Therefore you can't compare a newly constructed empty value with a // slot and get the right answer. static const bool safeToCompareToEmptyOrDeleted = false; }; template struct LinkedHashSetExtractor { static const Value& extract(const LinkedHashSetNode& node) { return node.m_value; } }; template struct LinkedHashSetTraits : public SimpleClassHashTraits > { typedef LinkedHashSetNode Node; typedef ValueTraitsArg ValueTraits; // The slot is empty when the m_next field is zero so it's safe to zero // the backing. static const bool emptyValueIsZero = true; static const bool hasIsEmptyValueFunction = true; static bool isEmptyValue(const Node& value) { return !value.m_next; } static const int deletedValue = -1; static void constructDeletedValue(Node& slot) { slot.m_next = reinterpret_cast(deletedValue); } static bool isDeletedValue(const Node& slot) { return slot.m_next == reinterpret_cast(deletedValue); } // We always need to call destructors, that's how we get linked and // unlinked from the chain. static const bool needsDestruction = true; // Whether we need to trace and do weak processing depends on the traits of // the type inside the node. template struct NeedsTracingLazily { static const bool value = ValueTraits::template NeedsTracingLazily<>::value; }; static const bool isWeak = ValueTraits::isWeak; }; template class LinkedHashSetIterator { private: typedef typename LinkedHashSetType::Node Node; typedef typename LinkedHashSetType::Traits Traits; typedef typename LinkedHashSetType::Value& ReferenceType; typedef typename LinkedHashSetType::Value* PointerType; typedef LinkedHashSetConstIterator const_iterator; Node* node() { return const_cast(m_iterator.node()); } protected: LinkedHashSetIterator(const Node* position, LinkedHashSetType* m_container) : m_iterator(position , m_container) { } public: // Default copy, assignment and destructor are OK. PointerType get() const { return const_cast(m_iterator.get()); } ReferenceType operator*() const { return *get(); } PointerType operator->() const { return get(); } LinkedHashSetIterator& operator++() { ++m_iterator; return *this; } LinkedHashSetIterator& operator--() { --m_iterator; return *this; } // Postfix ++ and -- intentionally omitted. // Comparison. bool operator==(const LinkedHashSetIterator& other) const { return m_iterator == other.m_iterator; } bool operator!=(const LinkedHashSetIterator& other) const { return m_iterator != other.m_iterator; } operator const_iterator() const { return m_iterator; } protected: const_iterator m_iterator; template friend class LinkedHashSet; }; template class LinkedHashSetConstIterator { private: typedef typename LinkedHashSetType::Node Node; typedef typename LinkedHashSetType::Traits Traits; typedef const typename LinkedHashSetType::Value& ReferenceType; typedef const typename LinkedHashSetType::Value* PointerType; const Node* node() const { return static_cast(m_position); } protected: LinkedHashSetConstIterator(const LinkedHashSetNodeBase* position, const LinkedHashSetType* container) : m_position(position) #ifdef ASSERT_ENABLED , m_container(container) , m_containerModifications(container->modifications()) #endif { } public: PointerType get() const { checkModifications(); return &static_cast(m_position)->m_value; } ReferenceType operator*() const { return *get(); } PointerType operator->() const { return get(); } LinkedHashSetConstIterator& operator++() { ASSERT(m_position); checkModifications(); m_position = m_position->m_next; return *this; } LinkedHashSetConstIterator& operator--() { ASSERT(m_position); checkModifications(); m_position = m_position->m_prev; return *this; } // Postfix ++ and -- intentionally omitted. // Comparison. bool operator==(const LinkedHashSetConstIterator& other) const { return m_position == other.m_position; } bool operator!=(const LinkedHashSetConstIterator& other) const { return m_position != other.m_position; } private: const LinkedHashSetNodeBase* m_position; #ifdef ASSERT_ENABLED void checkModifications() const { m_container->checkModifications(m_containerModifications); } const LinkedHashSetType* m_container; int64_t m_containerModifications; #else void checkModifications() const { } #endif template friend class LinkedHashSet; friend class LinkedHashSetIterator; }; template class LinkedHashSetReverseIterator : public LinkedHashSetIterator { typedef LinkedHashSetIterator Superclass; typedef LinkedHashSetConstReverseIterator const_reverse_iterator; typedef typename LinkedHashSetType::Node Node; protected: LinkedHashSetReverseIterator(const Node* position, LinkedHashSetType* container) : Superclass(position, container) { } public: LinkedHashSetReverseIterator& operator++() { Superclass::operator--(); return *this; } LinkedHashSetReverseIterator& operator--() { Superclass::operator++(); return *this; } // Postfix ++ and -- intentionally omitted. operator const_reverse_iterator() const { return *reinterpret_cast(this); } template friend class LinkedHashSet; }; template class LinkedHashSetConstReverseIterator : public LinkedHashSetConstIterator { typedef LinkedHashSetConstIterator Superclass; typedef typename LinkedHashSetType::Node Node; public: LinkedHashSetConstReverseIterator(const Node* position, const LinkedHashSetType* container) : Superclass(position, container) { } LinkedHashSetConstReverseIterator& operator++() { Superclass::operator--(); return *this; } LinkedHashSetConstReverseIterator& operator--() { Superclass::operator++(); return *this; } // Postfix ++ and -- intentionally omitted. template friend class LinkedHashSet; }; template inline LinkedHashSet::LinkedHashSet() { } template inline LinkedHashSet::LinkedHashSet(const LinkedHashSet& other) : m_anchor() { const_iterator end = other.end(); for (const_iterator it = other.begin(); it != end; ++it) add(*it); } template inline LinkedHashSet& LinkedHashSet::operator=(const LinkedHashSet& other) { LinkedHashSet tmp(other); swap(tmp); return *this; } template inline void LinkedHashSet::swap(LinkedHashSet& other) { m_impl.swap(other.m_impl); swap(m_anchor, other.m_anchor); } template inline LinkedHashSet::~LinkedHashSet() { // The destructor of m_anchor will implicitly be called here, which will // unlink the anchor from the collection. } template inline T& LinkedHashSet::first() { ASSERT(!isEmpty()); return firstNode()->m_value; } template inline const T& LinkedHashSet::first() const { ASSERT(!isEmpty()); return firstNode()->m_value; } template inline void LinkedHashSet::removeFirst() { ASSERT(!isEmpty()); m_impl.remove(static_cast(m_anchor.m_next)); } template inline T& LinkedHashSet::last() { ASSERT(!isEmpty()); return lastNode()->m_value; } template inline const T& LinkedHashSet::last() const { ASSERT(!isEmpty()); return lastNode()->m_value; } template inline void LinkedHashSet::removeLast() { ASSERT(!isEmpty()); m_impl.remove(static_cast(m_anchor.m_prev)); } template inline typename LinkedHashSet::iterator LinkedHashSet::find(ValuePeekInType value) { LinkedHashSet::Node* node = m_impl.template lookup(value); if (!node) return end(); return makeIterator(node); } template inline typename LinkedHashSet::const_iterator LinkedHashSet::find(ValuePeekInType value) const { const LinkedHashSet::Node* node = m_impl.template lookup(value); if (!node) return end(); return makeConstIterator(node); } template struct LinkedHashSetTranslatorAdapter { template static unsigned hash(const T& key) { return Translator::hash(key); } template static bool equal(const T& a, const U& b) { return Translator::equal(a.m_value, b); } }; template template inline typename LinkedHashSet::iterator LinkedHashSet::find(const T& value) { typedef LinkedHashSetTranslatorAdapter TranslatedFunctions; const LinkedHashSet::Node* node = m_impl.template lookup(value); if (!node) return end(); return makeIterator(node); } template template inline typename LinkedHashSet::const_iterator LinkedHashSet::find(const T& value) const { typedef LinkedHashSetTranslatorAdapter TranslatedFunctions; const LinkedHashSet::Node* node = m_impl.template lookup(value); if (!node) return end(); return makeConstIterator(node); } template template inline bool LinkedHashSet::contains(const T& value) const { return m_impl.template contains >(value); } template inline bool LinkedHashSet::contains(ValuePeekInType value) const { return m_impl.template contains(value); } template typename LinkedHashSet::AddResult LinkedHashSet::add(ValuePeekInType value) { return m_impl.template add(value, &m_anchor); } template typename LinkedHashSet::iterator LinkedHashSet::addReturnIterator(ValuePeekInType value) { typename ImplType::AddResult result = m_impl.template add(value, &m_anchor); return makeIterator(result.storedValue); } template typename LinkedHashSet::AddResult LinkedHashSet::appendOrMoveToLast(ValuePeekInType value) { typename ImplType::AddResult result = m_impl.template add(value, &m_anchor); Node* node = result.storedValue; if (!result.isNewEntry) { node->unlink(); m_anchor.insertBefore(*node); } return result; } template typename LinkedHashSet::AddResult LinkedHashSet::prependOrMoveToFirst(ValuePeekInType value) { typename ImplType::AddResult result = m_impl.template add(value, m_anchor.m_next); Node* node = result.storedValue; if (!result.isNewEntry) { node->unlink(); m_anchor.insertAfter(*node); } return result; } template typename LinkedHashSet::AddResult LinkedHashSet::insertBefore(ValuePeekInType beforeValue, ValuePeekInType newValue) { return insertBefore(find(beforeValue), newValue); } template inline void LinkedHashSet::remove(iterator it) { if (it == end()) return; m_impl.remove(it.node()); } template inline void LinkedHashSet::remove(ValuePeekInType value) { remove(find(value)); } template struct IsWeak > { static const bool value = IsWeak::value; }; inline void swap(LinkedHashSetNodeBase& a, LinkedHashSetNodeBase& b) { typedef LinkedHashSetNodeBase Base; swap(a.m_next, b.m_next); swap(a.m_prev, b.m_prev); if (b.m_next) { b.m_next->m_prev = &b; b.m_prev->m_next = &b; } if (a.m_next) { a.m_next->m_prev = &a; a.m_prev->m_next = &a; } } template inline void swap(LinkedHashSetNode& a, LinkedHashSetNode& b) { typedef LinkedHashSetNodeBase Base; swap(static_cast(a), static_cast(b)); swap(a.m_value, b.m_value); } // Warning: After and while calling this you have a collection with deleted // pointers. Consider using a smart pointer like OwnPtr and calling clear() // instead. template void deleteAllValues(const LinkedHashSet& set) { typedef typename LinkedHashSet::const_iterator iterator; iterator end = set.end(); for (iterator it = set.begin(); it != end; ++it) delete *it; } } using WTF::LinkedHashSet; #endif /* WTF_LinkedHashSet_h */