/* * 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 SKY_ENGINE_WTF_LISTHASHSET_H_ #define SKY_ENGINE_WTF_LISTHASHSET_H_ #include "sky/engine/wtf/DefaultAllocator.h" #include "sky/engine/wtf/HashSet.h" #include "sky/engine/wtf/OwnPtr.h" #include "sky/engine/wtf/PassOwnPtr.h" namespace WTF { // ListHashSet: 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 iteration of most WTF Hash data structures, iteration is // guaranteed safe against mutation of the ListHashSet, except for // removal of the item currently pointed to by a given iterator. template class ListHashSet; template class ListHashSetIterator; template class ListHashSetConstIterator; template class ListHashSetReverseIterator; template class ListHashSetConstReverseIterator; template class ListHashSetNodeBase; template class ListHashSetNode; template struct ListHashSetAllocator; template struct ListHashSetNodeHashFunctions; template struct ListHashSetTranslator; // Don't declare a destructor for HeapAllocated ListHashSet. template class ListHashSetDestructorBase; template class ListHashSetDestructorBase { protected: typename Allocator::AllocatorProvider m_allocatorProvider; }; template class ListHashSetDestructorBase { public: ~ListHashSetDestructorBase() { static_cast(this)->finalize(); } protected: typename Allocator::AllocatorProvider m_allocatorProvider; }; // Note that for a ListHashSet you cannot specify the HashTraits as a // template argument. It uses the default hash traits for the ValueArg // type. template::Hash, typename AllocatorArg = ListHashSetAllocator > class ListHashSet : public ListHashSetDestructorBase, AllocatorArg, AllocatorArg::isGarbageCollected> { typedef AllocatorArg Allocator; WTF_USE_ALLOCATOR(ListHashSet, Allocator); typedef ListHashSetNode Node; typedef HashTraits NodeTraits; typedef ListHashSetNodeHashFunctions NodeHash; typedef ListHashSetTranslator BaseTranslator; typedef HashTable ImplType; typedef HashTableIterator ImplTypeIterator; typedef HashTableConstIterator ImplTypeConstIterator; typedef HashArg HashFunctions; public: typedef ValueArg ValueType; typedef HashTraits ValueTraits; typedef typename ValueTraits::PeekInType ValuePeekInType; typedef typename ValueTraits::PassInType ValuePassInType; typedef typename ValueTraits::PassOutType ValuePassOutType; typedef ListHashSetIterator iterator; typedef ListHashSetConstIterator const_iterator; friend class ListHashSetIterator; friend class ListHashSetConstIterator; typedef ListHashSetReverseIterator reverse_iterator; typedef ListHashSetConstReverseIterator const_reverse_iterator; friend class ListHashSetReverseIterator; friend class ListHashSetConstReverseIterator; template struct HashTableAddResult { HashTableAddResult(Node* storedValue, bool isNewEntry) : storedValue(storedValue), isNewEntry(isNewEntry) { } Node* storedValue; bool isNewEntry; }; typedef HashTableAddResult AddResult; ListHashSet(); ListHashSet(const ListHashSet&); ListHashSet& operator=(const ListHashSet&); void finalize(); void swap(ListHashSet&); 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(m_head); } iterator end() { return makeIterator(0); } const_iterator begin() const { return makeConstIterator(m_head); } const_iterator end() const { return makeConstIterator(0); } reverse_iterator rbegin() { return makeReverseIterator(m_tail); } reverse_iterator rend() { return makeReverseIterator(0); } const_reverse_iterator rbegin() const { return makeConstReverseIterator(m_tail); } const_reverse_iterator rend() const { return makeConstReverseIterator(0); } ValueType& first(); const ValueType& first() const; void removeFirst(); ValueType& last(); const ValueType& 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(ValuePassInType); // 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(ValuePassInType); // 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(ValuePassInType); // 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(ValuePassInType); AddResult insertBefore(ValuePeekInType beforeValue, ValuePassInType newValue); AddResult insertBefore(iterator, ValuePassInType); void remove(ValuePeekInType value) { return remove(find(value)); } void remove(iterator); void clear(); template void removeAll(const Collection& other) { WTF::removeAll(*this, other); } ValuePassOutType take(iterator); ValuePassOutType take(ValuePeekInType); ValuePassOutType takeFirst(); private: void unlink(Node*); void unlinkAndDelete(Node*); void appendNode(Node*); void prependNode(Node*); void insertNodeBefore(Node* beforeNode, Node* newNode); void deleteAllNodes(); Allocator* allocator() const { return this->m_allocatorProvider.get(); } void createAllocatorIfNeeded() { this->m_allocatorProvider.createAllocatorIfNeeded(); } void deallocate(Node* node) const { this->m_allocatorProvider.deallocate(node); } iterator makeIterator(Node* position) { return iterator(this, position); } const_iterator makeConstIterator(Node* position) const { return const_iterator(this, position); } reverse_iterator makeReverseIterator(Node* position) { return reverse_iterator(this, position); } const_reverse_iterator makeConstReverseIterator(Node* position) const { return const_reverse_iterator(this, position); } ImplType m_impl; Node* m_head; Node* m_tail; }; // ListHashSetNode has this base class to hold the members because the MSVC // compiler otherwise gets into circular template dependencies when trying // to do sizeof on a node. template class ListHashSetNodeBase { protected: ListHashSetNodeBase(const ValueArg& value) : m_value(value) , m_prev(0) , m_next(0) #if ENABLE(ASSERT) , m_isAllocated(true) #endif { } template ListHashSetNodeBase(const U& value) : m_value(value) , m_prev(0) , m_next(0) #if ENABLE(ASSERT) , m_isAllocated(true) #endif { } public: ValueArg m_value; ListHashSetNodeBase* m_prev; ListHashSetNodeBase* m_next; #if ENABLE(ASSERT) bool m_isAllocated; #endif }; // This allocator is only used for non-Heap ListHashSets. template struct ListHashSetAllocator : public DefaultAllocator { typedef DefaultAllocator TableAllocator; typedef ListHashSetNode Node; typedef ListHashSetNodeBase NodeBase; class AllocatorProvider { public: void createAllocatorIfNeeded() { if (!m_allocator) m_allocator = adoptPtr(new ListHashSetAllocator); } void swap(AllocatorProvider& other) { m_allocator.swap(other.m_allocator); } void deallocate(Node* node) const { ASSERT(m_allocator); m_allocator->deallocate(node); } ListHashSetAllocator* get() const { ASSERT(m_allocator); return m_allocator.get(); } private: OwnPtr m_allocator; }; ListHashSetAllocator() : m_freeList(pool()) , m_isDoneWithInitialFreeList(false) { memset(m_pool.buffer, 0, sizeof(m_pool.buffer)); } Node* allocateNode() { Node* result = m_freeList; if (!result) return static_cast(fastMalloc(sizeof(NodeBase))); ASSERT(!result->m_isAllocated); Node* next = result->next(); ASSERT(!next || !next->m_isAllocated); if (!next && !m_isDoneWithInitialFreeList) { next = result + 1; if (next == pastPool()) { m_isDoneWithInitialFreeList = true; next = 0; } else { ASSERT(inPool(next)); ASSERT(!next->m_isAllocated); } } m_freeList = next; return result; } void deallocate(Node* node) { if (inPool(node)) { #if ENABLE(ASSERT) node->m_isAllocated = false; #endif node->m_next = m_freeList; m_freeList = node; return; } fastFree(node); } bool inPool(Node* node) { return node >= pool() && node < pastPool(); } private: Node* pool() { return reinterpret_cast_ptr(m_pool.buffer); } Node* pastPool() { return pool() + m_poolSize; } Node* m_freeList; bool m_isDoneWithInitialFreeList; #if defined(MEMORY_SANITIZER_INITIAL_SIZE) // The allocation pool for nodes is one big chunk that ASAN has no // insight into, so it can cloak errors. Make it as small as possible // to force nodes to be allocated individually where ASAN can see them. static const size_t m_poolSize = 1; #else static const size_t m_poolSize = inlineCapacity; #endif AlignedBuffer m_pool; }; template class ListHashSetNode : public ListHashSetNodeBase { public: typedef AllocatorArg NodeAllocator; typedef ValueArg Value; template ListHashSetNode(U value) : ListHashSetNodeBase(value) { } void* operator new(size_t, NodeAllocator* allocator) { COMPILE_ASSERT(sizeof(ListHashSetNode) == sizeof(ListHashSetNodeBase), PleaseAddAnyFieldsToTheBase); return allocator->allocateNode(); } void setWasAlreadyDestructed() { if (NodeAllocator::isGarbageCollected && HashTraits::needsDestruction) this->m_prev = unlinkedNodePointer(); } bool wasAlreadyDestructed() const { ASSERT(NodeAllocator::isGarbageCollected); return this->m_prev == unlinkedNodePointer(); } static void finalize(void* pointer) { ASSERT(HashTraits::needsDestruction); // No need to waste time calling finalize if it's not needed. ListHashSetNode* self = reinterpret_cast_ptr(pointer); // Check whether this node was already destructed before being // unlinked from the collection. if (self->wasAlreadyDestructed()) return; self->m_value.~ValueArg(); } void destroy(NodeAllocator* allocator) { this->~ListHashSetNode(); setWasAlreadyDestructed(); allocator->deallocate(this); } ListHashSetNode* next() const { return reinterpret_cast(this->m_next); } ListHashSetNode* prev() const { return reinterpret_cast(this->m_prev); } // Don't add fields here, the ListHashSetNodeBase and this should have // the same size. static ListHashSetNode* unlinkedNodePointer() { return reinterpret_cast(-1); } template friend struct ListHashSetNodeHashFunctions; }; template struct ListHashSetNodeHashFunctions { template static unsigned hash(const T& key) { return HashArg::hash(key->m_value); } template static bool equal(const T& a, const T& b) { return HashArg::equal(a->m_value, b->m_value); } static const bool safeToCompareToEmptyOrDeleted = false; }; template class ListHashSetIterator { private: typedef typename Set::const_iterator const_iterator; typedef typename Set::Node Node; typedef typename Set::ValueType ValueType; typedef ValueType& ReferenceType; typedef ValueType* PointerType; ListHashSetIterator(const Set* set, Node* position) : m_iterator(set, position) { } public: ListHashSetIterator() { } // 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(); } ListHashSetIterator& operator++() { ++m_iterator; return *this; } ListHashSetIterator& operator--() { --m_iterator; return *this; } // Postfix ++ and -- intentionally omitted. // Comparison. bool operator==(const ListHashSetIterator& other) const { return m_iterator == other.m_iterator; } bool operator!=(const ListHashSetIterator& other) const { return m_iterator != other.m_iterator; } operator const_iterator() const { return m_iterator; } private: Node* node() { return m_iterator.node(); } const_iterator m_iterator; template friend class ListHashSet; }; template class ListHashSetConstIterator { private: typedef typename Set::const_iterator const_iterator; typedef typename Set::Node Node; typedef typename Set::ValueType ValueType; typedef const ValueType& ReferenceType; typedef const ValueType* PointerType; friend class ListHashSetIterator; ListHashSetConstIterator(const Set* set, Node* position) : m_set(set) , m_position(position) { } public: ListHashSetConstIterator() { } PointerType get() const { return &m_position->m_value; } ReferenceType operator*() const { return *get(); } PointerType operator->() const { return get(); } ListHashSetConstIterator& operator++() { ASSERT(m_position != 0); m_position = m_position->next(); return *this; } ListHashSetConstIterator& operator--() { ASSERT(m_position != m_set->m_head); if (!m_position) m_position = m_set->m_tail; else m_position = m_position->prev(); return *this; } // Postfix ++ and -- intentionally omitted. // Comparison. bool operator==(const ListHashSetConstIterator& other) const { return m_position == other.m_position; } bool operator!=(const ListHashSetConstIterator& other) const { return m_position != other.m_position; } private: Node* node() { return m_position; } const Set* m_set; Node* m_position; template friend class ListHashSet; }; template class ListHashSetReverseIterator { private: typedef typename Set::const_reverse_iterator const_reverse_iterator; typedef typename Set::Node Node; typedef typename Set::ValueType ValueType; typedef ValueType& ReferenceType; typedef ValueType* PointerType; ListHashSetReverseIterator(const Set* set, Node* position) : m_iterator(set, position) { } public: ListHashSetReverseIterator() { } // 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(); } ListHashSetReverseIterator& operator++() { ++m_iterator; return *this; } ListHashSetReverseIterator& operator--() { --m_iterator; return *this; } // Postfix ++ and -- intentionally omitted. // Comparison. bool operator==(const ListHashSetReverseIterator& other) const { return m_iterator == other.m_iterator; } bool operator!=(const ListHashSetReverseIterator& other) const { return m_iterator != other.m_iterator; } operator const_reverse_iterator() const { return m_iterator; } private: Node* node() { return m_iterator.node(); } const_reverse_iterator m_iterator; template friend class ListHashSet; }; template class ListHashSetConstReverseIterator { private: typedef typename Set::reverse_iterator reverse_iterator; typedef typename Set::Node Node; typedef typename Set::ValueType ValueType; typedef const ValueType& ReferenceType; typedef const ValueType* PointerType; friend class ListHashSetReverseIterator; ListHashSetConstReverseIterator(const Set* set, Node* position) : m_set(set) , m_position(position) { } public: ListHashSetConstReverseIterator() { } PointerType get() const { return &m_position->m_value; } ReferenceType operator*() const { return *get(); } PointerType operator->() const { return get(); } ListHashSetConstReverseIterator& operator++() { ASSERT(m_position != 0); m_position = m_position->prev(); return *this; } ListHashSetConstReverseIterator& operator--() { ASSERT(m_position != m_set->m_tail); if (!m_position) m_position = m_set->m_head; else m_position = m_position->next(); return *this; } // Postfix ++ and -- intentionally omitted. // Comparison. bool operator==(const ListHashSetConstReverseIterator& other) const { return m_position == other.m_position; } bool operator!=(const ListHashSetConstReverseIterator& other) const { return m_position != other.m_position; } private: Node* node() { return m_position; } const Set* m_set; Node* m_position; template friend class ListHashSet; }; template struct ListHashSetTranslator { template static unsigned hash(const T& key) { return HashFunctions::hash(key); } template static bool equal(const T& a, const U& b) { return HashFunctions::equal(a->m_value, b); } template static void translate(T*& location, const U& key, const V& allocator) { location = new (const_cast(&allocator)) T(key); } }; template inline ListHashSet::ListHashSet() : m_head(0) , m_tail(0) { } template inline ListHashSet::ListHashSet(const ListHashSet& other) : m_head(0) , m_tail(0) { const_iterator end = other.end(); for (const_iterator it = other.begin(); it != end; ++it) add(*it); } template inline ListHashSet& ListHashSet::operator=(const ListHashSet& other) { ListHashSet tmp(other); swap(tmp); return *this; } template inline void ListHashSet::swap(ListHashSet& other) { m_impl.swap(other.m_impl); std::swap(m_head, other.m_head); std::swap(m_tail, other.m_tail); this->m_allocatorProvider.swap(other.m_allocatorProvider); } template inline void ListHashSet::finalize() { COMPILE_ASSERT(!Allocator::isGarbageCollected, FinalizeOnHeapAllocatedListHashSetShouldNeverBeCalled); deleteAllNodes(); } template inline T& ListHashSet::first() { ASSERT(!isEmpty()); return m_head->m_value; } template inline void ListHashSet::removeFirst() { ASSERT(!isEmpty()); m_impl.remove(m_head); unlinkAndDelete(m_head); } template inline const T& ListHashSet::first() const { ASSERT(!isEmpty()); return m_head->m_value; } template inline T& ListHashSet::last() { ASSERT(!isEmpty()); return m_tail->m_value; } template inline const T& ListHashSet::last() const { ASSERT(!isEmpty()); return m_tail->m_value; } template inline void ListHashSet::removeLast() { ASSERT(!isEmpty()); m_impl.remove(m_tail); unlinkAndDelete(m_tail); } template inline typename ListHashSet::iterator ListHashSet::find(ValuePeekInType value) { ImplTypeIterator it = m_impl.template find(value); if (it == m_impl.end()) return end(); return makeIterator(*it); } template inline typename ListHashSet::const_iterator ListHashSet::find(ValuePeekInType value) const { ImplTypeConstIterator it = m_impl.template find(value); if (it == m_impl.end()) return end(); return makeConstIterator(*it); } template struct ListHashSetTranslatorAdapter { 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 ListHashSet::iterator ListHashSet::find(const T& value) { ImplTypeConstIterator it = m_impl.template find >(value); if (it == m_impl.end()) return end(); return makeIterator(*it); } template template inline typename ListHashSet::const_iterator ListHashSet::find(const T& value) const { ImplTypeConstIterator it = m_impl.template find >(value); if (it == m_impl.end()) return end(); return makeConstIterator(*it); } template template inline bool ListHashSet::contains(const T& value) const { return m_impl.template contains >(value); } template inline bool ListHashSet::contains(ValuePeekInType value) const { return m_impl.template contains(value); } template typename ListHashSet::AddResult ListHashSet::add(ValuePassInType value) { createAllocatorIfNeeded(); // The second argument is a const ref. This is useful for the HashTable // because it lets it take lvalues by reference, but for our purposes // it's inconvenient, since it constrains us to be const, whereas the // allocator actually changes when it does allocations. typename ImplType::AddResult result = m_impl.template add(value, *this->allocator()); if (result.isNewEntry) appendNode(*result.storedValue); return AddResult(*result.storedValue, result.isNewEntry); } template typename ListHashSet::iterator ListHashSet::addReturnIterator(ValuePassInType value) { return makeIterator(add(value).storedValue); } template typename ListHashSet::AddResult ListHashSet::appendOrMoveToLast(ValuePassInType value) { createAllocatorIfNeeded(); typename ImplType::AddResult result = m_impl.template add(value, *this->allocator()); Node* node = *result.storedValue; if (!result.isNewEntry) unlink(node); appendNode(node); return AddResult(*result.storedValue, result.isNewEntry); } template typename ListHashSet::AddResult ListHashSet::prependOrMoveToFirst(ValuePassInType value) { createAllocatorIfNeeded(); typename ImplType::AddResult result = m_impl.template add(value, *this->allocator()); Node* node = *result.storedValue; if (!result.isNewEntry) unlink(node); prependNode(node); return AddResult(*result.storedValue, result.isNewEntry); } template typename ListHashSet::AddResult ListHashSet::insertBefore(iterator it, ValuePassInType newValue) { createAllocatorIfNeeded(); typename ImplType::AddResult result = m_impl.template add(newValue, *this->allocator()); if (result.isNewEntry) insertNodeBefore(it.node(), *result.storedValue); return AddResult(*result.storedValue, result.isNewEntry); } template typename ListHashSet::AddResult ListHashSet::insertBefore(ValuePeekInType beforeValue, ValuePassInType newValue) { createAllocatorIfNeeded(); return insertBefore(find(beforeValue), newValue); } template inline void ListHashSet::remove(iterator it) { if (it == end()) return; m_impl.remove(it.node()); unlinkAndDelete(it.node()); } template inline void ListHashSet::clear() { deleteAllNodes(); m_impl.clear(); m_head = 0; m_tail = 0; } template typename ListHashSet::ValuePassOutType ListHashSet::take(iterator it) { if (it == end()) return ValueTraits::emptyValue(); m_impl.remove(it.node()); ValuePassOutType result = ValueTraits::passOut(it.node()->m_value); unlinkAndDelete(it.node()); return result; } template typename ListHashSet::ValuePassOutType ListHashSet::take(ValuePeekInType value) { return take(find(value)); } template typename ListHashSet::ValuePassOutType ListHashSet::takeFirst() { ASSERT(!isEmpty()); m_impl.remove(m_head); ValuePassOutType result = ValueTraits::passOut(m_head->m_value); unlinkAndDelete(m_head); return result; } template void ListHashSet::unlink(Node* node) { if (!node->m_prev) { ASSERT(node == m_head); m_head = node->next(); } else { ASSERT(node != m_head); node->m_prev->m_next = node->m_next; } if (!node->m_next) { ASSERT(node == m_tail); m_tail = node->prev(); } else { ASSERT(node != m_tail); node->m_next->m_prev = node->m_prev; } } template void ListHashSet::unlinkAndDelete(Node* node) { unlink(node); node->destroy(this->allocator()); } template void ListHashSet::appendNode(Node* node) { node->m_prev = m_tail; node->m_next = 0; if (m_tail) { ASSERT(m_head); m_tail->m_next = node; } else { ASSERT(!m_head); m_head = node; } m_tail = node; } template void ListHashSet::prependNode(Node* node) { node->m_prev = 0; node->m_next = m_head; if (m_head) m_head->m_prev = node; else m_tail = node; m_head = node; } template void ListHashSet::insertNodeBefore(Node* beforeNode, Node* newNode) { if (!beforeNode) return appendNode(newNode); newNode->m_next = beforeNode; newNode->m_prev = beforeNode->m_prev; if (beforeNode->m_prev) beforeNode->m_prev->m_next = newNode; beforeNode->m_prev = newNode; if (!newNode->m_prev) m_head = newNode; } template void ListHashSet::deleteAllNodes() { if (!m_head) return; for (Node* node = m_head, *next = m_head->next(); node; node = next, next = node ? node->next() : 0) node->destroy(this->allocator()); } } // namespace WTF using WTF::ListHashSet; #endif // SKY_ENGINE_WTF_LISTHASHSET_H_