/* * Copyright (c) 2018-2020, Andreas Kling * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include #include #include #include namespace AK { template> class IntrusiveListNode; template class IntrusiveListStorage { private: friend class IntrusiveListNode; template T_::*member> friend class IntrusiveList; IntrusiveListNode* m_first { nullptr }; IntrusiveListNode* m_last { nullptr }; }; template T::*member> class IntrusiveList { public: IntrusiveList(); ~IntrusiveList(); void clear(); [[nodiscard]] bool is_empty() const; void append(T& n); void prepend(T& n); void remove(T& n); [[nodiscard]] bool contains(const T&) const; [[nodiscard]] Container first() const; [[nodiscard]] Container last() const; [[nodiscard]] Container take_first(); [[nodiscard]] Container take_last(); class Iterator { public: Iterator() = default; Iterator(T* value) : m_value(move(value)) { } const T& operator*() const { return *m_value; } auto operator->() const { return m_value; } T& operator*() { return *m_value; } auto operator->() { return m_value; } bool operator==(const Iterator& other) const { return other.m_value == m_value; } bool operator!=(const Iterator& other) const { return !(*this == other); } Iterator& operator++() { m_value = IntrusiveList::next(m_value); return *this; } Iterator& erase(); private: T* m_value { nullptr }; }; Iterator begin(); Iterator end() { return Iterator {}; } class ConstIterator { public: ConstIterator() = default; ConstIterator(const T* value) : m_value(value) { } const T& operator*() const { return *m_value; } auto operator->() const { return m_value; } bool operator==(const ConstIterator& other) const { return other.m_value == m_value; } bool operator!=(const ConstIterator& other) const { return !(*this == other); } ConstIterator& operator++() { m_value = IntrusiveList::next(m_value); return *this; } private: const T* m_value { nullptr }; }; ConstIterator begin() const; ConstIterator end() const { return ConstIterator {}; } private: static T* next(T* current); static const T* next(const T* current); static T* node_to_value(IntrusiveListNode& node); IntrusiveListStorage m_storage; }; template struct SelfReferenceIfNeeded { Contained reference = nullptr; }; template struct SelfReferenceIfNeeded { }; template class IntrusiveListNode { public: ~IntrusiveListNode(); void remove(); bool is_in_list() const; static constexpr bool IsRaw = IsPointer; private: template T_::*member> friend class IntrusiveList; IntrusiveListStorage* m_storage = nullptr; IntrusiveListNode* m_next = nullptr; IntrusiveListNode* m_prev = nullptr; SelfReferenceIfNeeded m_self; }; template T::*member> inline typename IntrusiveList::Iterator& IntrusiveList::Iterator::erase() { auto old = m_value; m_value = IntrusiveList::next(m_value); (old->*member).remove(); return *this; } template T::*member> inline IntrusiveList::IntrusiveList() { } template T::*member> inline IntrusiveList::~IntrusiveList() { clear(); } template T::*member> inline void IntrusiveList::clear() { while (m_storage.m_first) m_storage.m_first->remove(); } template T::*member> inline bool IntrusiveList::is_empty() const { return m_storage.m_first == nullptr; } template T::*member> inline void IntrusiveList::append(T& n) { remove(n); auto& nnode = n.*member; nnode.m_storage = &m_storage; nnode.m_prev = m_storage.m_last; nnode.m_next = nullptr; if constexpr (!RemoveReference::IsRaw) nnode.m_self.reference = &n; // Note: Self-reference ensures that the object will keep a ref to itself when the Container is a smart pointer. if (m_storage.m_last) m_storage.m_last->m_next = &nnode; m_storage.m_last = &nnode; if (!m_storage.m_first) m_storage.m_first = &nnode; } template T::*member> inline void IntrusiveList::prepend(T& n) { auto& nnode = n.*member; if (nnode.m_storage) nnode.remove(); nnode.m_storage = &m_storage; nnode.m_prev = nullptr; nnode.m_next = m_storage.m_first; if constexpr (!RemoveReference::IsRaw) nnode.m_self.reference = &n; if (m_storage.m_first) m_storage.m_first->m_prev = &nnode; m_storage.m_first = &nnode; if (!m_storage.m_last) m_storage.m_last = &nnode; } template T::*member> inline void IntrusiveList::remove(T& n) { auto& nnode = n.*member; if (nnode.m_storage) nnode.remove(); } template T::*member> inline bool IntrusiveList::contains(const T& n) const { auto& nnode = n.*member; return nnode.m_storage == &m_storage; } template T::*member> inline Container IntrusiveList::first() const { return m_storage.m_first ? node_to_value(*m_storage.m_first) : nullptr; } template T::*member> inline Container IntrusiveList::take_first() { if (auto* ptr = first()) { remove(*ptr); return ptr; } return nullptr; } template T::*member> inline Container IntrusiveList::take_last() { if (auto* ptr = last()) { remove(*ptr); return ptr; } return nullptr; } template T::*member> inline Container IntrusiveList::last() const { return m_storage.m_last ? node_to_value(*m_storage.m_last) : nullptr; } template T::*member> inline const T* IntrusiveList::next(const T* current) { auto& nextnode = (current->*member).m_next; const T* nextstruct = nextnode ? node_to_value(*nextnode) : nullptr; return nextstruct; } template T::*member> inline T* IntrusiveList::next(T* current) { auto& nextnode = (current->*member).m_next; T* nextstruct = nextnode ? node_to_value(*nextnode) : nullptr; return nextstruct; } template T::*member> inline typename IntrusiveList::Iterator IntrusiveList::begin() { return m_storage.m_first ? Iterator(node_to_value(*m_storage.m_first)) : Iterator(); } template T::*member> inline typename IntrusiveList::ConstIterator IntrusiveList::begin() const { return m_storage.m_first ? ConstIterator(node_to_value(*m_storage.m_first)) : ConstIterator(); } template T::*member> inline T* IntrusiveList::node_to_value(IntrusiveListNode& node) { // Note: Since this might seem odd, here's an explanation on what this function actually does: // `node` is a reference that resides in some part of the actual value (of type T), the // placement (i.e. offset) of which is described by the pointer-to-data-member parameter // named `member`. // This function effectively takes in the address of the data member, and returns the address // of the value (of type T) holding that member. return bit_cast(bit_cast(&node) - bit_cast(member)); } template inline IntrusiveListNode::~IntrusiveListNode() { if (m_storage) remove(); } template inline void IntrusiveListNode::remove() { VERIFY(m_storage); if (m_storage->m_first == this) m_storage->m_first = m_next; if (m_storage->m_last == this) m_storage->m_last = m_prev; if (m_prev) m_prev->m_next = m_next; if (m_next) m_next->m_prev = m_prev; m_prev = nullptr; m_next = nullptr; m_storage = nullptr; if constexpr (!IsRaw) m_self.reference = nullptr; } template inline bool IntrusiveListNode::is_in_list() const { return m_storage != nullptr; } // Specialise IntrusiveList(Node) for NonnullRefPtr // By default, intrusive lists cannot contain null entries anyway, so switch to RefPtr // and just make the user-facing functions deref the pointers. template struct IntrusiveListNode> : public IntrusiveListNode> { }; template> T::*member> class IntrusiveList, member> : public IntrusiveList, member> { public: [[nodiscard]] NonnullRefPtr first() const { return *IntrusiveList, member>::first(); } [[nodiscard]] NonnullRefPtr last() const { return *IntrusiveList, member>::last(); } [[nodiscard]] NonnullRefPtr take_first() { return *IntrusiveList, member>::take_first(); } [[nodiscard]] NonnullRefPtr take_last() { return *IntrusiveList, member>::take_last(); } }; } using AK::IntrusiveList; using AK::IntrusiveListNode;