diff --git a/AK/IntrusiveRedBlackTree.h b/AK/IntrusiveRedBlackTree.h deleted file mode 100644 index 28c01e8ffb..0000000000 --- a/AK/IntrusiveRedBlackTree.h +++ /dev/null @@ -1,248 +0,0 @@ -/* - * 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) - { -#ifdef AK_OS_WINDOWS - // NOTE: https://learn.microsoft.com/en-us/cpp/build/reference/vmb-vmg-representation-method?view=msvc-170 - static_assert(sizeof(member) == 4); - auto distance = bit_cast(static_cast(bit_cast(member))); - return bit_cast(bit_cast(&node) - distance); -#else - return bit_cast(bit_cast(&node) - bit_cast(member)); -#endif - } -}; - -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 diff --git a/Tests/AK/CMakeLists.txt b/Tests/AK/CMakeLists.txt index 7bfb14d00f..8501632763 100644 --- a/Tests/AK/CMakeLists.txt +++ b/Tests/AK/CMakeLists.txt @@ -41,7 +41,6 @@ set(AK_TEST_SOURCES TestInsertionSort.cpp TestIntegerMath.cpp TestIntrusiveList.cpp - TestIntrusiveRedBlackTree.cpp TestJSON.cpp TestLEB128.cpp TestMemory.cpp diff --git a/Tests/AK/TestIntrusiveRedBlackTree.cpp b/Tests/AK/TestIntrusiveRedBlackTree.cpp deleted file mode 100644 index a8f12b7804..0000000000 --- a/Tests/AK/TestIntrusiveRedBlackTree.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include -#include -#include -#include -#include - -class IntrusiveTest { -public: - IntrusiveTest(int value) - : m_some_value(value) - { - } - - IntrusiveRedBlackTreeNode> m_tree_node; - int m_some_value; -}; -using IntrusiveRBTree = IntrusiveRedBlackTree<&IntrusiveTest::m_tree_node>; - -TEST_CASE(construct) -{ - IntrusiveRBTree empty; - EXPECT(empty.is_empty()); - EXPECT(empty.size() == 0); -} - -TEST_CASE(ints) -{ - IntrusiveRBTree test; - IntrusiveTest first { 10 }; - test.insert(1, first); - IntrusiveTest second { 20 }; - test.insert(3, second); - IntrusiveTest third { 30 }; - test.insert(2, third); - EXPECT_EQ(test.size(), 3u); - EXPECT_EQ(test.find(3)->m_some_value, 20); - EXPECT_EQ(test.find(2)->m_some_value, 30); - EXPECT_EQ(test.find(1)->m_some_value, 10); - EXPECT(!test.remove(4)); - EXPECT(test.remove(2)); - EXPECT(test.remove(1)); - EXPECT(test.remove(3)); - EXPECT_EQ(test.size(), 0u); -} - -TEST_CASE(largest_smaller_than) -{ - IntrusiveRBTree test; - IntrusiveTest first { 10 }; - test.insert(1, first); - IntrusiveTest second { 20 }; - test.insert(11, second); - IntrusiveTest third { 30 }; - test.insert(21, third); - EXPECT_EQ(test.size(), 3u); - EXPECT_EQ(test.find_largest_not_above(3)->m_some_value, 10); - EXPECT_EQ(test.find_largest_not_above(17)->m_some_value, 20); - EXPECT_EQ(test.find_largest_not_above(22)->m_some_value, 30); - EXPECT_EQ(test.find_largest_not_above(-5), nullptr); - VERIFY(test.remove(1)); - VERIFY(test.remove(11)); - VERIFY(test.remove(21)); -} - -TEST_CASE(key_ordered_iteration) -{ - constexpr auto amount = 10000; - IntrusiveRBTree test; - Vector> m_entries; - Array keys {}; - - // generate random key order - for (int i = 0; i < amount; i++) { - keys[i] = i; - } - for (size_t i = 0; i < amount; i++) { - swap(keys[i], keys[get_random() % amount]); - } - - // insert random keys - for (size_t i = 0; i < amount; i++) { - auto entry = make(keys[i]); - test.insert(keys[i], *entry); - m_entries.append(move(entry)); - } - - // check key-ordered iteration - int index = 0; - for (auto& value : test) { - EXPECT(value.m_some_value == index++); - } - - // ensure we can remove all of them (aka, tree structure is not destroyed somehow) - for (size_t i = 0; i < amount; i++) { - EXPECT(test.remove(i)); - } -} - -TEST_CASE(clear) -{ - IntrusiveRBTree test; - Vector> m_entries; - for (size_t i = 0; i < 1000; i++) { - auto entry = make(i); - test.insert(i, *entry); - m_entries.append(move(entry)); - } - test.clear(); - EXPECT_EQ(test.size(), 0u); -} - -class IntrusiveRefPtrTest : public RefCounted { -public: - IntrusiveRefPtrTest() - { - } - - IntrusiveRedBlackTreeNode> m_tree_node; -}; -using IntrusiveRefPtrRBTree = IntrusiveRedBlackTree<&IntrusiveRefPtrTest::m_tree_node>; - -TEST_CASE(intrusive_ref_ptr_no_ref_leaks) -{ - auto item = adopt_ref(*new IntrusiveRefPtrTest()); - EXPECT_EQ(1u, item->ref_count()); - IntrusiveRefPtrRBTree ref_tree; - - ref_tree.insert(0, *item); - EXPECT_EQ(2u, item->ref_count()); - - ref_tree.remove(0); - EXPECT_EQ(1u, item->ref_count()); -} - -TEST_CASE(intrusive_ref_ptr_clear) -{ - auto item = adopt_ref(*new IntrusiveRefPtrTest()); - EXPECT_EQ(1u, item->ref_count()); - IntrusiveRefPtrRBTree ref_tree; - - ref_tree.insert(0, *item); - EXPECT_EQ(2u, item->ref_count()); - - ref_tree.clear(); - EXPECT_EQ(1u, item->ref_count()); -} - -TEST_CASE(intrusive_ref_ptr_destructor) -{ - auto item = adopt_ref(*new IntrusiveRefPtrTest()); - EXPECT_EQ(1u, item->ref_count()); - - { - IntrusiveRefPtrRBTree ref_tree; - ref_tree.insert(0, *item); - EXPECT_EQ(2u, item->ref_count()); - } - - EXPECT_EQ(1u, item->ref_count()); -} - -class IntrusiveNonnullRefPtrTest : public RefCounted { -public: - IntrusiveNonnullRefPtrTest() - { - } - - IntrusiveRedBlackTreeNode> m_tree_node; -}; -using IntrusiveNonnullRefPtrRBTree = IntrusiveRedBlackTree<&IntrusiveNonnullRefPtrTest::m_tree_node>; - -TEST_CASE(intrusive_nonnull_ref_ptr_intrusive) -{ - auto item = adopt_ref(*new IntrusiveNonnullRefPtrTest()); - EXPECT_EQ(1u, item->ref_count()); - IntrusiveNonnullRefPtrRBTree nonnull_ref_tree; - - nonnull_ref_tree.insert(0, *item); - EXPECT_EQ(2u, item->ref_count()); - EXPECT(!nonnull_ref_tree.is_empty()); - - nonnull_ref_tree.remove(0); - EXPECT_EQ(1u, item->ref_count()); - - EXPECT(nonnull_ref_tree.is_empty()); -}