/* * Copyright (C) 2005-2019 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. * */ #pragma once #include #if CHECK_HASHTABLE_ITERATORS #include #endif 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 struct ListHashSetNode; template struct ListHashSetNodeHashFunctions; template struct ListHashSetTranslator; template> class ListHashSet final #if CHECK_HASHTABLE_ITERATORS : public CanMakeWeakPtr, WeakPtrFactoryInitialization::Eager> #endif { WTF_MAKE_FAST_ALLOCATED; private: typedef ListHashSetNode Node; typedef HashTraits NodeTraits; typedef ListHashSetNodeHashFunctions NodeHash; typedef ListHashSetTranslator BaseTranslator; typedef HashArg HashFunctions; public: typedef ValueArg ValueType; typedef ListHashSetIterator iterator; typedef ListHashSetConstIterator const_iterator; friend class ListHashSetConstIterator; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; typedef HashTableAddResult AddResult; ListHashSet() = default; ListHashSet(std::initializer_list); ListHashSet(const ListHashSet&); ListHashSet(ListHashSet&&); ListHashSet& operator=(const ListHashSet&); ListHashSet& operator=(ListHashSet&&); ~ListHashSet(); void swap(ListHashSet&); unsigned size() const; unsigned capacity() const; bool isEmpty() const; iterator begin() { return makeIterator(m_head); } iterator end() { return makeIterator(nullptr); } const_iterator begin() const { return makeConstIterator(m_head); } const_iterator end() const { return makeConstIterator(nullptr); } iterator random() { return makeIterator(m_impl.random()); } const_iterator random() const { return makeIterator(m_impl.random()); } reverse_iterator rbegin() { return reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } ValueType& first(); const ValueType& first() const; void removeFirst(); ValueType takeFirst(); ValueType& last(); const ValueType& last() const; void removeLast(); ValueType takeLast(); iterator find(const ValueType&); const_iterator find(const ValueType&) const; bool contains(const ValueType&) 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 an iterator to the new value's location, // and a bool that is true if an new entry was added. AddResult add(const ValueType&); AddResult add(ValueType&&); // 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(const ValueType&); AddResult appendOrMoveToLast(ValueType&&); // 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(const ValueType&); AddResult prependOrMoveToFirst(ValueType&&); AddResult insertBefore(const ValueType& beforeValue, const ValueType& newValue); AddResult insertBefore(const ValueType& beforeValue, ValueType&& newValue); AddResult insertBefore(iterator, const ValueType&); AddResult insertBefore(iterator, ValueType&&); bool remove(const ValueType&); bool remove(iterator); void clear(); // Overloads for smart pointer values that take the raw pointer type as the parameter. template typename std::enable_if::value, iterator>::type find(typename GetPtrHelper::PtrType); template typename std::enable_if::value, const_iterator>::type find(typename GetPtrHelper::PtrType) const; template typename std::enable_if::value, bool>::type contains(typename GetPtrHelper::PtrType) const; template typename std::enable_if::value, AddResult>::type insertBefore(typename GetPtrHelper::PtrType, const ValueType&); template typename std::enable_if::value, AddResult>::type insertBefore(typename GetPtrHelper::PtrType, ValueType&&); template typename std::enable_if::value, bool>::type remove(typename GetPtrHelper::PtrType); private: void unlink(Node*); void unlinkAndDelete(Node*); void appendNode(Node*); void prependNode(Node*); void insertNodeBefore(Node* beforeNode, Node* newNode); void deleteAllNodes(); iterator makeIterator(Node*); const_iterator makeConstIterator(Node*) const; HashTable m_impl; Node* m_head { nullptr }; Node* m_tail { nullptr }; }; template struct ListHashSetNode #if CHECK_HASHTABLE_ITERATORS : CanMakeWeakPtr, WeakPtrFactoryInitialization::Eager> #endif { WTF_MAKE_STRUCT_FAST_ALLOCATED; template ListHashSetNode(T&& value) : m_value(std::forward(value)) { } ValueArg m_value; ListHashSetNode* m_prev { nullptr }; ListHashSetNode* m_next { nullptr }; }; 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 constexpr bool safeToCompareToEmptyOrDeleted = false; }; template class ListHashSetIterator { WTF_MAKE_FAST_ALLOCATED; private: typedef ListHashSet ListHashSetType; typedef ListHashSetIterator iterator; typedef ListHashSetConstIterator const_iterator; typedef ListHashSetNode Node; typedef ValueArg ValueType; friend class ListHashSet; ListHashSetIterator(const ListHashSetType* set, Node* position) : m_iterator(set, position) { } public: typedef ptrdiff_t difference_type; typedef ValueType value_type; typedef ValueType* pointer; typedef ValueType& reference; typedef std::bidirectional_iterator_tag iterator_category; ListHashSetIterator() { } // default copy, assignment and destructor are OK ValueType* get() const { return const_cast(m_iterator.get()); } ValueType& operator*() const { return *get(); } ValueType* operator->() const { return get(); } iterator& operator++() { ++m_iterator; return *this; } // postfix ++ intentionally omitted iterator& operator--() { --m_iterator; return *this; } // postfix -- intentionally omitted // Comparison. bool operator==(const iterator& other) const { return m_iterator == other.m_iterator; } bool operator!=(const iterator& 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 class ListHashSetConstIterator { WTF_MAKE_FAST_ALLOCATED; private: typedef ListHashSet ListHashSetType; typedef ListHashSetIterator iterator; typedef ListHashSetConstIterator const_iterator; typedef ListHashSetNode Node; typedef ValueArg ValueType; friend class ListHashSet; friend class ListHashSetIterator; ListHashSetConstIterator(const ListHashSetType* set, Node* position) : m_set(set) , m_position(position) #if CHECK_HASHTABLE_ITERATORS , m_weakSet(makeWeakPtr(set)) , m_weakPosition(makeWeakPtr(position)) #endif { } public: typedef ptrdiff_t difference_type; typedef const ValueType value_type; typedef const ValueType* pointer; typedef const ValueType& reference; typedef std::bidirectional_iterator_tag iterator_category; ListHashSetConstIterator() { } const ValueType* get() const { #if CHECK_HASHTABLE_ITERATORS ASSERT(m_weakPosition); #endif return std::addressof(m_position->m_value); } const ValueType& operator*() const { return *get(); } const ValueType* operator->() const { return get(); } const_iterator& operator++() { #if CHECK_HASHTABLE_ITERATORS ASSERT(m_weakPosition); #endif ASSERT(m_position); m_position = m_position->m_next; #if CHECK_HASHTABLE_ITERATORS m_weakPosition = makeWeakPtr(m_position); #endif return *this; } // postfix ++ intentionally omitted const_iterator& operator--() { #if CHECK_HASHTABLE_ITERATORS ASSERT(m_weakSet); m_weakPosition.get(); #endif ASSERT(m_position != m_set->m_head); if (!m_position) m_position = m_set->m_tail; else m_position = m_position->m_prev; #if CHECK_HASHTABLE_ITERATORS m_weakPosition = makeWeakPtr(m_position); #endif return *this; } // postfix -- intentionally omitted // Comparison. bool operator==(const const_iterator& other) const { return m_position == other.m_position; } bool operator!=(const const_iterator& other) const { return m_position != other.m_position; } private: Node* node() { return m_position; } const ListHashSetType* m_set { nullptr }; Node* m_position { nullptr }; #if CHECK_HASHTABLE_ITERATORS WeakPtr m_weakSet; WeakPtr m_weakPosition; #endif }; 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, U&& key, V&&) { location = new T(std::forward(key)); } }; template inline ListHashSet::ListHashSet(std::initializer_list initializerList) { for (const auto& value : initializerList) add(value); } template inline ListHashSet::ListHashSet(const ListHashSet& other) { for (auto it = other.begin(), end = other.end(); it != end; ++it) add(*it); } template inline ListHashSet& ListHashSet::operator=(const ListHashSet& other) { ListHashSet tmp(other); swap(tmp); return *this; } template inline ListHashSet::ListHashSet(ListHashSet&& other) : m_impl(WTFMove(other.m_impl)) , m_head(std::exchange(other.m_head, nullptr)) , m_tail(std::exchange(other.m_tail, nullptr)) { } template inline ListHashSet& ListHashSet::operator=(ListHashSet&& other) { ListHashSet movedSet(WTFMove(other)); swap(movedSet); 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); } template inline ListHashSet::~ListHashSet() { deleteAllNodes(); } template inline unsigned ListHashSet::size() const { return m_impl.size(); } template inline unsigned ListHashSet::capacity() const { return m_impl.capacity(); } template inline bool ListHashSet::isEmpty() const { return m_impl.isEmpty(); } template inline T& ListHashSet::first() { ASSERT(!isEmpty()); return m_head->m_value; } template inline void ListHashSet::removeFirst() { takeFirst(); } template inline T ListHashSet::takeFirst() { ASSERT(!isEmpty()); auto it = m_impl.find(m_head); T result = WTFMove((*it)->m_value); m_impl.remove(it); unlinkAndDelete(m_head); return result; } 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() { takeLast(); } template inline T ListHashSet::takeLast() { ASSERT(!isEmpty()); auto it = m_impl.find(m_tail); T result = WTFMove((*it)->m_value); m_impl.remove(it); unlinkAndDelete(m_tail); return result; } template inline auto ListHashSet::find(const ValueType& value) -> iterator { auto it = m_impl.template find(value); if (it == m_impl.end()) return end(); return makeIterator(*it); } template inline auto ListHashSet::find(const ValueType& value) const -> const_iterator { auto 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 auto ListHashSet::find(const T& value) -> iterator { auto it = m_impl.template find>(value); if (it == m_impl.end()) return end(); return makeIterator(*it); } template template inline auto ListHashSet::find(const T& value) const -> const_iterator { auto 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(const ValueType& value) const { return m_impl.template contains(value); } template auto ListHashSet::add(const ValueType& value) -> AddResult { auto result = m_impl.template add(value, nullptr); if (result.isNewEntry) appendNode(*result.iterator); return AddResult(makeIterator(*result.iterator), result.isNewEntry); } template auto ListHashSet::add(ValueType&& value) -> AddResult { auto result = m_impl.template add(WTFMove(value), nullptr); if (result.isNewEntry) appendNode(*result.iterator); return AddResult(makeIterator(*result.iterator), result.isNewEntry); } template auto ListHashSet::appendOrMoveToLast(const ValueType& value) -> AddResult { auto result = m_impl.template add(value, nullptr); Node* node = *result.iterator; if (!result.isNewEntry) unlink(node); appendNode(node); return AddResult(makeIterator(*result.iterator), result.isNewEntry); } template auto ListHashSet::appendOrMoveToLast(ValueType&& value) -> AddResult { auto result = m_impl.template add(WTFMove(value), nullptr); Node* node = *result.iterator; if (!result.isNewEntry) unlink(node); appendNode(node); return AddResult(makeIterator(*result.iterator), result.isNewEntry); } template auto ListHashSet::prependOrMoveToFirst(const ValueType& value) -> AddResult { auto result = m_impl.template add(value, nullptr); Node* node = *result.iterator; if (!result.isNewEntry) unlink(node); prependNode(node); return AddResult(makeIterator(*result.iterator), result.isNewEntry); } template auto ListHashSet::prependOrMoveToFirst(ValueType&& value) -> AddResult { auto result = m_impl.template add(WTFMove(value), nullptr); Node* node = *result.iterator; if (!result.isNewEntry) unlink(node); prependNode(node); return AddResult(makeIterator(*result.iterator), result.isNewEntry); } template auto ListHashSet::insertBefore(const ValueType& beforeValue, const ValueType& newValue) -> AddResult { return insertBefore(find(beforeValue), newValue); } template auto ListHashSet::insertBefore(const ValueType& beforeValue, ValueType&& newValue) -> AddResult { return insertBefore(find(beforeValue), WTFMove(newValue)); } template auto ListHashSet::insertBefore(iterator it, const ValueType& newValue) -> AddResult { auto result = m_impl.template add(newValue, nullptr); if (result.isNewEntry) insertNodeBefore(it.node(), *result.iterator); return AddResult(makeIterator(*result.iterator), result.isNewEntry); } template auto ListHashSet::insertBefore(iterator it, ValueType&& newValue) -> AddResult { auto result = m_impl.template add(WTFMove(newValue), nullptr); if (result.isNewEntry) insertNodeBefore(it.node(), *result.iterator); return AddResult(makeIterator(*result.iterator), result.isNewEntry); } template inline bool ListHashSet::remove(iterator it) { if (it == end()) return false; m_impl.remove(it.node()); unlinkAndDelete(it.node()); return true; } template inline bool ListHashSet::remove(const ValueType& value) { return remove(find(value)); } template inline void ListHashSet::clear() { deleteAllNodes(); m_impl.clear(); m_head = nullptr; m_tail = nullptr; } template template inline auto ListHashSet::find(typename GetPtrHelper::PtrType value) -> typename std::enable_if::value, iterator>::type { auto it = m_impl.template find(value); if (it == m_impl.end()) return end(); return makeIterator(*it); } template template inline auto ListHashSet::find(typename GetPtrHelper::PtrType value) const -> typename std::enable_if::value, const_iterator>::type { auto it = m_impl.template find(value); if (it == m_impl.end()) return end(); return makeConstIterator(*it); } template template inline auto ListHashSet::contains(typename GetPtrHelper::PtrType value) const -> typename std::enable_if::value, bool>::type { return m_impl.template contains(value); } template template inline auto ListHashSet::insertBefore(typename GetPtrHelper::PtrType beforeValue, const ValueType& newValue) -> typename std::enable_if::value, AddResult>::type { return insertBefore(find(beforeValue), newValue); } template template inline auto ListHashSet::insertBefore(typename GetPtrHelper::PtrType beforeValue, ValueType&& newValue) -> typename std::enable_if::value, AddResult>::type { return insertBefore(find(beforeValue), WTFMove(newValue)); } template template inline auto ListHashSet::remove(typename GetPtrHelper::PtrType value) -> typename std::enable_if::value, bool>::type { return remove(find(value)); } template void ListHashSet::unlink(Node* node) { if (!node->m_prev) { ASSERT(node == m_head); m_head = node->m_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->m_prev; } else { ASSERT(node != m_tail); node->m_next->m_prev = node->m_prev; } } template void ListHashSet::unlinkAndDelete(Node* node) { unlink(node); delete node; } template void ListHashSet::appendNode(Node* node) { node->m_prev = m_tail; node->m_next = nullptr; 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 = nullptr; 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->m_next; node; node = next, next = node ? node->m_next : nullptr) delete node; } template inline auto ListHashSet::makeIterator(Node* position) -> iterator { return iterator(this, position); } template inline auto ListHashSet::makeConstIterator(Node* position) const -> const_iterator { return const_iterator(this, position); } } // namespace WTF using WTF::ListHashSet;