/* * Copyright (c) 2018-2020, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #ifdef KERNEL # include #endif namespace AK::Detail { template> class IntrusiveListNode; struct ExtractIntrusiveListTypes { template static V value(IntrusiveListNode T::*x); template static Container container(IntrusiveListNode T::*x); }; template> using SubstitutedIntrusiveListNode = IntrusiveListNode::Type>; template class IntrusiveListStorage { private: friend class IntrusiveListNode; template T_::*member> friend class IntrusiveList; SubstitutedIntrusiveListNode* m_first { nullptr }; SubstitutedIntrusiveListNode* m_last { nullptr }; }; template T::*member> class IntrusiveList { AK_MAKE_NONCOPYABLE(IntrusiveList); AK_MAKE_NONMOVABLE(IntrusiveList); public: IntrusiveList() = default; ~IntrusiveList(); void clear(); [[nodiscard]] bool is_empty() const; [[nodiscard]] size_t size_slow() const; void append(T& n); void prepend(T& n); void insert_before(T&, T&); void remove(T& n); [[nodiscard]] bool contains(T const&) 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)) { } T const& operator*() const { return *m_value; } auto operator->() const { return m_value; } T& operator*() { return *m_value; } auto operator->() { return m_value; } bool operator==(Iterator const& other) const { return other.m_value == m_value; } Iterator& operator++() { m_value = IntrusiveList::next(m_value); return *this; } Iterator& erase(); private: T* m_value { nullptr }; }; Iterator begin(); Iterator end() { return Iterator {}; } class ReverseIterator { public: ReverseIterator() = default; ReverseIterator(T* value) : m_value(move(value)) { } T const& operator*() const { return *m_value; } auto operator->() const { return m_value; } T& operator*() { return *m_value; } auto operator->() { return m_value; } bool operator==(ReverseIterator const& other) const { return other.m_value == m_value; } ReverseIterator& operator++() { m_value = IntrusiveList::prev(m_value); return *this; } ReverseIterator& erase(); private: T* m_value { nullptr }; }; ReverseIterator rbegin(); ReverseIterator rend() { return ReverseIterator {}; } class ConstIterator { public: ConstIterator() = default; ConstIterator(T const* value) : m_value(value) { } T const& operator*() const { return *m_value; } auto operator->() const { return m_value; } bool operator==(ConstIterator const& other) const { return other.m_value == m_value; } ConstIterator& operator++() { m_value = IntrusiveList::next(m_value); return *this; } private: T const* m_value { nullptr }; }; ConstIterator begin() const; ConstIterator end() const { return ConstIterator {}; } private: static T* next(T* current); static T* prev(T* current); static T const* next(T const* current); static T const* prev(T const* current); static T* node_to_value(SubstitutedIntrusiveListNode& node); IntrusiveListStorage m_storage; }; template class IntrusiveListNode { public: ~IntrusiveListNode(); void remove(); bool is_in_list() const; static constexpr bool IsRaw = IsPointer; // Note: For some reason, clang does not consider `member` as declared here, and as declared above (`SubstitutedIntrusiveListNode T::*`) // to be of equal types. so for now, just make the members public on clang. #if !defined(AK_COMPILER_CLANG) private: template T_::*member> friend class ::AK::Detail::IntrusiveList; #endif IntrusiveListStorage* m_storage = nullptr; SubstitutedIntrusiveListNode* m_next = nullptr; SubstitutedIntrusiveListNode* m_prev = nullptr; [[no_unique_address]] 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() { 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 size_t IntrusiveList::size_slow() const { size_t size = 0; auto it_end = end(); for (auto it = begin(); it != it_end; ++it) { ++size; } return size; } 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) { remove(n); auto& nnode = n.*member; 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::insert_before(T& bn, T& n) { remove(n); auto& new_node = n.*member; auto& before_node = bn.*member; new_node.m_storage = &m_storage; new_node.m_next = &before_node; new_node.m_prev = before_node.m_prev; if (before_node.m_prev) before_node.m_prev->m_next = &new_node; before_node.m_prev = &new_node; if (m_storage.m_first == &before_node) { m_storage.m_first = &new_node; } if constexpr (!RemoveReference::IsRaw) new_node.m_self.reference = &n; } 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(T const& 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 (Container ptr = first()) { remove(*ptr); return ptr; } return nullptr; } template T::*member> inline Container IntrusiveList::take_last() { if (Container 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 T const* IntrusiveList::next(T const* current) { auto& nextnode = (current->*member).m_next; T const* nextstruct = nextnode ? node_to_value(*nextnode) : nullptr; return nextstruct; } template T::*member> inline T const* IntrusiveList::prev(T const* current) { auto& prevnode = (current->*member).m_prev; T const* prevstruct = prevnode ? node_to_value(*prevnode) : nullptr; return prevstruct; } 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 T* IntrusiveList::prev(T* current) { auto& prevnode = (current->*member).m_prev; T* prevstruct = prevnode ? node_to_value(*prevnode) : nullptr; return prevstruct; } 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::ReverseIterator IntrusiveList::rbegin() { return m_storage.m_last ? ReverseIterator(node_to_value(*m_storage.m_last)) : ReverseIterator(); } 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(SubstitutedIntrusiveListNode& 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() { VERIFY(!is_in_list()); } 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 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> 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(); } }; #ifdef KERNEL // Specialise IntrusiveList for NonnullLockRefPtr // By default, intrusive lists cannot contain null entries anyway, so switch to LockRefPtr // and just make the user-facing functions deref the pointers. template> T::*member> class IntrusiveList, member> : public IntrusiveList, member> { public: [[nodiscard]] NonnullLockRefPtr first() const { return *IntrusiveList, member>::first(); } [[nodiscard]] NonnullLockRefPtr last() const { return *IntrusiveList, member>::last(); } [[nodiscard]] NonnullLockRefPtr take_first() { return *IntrusiveList, member>::take_first(); } [[nodiscard]] NonnullLockRefPtr take_last() { return *IntrusiveList, member>::take_last(); } }; #endif } namespace AK { template> using IntrusiveListNode = Detail::SubstitutedIntrusiveListNode; template using IntrusiveList = Detail::IntrusiveList< decltype(Detail::ExtractIntrusiveListTypes::value(member)), decltype(Detail::ExtractIntrusiveListTypes::container(member)), member>; } #if USING_AK_GLOBALLY using AK::IntrusiveList; using AK::IntrusiveListNode; #endif