/* * Copyright (c) 2021, Idan Horowitz * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include namespace AK::Detail { template> class IntrusiveRedBlackTreeNode; struct ExtractIntrusiveRedBlackTreeTypes { template static K key(IntrusiveRedBlackTreeNode T::*x); template static V value(IntrusiveRedBlackTreeNode T::*x); template static Container container(IntrusiveRedBlackTreeNode T::*x); }; template> using SubstitutedIntrusiveRedBlackTreeNode = IntrusiveRedBlackTreeNode::Type>; template V::*member> class IntrusiveRedBlackTree : public BaseRedBlackTree { public: IntrusiveRedBlackTree() = default; virtual ~IntrusiveRedBlackTree() override { clear(); } using BaseTree = BaseRedBlackTree; using TreeNode = SubstitutedIntrusiveRedBlackTreeNode; Container find(K key) { auto* node = static_cast(BaseTree::find(this->m_root, key)); if (!node) return nullptr; return node_to_value(*node); } Container find_largest_not_above(K key) { auto* node = static_cast(BaseTree::find_largest_not_above(this->m_root, key)); if (!node) return nullptr; return node_to_value(*node); } Container find_smallest_not_below(K key) { auto* node = static_cast(BaseTree::find_smallest_not_below(this->m_root, key)); if (!node) return nullptr; return node_to_value(*node); } void insert(K key, V& value) { auto& node = value.*member; VERIFY(!node.m_in_tree); static_cast(node).key = key; BaseTree::insert(&node); if constexpr (!TreeNode::IsRaw) node.m_self.reference = &value; // Note: Self-reference ensures that the object will keep a ref to itself when the Container is a smart pointer. node.m_in_tree = true; } template class BaseIterator { public: BaseIterator() = default; bool operator!=(BaseIterator const& other) const { return m_node != other.m_node; } BaseIterator& operator++() { if (!m_node) return *this; m_prev = m_node; // the complexity is O(logn) for each successor call, but the total complexity for all elements comes out to O(n), meaning the amortized cost for a single call is O(1) m_node = static_cast(BaseTree::successor(m_node)); return *this; } BaseIterator& operator--() { if (!m_prev) return *this; m_node = m_prev; m_prev = static_cast(BaseTree::predecessor(m_prev)); return *this; } ElementType& operator*() { VERIFY(m_node); return *node_to_value(*m_node); } auto operator->() { VERIFY(m_node); return node_to_value(*m_node); } [[nodiscard]] bool is_end() const { return !m_node; } [[nodiscard]] bool is_begin() const { return !m_prev; } [[nodiscard]] auto key() const { return m_node->key; } private: friend class IntrusiveRedBlackTree; explicit BaseIterator(TreeNode* node, TreeNode* prev = nullptr) : m_node(node) , m_prev(prev) { } TreeNode* m_node { nullptr }; TreeNode* m_prev { nullptr }; }; using Iterator = BaseIterator; Iterator begin() { return Iterator(static_cast(this->m_minimum)); } Iterator end() { return {}; } Iterator begin_from(K key) { return Iterator(static_cast(BaseTree::find(this->m_root, key))); } Iterator begin_from(V& value) { return Iterator(&(value.*member)); } using ConstIterator = BaseIterator; ConstIterator begin() const { return ConstIterator(static_cast(this->m_minimum)); } ConstIterator end() const { return {}; } ConstIterator begin_from(K key) const { return ConstIterator(static_cast(BaseTree::find(this->m_rootF, key))); } ConstIterator begin_from(V const& value) const { return Iterator(&(value.*member)); } bool remove(K key) { auto* node = static_cast(BaseTree::find(this->m_root, key)); if (!node) return false; BaseTree::remove(node); node->right_child = nullptr; node->left_child = nullptr; node->m_in_tree = false; if constexpr (!TreeNode::IsRaw) node->m_self.reference = nullptr; return true; } void clear() { clear_nodes(static_cast(this->m_root)); this->m_root = nullptr; this->m_minimum = nullptr; this->m_size = 0; } private: static void clear_nodes(TreeNode* node) { if (!node) return; clear_nodes(static_cast(node->right_child)); node->right_child = nullptr; clear_nodes(static_cast(node->left_child)); node->left_child = nullptr; node->m_in_tree = false; if constexpr (!TreeNode::IsRaw) node->m_self.reference = nullptr; } static V* node_to_value(TreeNode& node) { return bit_cast(bit_cast(&node) - bit_cast(member)); } }; template class IntrusiveRedBlackTreeNode : public BaseRedBlackTree::Node { public: ~IntrusiveRedBlackTreeNode() { VERIFY(!is_in_tree()); } [[nodiscard]] bool is_in_tree() const { return m_in_tree; } [[nodiscard]] K key() const { return BaseRedBlackTree::Node::key; } static constexpr bool IsRaw = IsPointer; #if !defined(AK_COMPILER_CLANG) private: template TV::*member> friend class ::AK::Detail::IntrusiveRedBlackTree; #endif bool m_in_tree { false }; [[no_unique_address]] SelfReferenceIfNeeded m_self; }; // Specialise IntrusiveRedBlackTree for NonnullRefPtr // By default, red black trees cannot contain null entries anyway, so switch to RefPtr // and just make the user-facing functions deref the pointers. template> V::*member> class IntrusiveRedBlackTree, member> : public IntrusiveRedBlackTree, member> { public: [[nodiscard]] NonnullRefPtr find(K key) const { return IntrusiveRedBlackTree, member>::find(key).release_nonnull(); } [[nodiscard]] NonnullRefPtr find_largest_not_above(K key) const { return IntrusiveRedBlackTree, member>::find_largest_not_above(key).release_nonnull(); } [[nodiscard]] NonnullRefPtr find_smallest_not_below(K key) const { return IntrusiveRedBlackTree, member>::find_smallest_not_below(key).release_nonnull(); } }; } namespace AK { template> using IntrusiveRedBlackTreeNode = Detail::SubstitutedIntrusiveRedBlackTreeNode; template using IntrusiveRedBlackTree = Detail::IntrusiveRedBlackTree< decltype(Detail::ExtractIntrusiveRedBlackTreeTypes::key(member)), decltype(Detail::ExtractIntrusiveRedBlackTreeTypes::value(member)), decltype(Detail::ExtractIntrusiveRedBlackTreeTypes::container(member)), member>; } #if USING_AK_GLOBALLY using AK::IntrusiveRedBlackTree; using AK::IntrusiveRedBlackTreeNode; #endif