mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 12:22:44 +00:00 
			
		
		
		
	 14c8373eb0
			
		
	
	
		14c8373eb0
		
	
	
	
	
		
			
			This makes the user-facing type only take the node member pointer, and lets the compiler figure out the other needed types from that.
		
			
				
	
	
		
			228 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #include <AK/IntrusiveDetails.h>
 | |
| #include <AK/RedBlackTree.h>
 | |
| 
 | |
| namespace AK::Detail {
 | |
| 
 | |
| template<Integral K, typename V, typename Container = RawPtr<V>>
 | |
| class IntrusiveRedBlackTreeNode;
 | |
| 
 | |
| struct ExtractIntrusiveRedBlackTreeTypes {
 | |
|     template<typename K, typename V, typename Container, typename T>
 | |
|     static K key(IntrusiveRedBlackTreeNode<K, V, Container> T::*x);
 | |
|     template<typename K, typename V, typename Container, typename T>
 | |
|     static V value(IntrusiveRedBlackTreeNode<K, V, Container> T::*x);
 | |
|     template<typename K, typename V, typename Container, typename T>
 | |
|     static Container container(IntrusiveRedBlackTreeNode<K, V, Container> T::*x);
 | |
| };
 | |
| 
 | |
| template<Integral K, typename V, typename Container = RawPtr<V>>
 | |
| using SubstitutedIntrusiveRedBlackTreeNode = IntrusiveRedBlackTreeNode<K, V, typename Detail::SubstituteIntrusiveContainerType<V, Container>::Type>;
 | |
| 
 | |
| template<Integral K, typename V, typename Container, SubstitutedIntrusiveRedBlackTreeNode<K, V, Container> V::*member>
 | |
| class IntrusiveRedBlackTree : public BaseRedBlackTree<K> {
 | |
| 
 | |
| public:
 | |
|     IntrusiveRedBlackTree() = default;
 | |
|     virtual ~IntrusiveRedBlackTree() override
 | |
|     {
 | |
|         clear();
 | |
|     }
 | |
| 
 | |
|     using BaseTree = BaseRedBlackTree<K>;
 | |
|     using TreeNode = SubstitutedIntrusiveRedBlackTreeNode<K, V, Container>;
 | |
| 
 | |
|     Container find(K key)
 | |
|     {
 | |
|         auto* node = static_cast<TreeNode*>(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<TreeNode*>(BaseTree::find_largest_not_above(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<typename BaseTree::Node&>(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<typename ElementType>
 | |
|     class BaseIterator {
 | |
|     public:
 | |
|         BaseIterator() = default;
 | |
|         bool operator!=(const BaseIterator& 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<TreeNode*>(BaseTree::successor(m_node));
 | |
|             return *this;
 | |
|         }
 | |
|         BaseIterator& operator--()
 | |
|         {
 | |
|             if (!m_prev)
 | |
|                 return *this;
 | |
|             m_node = m_prev;
 | |
|             m_prev = static_cast<TreeNode*>(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<V>;
 | |
|     Iterator begin() { return Iterator(static_cast<TreeNode*>(this->m_minimum)); }
 | |
|     Iterator end() { return {}; }
 | |
|     Iterator begin_from(K key) { return Iterator(static_cast<TreeNode*>(BaseTree::find(this->m_root, key))); }
 | |
| 
 | |
|     using ConstIterator = BaseIterator<const V>;
 | |
|     ConstIterator begin() const { return ConstIterator(static_cast<TreeNode*>(this->m_minimum)); }
 | |
|     ConstIterator end() const { return {}; }
 | |
|     ConstIterator begin_from(K key) const { return ConstIterator(static_cast<TreeNode*>(BaseTree::find(this->m_rootF, key))); }
 | |
| 
 | |
|     bool remove(K key)
 | |
|     {
 | |
|         auto* node = static_cast<TreeNode*>(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<TreeNode*>(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<TreeNode*>(node->right_child));
 | |
|         node->right_child = nullptr;
 | |
|         clear_nodes(static_cast<TreeNode*>(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<V*>(bit_cast<u8*>(&node) - bit_cast<u8*>(member));
 | |
|     }
 | |
| };
 | |
| 
 | |
| template<Integral K, typename V, typename Container>
 | |
| class IntrusiveRedBlackTreeNode : public BaseRedBlackTree<K>::Node {
 | |
| public:
 | |
|     ~IntrusiveRedBlackTreeNode()
 | |
|     {
 | |
|         VERIFY(!is_in_tree());
 | |
|     }
 | |
| 
 | |
|     [[nodiscard]] bool is_in_tree() const
 | |
|     {
 | |
|         return m_in_tree;
 | |
|     }
 | |
| 
 | |
|     [[nodiscard]] K key() const
 | |
|     {
 | |
|         return BaseRedBlackTree<K>::Node::key;
 | |
|     }
 | |
| 
 | |
|     static constexpr bool IsRaw = IsPointer<Container>;
 | |
| 
 | |
| #ifndef __clang__
 | |
| private:
 | |
|     template<Integral TK, typename TV, typename TContainer, SubstitutedIntrusiveRedBlackTreeNode<TK, TV, TContainer> TV::*member>
 | |
|     friend class ::AK::Detail::IntrusiveRedBlackTree;
 | |
| #endif
 | |
| 
 | |
|     bool m_in_tree { false };
 | |
|     [[no_unique_address]] SelfReferenceIfNeeded<Container, IsRaw> 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<Integral K, typename V, SubstitutedIntrusiveRedBlackTreeNode<K, V, NonnullRefPtr<V>> V::*member>
 | |
| class IntrusiveRedBlackTree<K, V, NonnullRefPtr<V>, member> : public IntrusiveRedBlackTree<K, V, RefPtr<V>, member> {
 | |
| public:
 | |
|     [[nodiscard]] NonnullRefPtr<V> find(K key) const { return IntrusiveRedBlackTree<K, V, RefPtr<V>, member>::find(key).release_nonnull(); }
 | |
|     [[nodiscard]] NonnullRefPtr<V> find_largest_not_above(K key) const { return IntrusiveRedBlackTree<K, V, RefPtr<V>, member>::find_largest_not_above(key).release_nonnull(); }
 | |
| };
 | |
| 
 | |
| }
 | |
| 
 | |
| namespace AK {
 | |
| 
 | |
| template<Integral K, typename V, typename Container = RawPtr<K>>
 | |
| using IntrusiveRedBlackTreeNode = Detail::SubstitutedIntrusiveRedBlackTreeNode<K, V, Container>;
 | |
| 
 | |
| template<auto member>
 | |
| using IntrusiveRedBlackTree = Detail::IntrusiveRedBlackTree<
 | |
|     decltype(Detail::ExtractIntrusiveRedBlackTreeTypes::key(member)),
 | |
|     decltype(Detail::ExtractIntrusiveRedBlackTreeTypes::value(member)),
 | |
|     decltype(Detail::ExtractIntrusiveRedBlackTreeTypes::container(member)),
 | |
|     member>;
 | |
| 
 | |
| }
 | |
| 
 | |
| using AK::IntrusiveRedBlackTree;
 | |
| using AK::IntrusiveRedBlackTreeNode;
 |