/* * Copyright (c) Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include namespace ams::util { template requires std::convertible_to class FixedTree { private: class IteratorBase; friend class IteratorBase; private: enum class Color : u8 { Red = 0, Black = 1, }; static constexpr inline int Index_Nil = -1; static constexpr inline int Index_Leaf = -2; static constexpr inline int Index_BeforeBegin = -3; static constexpr inline int Index_AfterEnd = -4; static constexpr inline size_t max_size = 0x40000000; struct Header { /* "Nintendo Red-Black tree" */ static constexpr u32 Signature = util::ReverseFourCC<'N','N','R','B'>::Code; u32 header_size; u32 header_signature; u32 _08; s32 max_elements; s32 cur_elements; s32 root_index; s32 left_most_index; s32 right_most_index; s32 index_signature; u32 buffer_size; u32 node_size; u32 element_size; u32 _30; u32 _34; u32 _38; u32 _3C; u32 _40; u32 _44; u32 _48; u32 _4C; void InitializeHeader(u32 _08, s32 max_e, s32 cur_e, u32 ind_sig, u32 buf_sz, u32 node_sz, u32 e_sz, u32 _30, u32 _34, u32 _38, u32 _3C, u32 _40, u32 _44) { this->header_size = sizeof(Header); this->header_signature = Signature; this->_08 = _08; this->max_elements = max_e; this->cur_elements = cur_e; this->root_index = Index_Nil; this->left_most_index = Index_Nil; this->right_most_index = Index_Nil; this->index_signature = ind_sig; this->buffer_size = buf_sz; this->node_size = node_sz; this->element_size = e_sz; this->_30 = _30; this->_34 = _34; this->_38 = _38; this->_3C = _3C; this->_40 = _40; this->_44 = _44; this->_48 = 0; this->_4C = 0; } }; static_assert(sizeof(Header) == 0x50); struct IndexPair { int first; int last; }; struct Node { Member m_data; int m_parent; int m_right; int m_left; Color m_color; void SetLeft(int l, Node *n, int p) { m_left = l; n->m_parent = p; } void SetRight(int r, Node *n, int p) { m_right = r; n->m_parent = p; } }; class Iterator; class ConstIterator; class IteratorBase { private: friend class ConstIterator; private: const FixedTree *m_this; int m_index; protected: constexpr ALWAYS_INLINE IteratorBase(const FixedTree *tree, int index) : m_this(tree), m_index(index) { /* ... */ } constexpr bool IsEqualImpl(const IteratorBase &rhs) const { /* Validate pre-conditions. */ AMS_ASSERT(m_this); /* Check for tree equality. */ if (m_this != rhs.m_this) { return false; } /* Check for nil. */ if (m_this->IsNil(m_index) && m_this->IsNil(rhs.m_index)) { return true; } /* Check for index equality. */ return m_index == rhs.m_index; } constexpr IteratorMember &DereferenceImpl() const { /* Validate pre-conditions. */ AMS_ASSERT(m_this); if (!m_this->IsNil(m_index)) { return m_this->m_nodes[m_index].m_data; } else { AMS_ASSERT(false); return m_this->GetNode(std::numeric_limits::max())->m_data; } } constexpr ALWAYS_INLINE IteratorBase &IncrementImpl() { /* Validate pre-conditions. */ AMS_ASSERT(m_this); this->OperateIndex(true); return *this; } constexpr ALWAYS_INLINE IteratorBase &DecrementImpl() { /* Validate pre-conditions. */ AMS_ASSERT(m_this); this->OperateIndex(false); return *this; } constexpr void OperateIndex(bool increment) { if (increment) { /* We're incrementing. */ if (m_index == Index_BeforeBegin) { m_index = 0; } else { m_index = m_this->UncheckedPP(m_index); if (m_this->IsNil(m_index)) { m_index = Index_AfterEnd; } } } else { /* We're decrementing. */ if (m_index == Index_AfterEnd) { m_index = static_cast(m_this->size()) - 1; } else { m_index = m_this->UncheckedMM(m_index); if (m_this->IsNil(m_index)) { m_index = Index_BeforeBegin; } } } } }; class Iterator : public IteratorBase { public: constexpr ALWAYS_INLINE Iterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ } constexpr ALWAYS_INLINE Iterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ } constexpr ALWAYS_INLINE Iterator(const Iterator &rhs) = default; constexpr ALWAYS_INLINE bool operator==(const Iterator &rhs) const { return this->IsEqualImpl(rhs); } constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const { return !(*this == rhs); } constexpr ALWAYS_INLINE IteratorMember &operator*() const { return static_cast(this->DereferenceImpl()); } constexpr ALWAYS_INLINE IteratorMember *operator->() const { return std::addressof(this->operator *()); } constexpr ALWAYS_INLINE Iterator &operator++() { return static_cast(this->IncrementImpl()); } constexpr ALWAYS_INLINE Iterator &operator--() { return static_cast(this->DecrementImpl()); } }; class ConstIterator : public IteratorBase { public: constexpr ALWAYS_INLINE ConstIterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ } constexpr ALWAYS_INLINE ConstIterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ } constexpr ALWAYS_INLINE ConstIterator(const ConstIterator &rhs) = default; constexpr ALWAYS_INLINE ConstIterator(const Iterator &rhs) : IteratorBase(rhs.m_this, rhs.m_index) { /* ... */ } constexpr ALWAYS_INLINE bool operator==(const ConstIterator &rhs) const { return this->IsEqualImpl(rhs); } constexpr ALWAYS_INLINE bool operator!=(const ConstIterator &rhs) const { return !(*this == rhs); } constexpr ALWAYS_INLINE const IteratorMember &operator*() const { return static_cast(this->DereferenceImpl()); } constexpr ALWAYS_INLINE const IteratorMember *operator->() const { return std::addressof(this->operator *()); } constexpr ALWAYS_INLINE ConstIterator &operator++() { return static_cast(this->IncrementImpl()); } constexpr ALWAYS_INLINE ConstIterator &operator--() { return static_cast(this->DecrementImpl()); } }; public: using iterator = Iterator; using const_iterator = ConstIterator; private: impl::AvailableIndexFinder m_index_finder; Node m_dummy_leaf; Node *m_p_dummy_leaf; u8 *m_buffer; Header *m_header; Node *m_nodes; iterator m_end_iterator; public: FixedTree() : m_end_iterator(*this, Index_Nil) { this->SetDummyMemory(); } protected: void InitializeImpl(int num_elements, void *buffer, size_t buffer_size) { /* Check pre-conditions. */ AMS_ASSERT(num_elements > 0); AMS_ASSERT(static_cast(num_elements) <= max_size); AMS_ASSERT(util::IsAligned(reinterpret_cast(buffer), BufferAlignment)); AMS_ASSERT(buffer_size == GetRequiredMemorySize(num_elements)); /* Set buffer. */ m_buffer = static_cast(buffer); m_header = reinterpret_cast
(m_buffer); /* Setup memory. */ this->InitializeMemory(num_elements, buffer_size, impl::AvailableIndexFinder::GetSignature()); /* Check that buffer was set up correctly. */ AMS_ASSERT(static_cast(buffer_size) == m_header->buffer_size); /* Setup dummy leaf. */ this->SetDummyMemory(); } public: static constexpr size_t SizeOfNodes(size_t num_elements) { return util::AlignUp(sizeof(Node) * num_elements, BufferAlignment); } static constexpr size_t SizeOfIndex(size_t num_elements) { return impl::AvailableIndexFinder::GetRequiredMemorySize(num_elements); } static constexpr size_t GetRequiredMemorySize(size_t num_elements) { return sizeof(Header) + SizeOfNodes(num_elements) + SizeOfIndex(num_elements); } private: void SetDummyMemory() { m_dummy_leaf.m_color = Color::Black; m_dummy_leaf.m_parent = Index_Nil; m_dummy_leaf.m_left = Index_Leaf; m_dummy_leaf.m_right = Index_Leaf; m_p_dummy_leaf = std::addressof(m_dummy_leaf); } void InitializeMemory(int num_elements, u32 buffer_size, u32 signature) { /* Initialize the header. */ m_header->InitializeHeader(1, num_elements, 0, signature, buffer_size, sizeof(Node), sizeof(Member), 4, 4, 4, 4, 4, BufferAlignment); /* Setup index finder. */ m_index_finder.Initialize(std::addressof(m_header->cur_elements), std::addressof(m_header->max_elements), m_buffer + sizeof(*m_header) + SizeOfNodes(num_elements)); /* Set nodes array. */ m_nodes = reinterpret_cast(m_buffer + sizeof(*m_header)); } Node *GetNode(int index) const { if (index >= 0) { return m_nodes + index; } else { return m_p_dummy_leaf; } } constexpr ALWAYS_INLINE bool IsNil(int index) const { return index < 0; } constexpr ALWAYS_INLINE bool IsLeaf(int index) const { return index == Index_Leaf; } int GetRoot() const { return m_header->root_index; } void SetRoot(int index) { if (index == Index_Leaf) { index = Index_Nil; } m_header->root_index = index; } int GetLMost() const { return m_header->left_most_index; } void SetLMost(int index) { m_header->left_most_index = index; } int GetRMost() const { return m_header->right_most_index; } void SetRMost(int index) { m_header->right_most_index = index; } int GetParent(int index) const { return this->GetNode(index)->m_parent; } int AcquireIndex() { return m_index_finder.AcquireIndex(); } void ReleaseIndex(int index) { return m_index_finder.ReleaseIndex(index); } int EraseByIndex(int target_index) { /* Setup tracking variables. */ const auto next_index = this->UncheckedPP(target_index); auto *target_node = this->GetNode(target_index); auto a_index = Index_Leaf; auto *a_node = this->GetNode(a_index); auto b_index = Index_Leaf; auto *b_node = this->GetNode(b_index); auto cur_index = target_index; auto *cur_node = this->GetNode(cur_index); if (cur_node->m_left == Index_Leaf) { a_index = cur_node->m_right; a_node = this->GetNode(a_index); m_p_dummy_leaf->m_parent = cur_index; } else { if (cur_node->m_right == Index_Leaf) { a_index = cur_node->m_left; } else { cur_index = next_index; cur_node = this->GetNode(cur_index); a_index = cur_node->m_right; } a_node = this->GetNode(a_index); m_p_dummy_leaf->m_parent = cur_index; } /* Ensure the a node is updated (redundant) */ a_node = this->GetNode(a_index); /* Update relevant metrics/links. */ if (cur_index == target_index) { /* No left, but has right. */ b_index = target_node->m_parent; b_node = this->GetNode(b_index); if (a_index != Index_Leaf) { a_node->m_parent = b_index; } if (this->GetRoot() == target_index) { this->SetRoot(a_index); } else if (b_node->m_left == target_index) { b_node->m_left = a_index; } else { b_node->m_right = a_index; } if (this->GetLMost() == target_index) { this->SetLMost((a_index != Index_Leaf) ? this->FindMinInSubtree(a_index) : b_index); } if (this->GetRMost() == target_index) { this->SetRMost((a_index != Index_Leaf) ? this->FindMaxInSubtree(a_index) : b_index); } } else { /* Has left or doesn't have right. */ /* Fix left links. */ this->GetNode(target_node->m_left)->m_parent = cur_index; cur_node->m_left = target_node->m_left; if (cur_index == target_node->m_right) { b_index = cur_index; b_node = this->GetNode(b_index); } else { b_index = cur_node->m_parent; b_node = this->GetNode(b_index); if (!this->IsNil(a_index)) { a_node->m_parent = b_index; } b_node->m_left = a_index; cur_node->m_right = target_node->m_right; this->GetNode(target_node->m_right)->m_parent = cur_index; } if (this->GetRoot() == target_index) { this->SetRoot(cur_index); } else { if (this->GetNode(target_node->m_parent)->m_left == target_index) { this->GetNode(target_node->m_parent)->m_left = cur_index; } else { this->GetNode(target_node->m_parent)->m_right = cur_index; } } cur_node->m_parent = target_node->m_parent; std::swap(cur_node->m_color, target_node->m_color); } /* Ensure the tree remains balanced. */ if (target_node->m_color == Color::Black) { while (true) { if (a_index == this->GetRoot() || a_node->m_color != Color::Black) { break; } if (a_index == b_node->m_left) { cur_index = b_node->m_right; cur_node = this->GetNode(cur_index); if (cur_node->m_color == Color::Red) { cur_node->m_color = Color::Black; b_node->m_color = Color::Red; AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black); this->RotateLeft(b_index); cur_index = b_node->m_right; cur_node = this->GetNode(cur_index); } if (this->IsNil(cur_index)) { a_index = b_index; a_node = b_node; } else { if (this->GetNode(cur_node->m_left)->m_color != Color::Black || this->GetNode(cur_node->m_right)->m_color != Color::Black) { if (this->GetNode(cur_node->m_right)->m_color == Color::Black) { this->GetNode(cur_node->m_left)->m_color = Color::Black; cur_node->m_color = Color::Red; AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black); this->RotateRight(cur_index); cur_index = b_node->m_right; cur_node = this->GetNode(cur_index); } cur_node->m_color = b_node->m_color; b_node->m_color = Color::Black; this->GetNode(cur_node->m_right)->m_color = Color::Black; this->RotateLeft(b_index); break; } cur_node->m_color = Color::Red; AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black); a_index = b_index; a_node = b_node; } } else { cur_index = b_node->m_left; cur_node = this->GetNode(cur_index); if (cur_node->m_color == Color::Red) { cur_node->m_color = Color::Black; b_node->m_color = Color::Red; AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black); this->RotateRight(b_index); cur_index = b_node->m_left; cur_node = this->GetNode(cur_index); } if (this->IsNil(cur_index)) { a_index = b_index; a_node = b_node; } else { if (this->GetNode(cur_node->m_right)->m_color != Color::Black || this->GetNode(cur_node->m_left)->m_color != Color::Black) { if (this->GetNode(cur_node->m_left)->m_color == Color::Black) { this->GetNode(cur_node->m_right)->m_color = Color::Black; cur_node->m_color = Color::Red; AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black); this->RotateLeft(cur_index); cur_index = b_node->m_left; cur_node = this->GetNode(cur_index); } cur_node->m_color = b_node->m_color; b_node->m_color = Color::Black; this->GetNode(cur_node->m_left)->m_color = Color::Black; this->RotateRight(b_index); break; } cur_node->m_color = Color::Red; AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black); a_index = b_index; a_node = b_node; } } b_index = a_node->m_parent; b_node = this->GetNode(b_index); } a_node->m_color = Color::Black; } /* Release the index. */ this->ReleaseIndex(target_index); return target_index; } int FindIndex(const Member &elem) const { return this->FindIndexSub(this->GetRoot(), elem); } int FindIndexSub(int index, const Member &elem) const { if (index != Index_Nil) { auto *node = this->GetNode(index); if (Compare{}(elem, node->m_data)) { if (!this->IsLeaf(node->m_left)) { return this->FindIndexSub(node->m_left, elem); } } else { if (!Compare{}(node->m_data, elem)) { return index; } if (!this->IsLeaf(node->m_right)) { return this->FindIndexSub(node->m_right, elem); } } } return Index_Nil; } int FindMaxInSubtree(int index) const { int max = index; for (auto *node = this->GetNode(index); !this->IsNil(node->m_right); node = this->GetNode(node->m_right)) { max = node->m_right; } return max; } int FindMinInSubtree(int index) const { int min = index; for (auto *node = this->GetNode(index); !this->IsNil(node->m_left); node = this->GetNode(node->m_left)) { min = node->m_left; } return min; } int InsertAt(bool before, int parent, const Member &elem) { /* Get an index for the new element. */ const auto index = this->AcquireIndex(); /* Create the node. */ auto *node = this->GetNode(index); node->m_color = Color::Red; node->m_parent = parent; node->m_right = Index_Leaf; node->m_left = Index_Leaf; std::memcpy(reinterpret_cast(std::addressof(node->m_data)), reinterpret_cast(std::addressof(elem)), sizeof(node->m_data)); /* Fix up the parent node. */ auto *parent_node = this->GetNode(parent); if (before) { parent_node->m_left = index; if (parent == this->GetLMost()) { this->SetLMost(index); } } else { parent_node->m_right = index; if (parent == this->GetRMost()) { this->SetRMost(index); } } /* Ensure the tree is balanced. */ int cur_index = index; while (true) { auto *cur_node = this->GetNode(cur_index); if (this->GetNode(cur_node->m_parent)->m_color != Color::Red) { break; } auto *p_node = this->GetNode(cur_node->m_parent); auto *g_node = this->GetNode(p_node->m_parent); if (cur_node->m_parent == g_node->m_left) { if (auto *gr_node = this->GetNode(g_node->m_right); gr_node->m_color == Color::Red) { p_node->m_color = Color::Black; gr_node->m_color = Color::Black; g_node->m_color = Color::Red; AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red); cur_index = p_node->m_parent; continue; } if (cur_index == p_node->m_right) { cur_index = cur_node->m_parent; cur_node = this->GetNode(cur_index); this->RotateLeft(cur_index); } p_node = this->GetNode(cur_node->m_parent); p_node->m_color = Color::Black; g_node = this->GetNode(p_node->m_parent); g_node->m_color = Color::Red; AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red); this->RotateRight(p_node->m_parent); } else { if (auto *gl_node = this->GetNode(g_node->m_left); gl_node->m_color == Color::Red) { p_node->m_color = Color::Black; gl_node->m_color = Color::Black; g_node->m_color = Color::Red; AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red); cur_index = p_node->m_parent; continue; } if (cur_index == p_node->m_left) { cur_index = cur_node->m_parent; cur_node = this->GetNode(cur_index); this->RotateRight(cur_index); } p_node = this->GetNode(cur_node->m_parent); p_node->m_color = Color::Black; g_node = this->GetNode(p_node->m_parent); g_node->m_color = Color::Red; AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red); this->RotateLeft(p_node->m_parent); } } /* Set root color. */ this->GetNode(this->GetRoot())->m_color = Color::Black; return index; } int InsertNoHint(bool before, const Member &elem) { int cur_index = this->GetRoot(); int prev_index = Index_Nil; bool less = true; while (cur_index != Index_Nil && cur_index != Index_Leaf) { auto *node = this->GetNode(cur_index); prev_index = cur_index; if (before) { less = Compare{}(node->m_data, elem); } else { less = Compare{}(elem, node->m_data); } if (less) { cur_index = node->m_left; } else { cur_index = node->m_right; } } if (cur_index == Index_Nil) { /* Create a new node. */ const auto index = this->AcquireIndex(); auto *node = this->GetNode(index); node->m_color = Color::Black; node->m_parent = Index_Nil; node->m_right = Index_Leaf; node->m_left = Index_Leaf; std::memcpy(reinterpret_cast(std::addressof(node->m_data)), reinterpret_cast(std::addressof(elem)), sizeof(node->m_data)); this->SetRoot(index); this->SetLMost(index); this->SetRMost(index); return index; } else { auto *compare_node = this->GetNode(prev_index); if (less) { if (prev_index == this->GetLMost()) { return this->InsertAt(less, prev_index, elem); } else { compare_node = this->GetNode(this->UncheckedMM(prev_index)); } } if (Compare{}(compare_node->m_data, elem)) { return this->InsertAt(less, prev_index, elem); } else { return Index_Nil; } } } void RotateLeft(int index) { /* Determine indices. */ const auto p_index = this->GetParent(index); const auto r_index = this->GetNode(index)->m_right; const auto l_index = this->GetNode(index)->m_left; const auto rl_index = this->GetNode(r_index)->m_left; const auto rr_index = this->GetNode(r_index)->m_right; /* Get nodes. */ auto *node = this->GetNode(index); auto *p_node = this->GetNode(p_index); auto *r_node = this->GetNode(r_index); auto *l_node = this->GetNode(l_index); auto *rl_node = this->GetNode(rl_index); auto *rr_node = this->GetNode(rr_index); /* Perform the rotation. */ if (p_index == Index_Nil) { r_node->m_parent = Index_Nil; m_header->root_index = r_index; } else if (p_node->m_left == index) { p_node->SetLeft(r_index, r_node, p_index); } else { p_node->SetRight(r_index, r_node, p_index); } r_node->SetLeft(index, node, r_index); r_node->SetRight(rr_index, rr_node, r_index); node->SetLeft(l_index, l_node, index); node->SetRight(rl_index, rl_node, index); } void RotateRight(int index) { /* Determine indices. */ const auto p_index = this->GetParent(index); const auto l_index = this->GetNode(index)->m_left; const auto ll_index = this->GetNode(l_index)->m_left; const auto lr_index = this->GetNode(l_index)->m_right; const auto r_index = this->GetNode(index)->m_right; /* Get nodes. */ auto *node = this->GetNode(index); auto *p_node = this->GetNode(p_index); auto *l_node = this->GetNode(l_index); auto *ll_node = this->GetNode(ll_index); auto *lr_node = this->GetNode(lr_index); auto *r_node = this->GetNode(r_index); /* Perform the rotation. */ if (p_index == Index_Nil) { l_node->m_parent = Index_Nil; m_header->root_index = l_index; } else if (p_node->m_left == index) { p_node->SetLeft(l_index, l_node, p_index); } else { p_node->SetRight(l_index, l_node, p_index); } l_node->SetLeft(ll_index, ll_node, l_index); l_node->SetRight(index, node, l_index); node->SetLeft(lr_index, lr_node, index); node->SetRight(r_index, r_node, index); } int UncheckedMM(int index) const { auto *node = this->GetNode(index); if (this->IsNil(index)) { index = this->GetRMost(); node = this->GetNode(index); } else if (this->IsNil(node->m_left)) { int parent = node->m_parent; Node *p; for (p = this->GetNode(parent); !this->IsNil(parent) && index == p->m_left; p = this->GetNode(parent)) { index = parent; node = p; parent = p->m_parent; } if (!this->IsNil(index)) { index = parent; node = p; } } else { index = this->FindMaxInSubtree(node->m_left); node = this->GetNode(index); } if (this->IsNil(index)) { return Index_Leaf; } else { return index; } } int UncheckedPP(int index) const { auto *node = this->GetNode(index); if (!this->IsNil(index)) { if (this->IsNil(node->m_right)) { int parent = node->m_parent; Node *p; for (p = this->GetNode(parent); !this->IsNil(parent) && index == p->m_right; p = this->GetNode(parent)) { index = parent; node = p; parent = p->m_parent; } index = parent; node = p; } else { index = this->FindMinInSubtree(node->m_right); node = this->GetNode(index); } } if (this->IsNil(index)) { return Index_Leaf; } else { return index; } } public: void Initialize(size_t num_elements, void *buffer, size_t buffer_size) { AMS_ASSERT(num_elements <= max_size); return this->InitializeImpl(static_cast(num_elements), buffer, buffer_size); } iterator begin() { return iterator(*this); } const_iterator begin() const { return const_iterator(*this); } iterator end() { return m_end_iterator; } const_iterator end() const { return m_end_iterator; } size_t size() const { return m_header->cur_elements; } void clear() { const auto num_elements = m_header->max_elements; const auto buffer_size = m_header->buffer_size; AMS_ASSERT(buffer_size == static_cast(GetRequiredMemorySize(num_elements))); return this->InitializeMemory(num_elements, buffer_size, impl::AvailableIndexFinder::GetSignature()); } bool erase(const Member &elem) { const auto range = this->equal_range(elem); if (range.first != range.last) { this->EraseByIndex(range.first); return true; } else { return false; } } iterator find(const Member &elem) { if (const auto index = this->FindIndex(elem); index >= 0) { return iterator(*this, index); } else { return this->end(); } } const_iterator find(const Member &elem) const { if (const auto index = this->FindIndex(elem); index >= 0) { return const_iterator(*this, index); } else { return this->end(); } } std::pair insert(const Member &elem) { const auto index = this->InsertNoHint(false, elem); const auto it = iterator(*this, index); return std::make_pair(it, !this->IsNil(index)); } IndexPair equal_range(const Member &elem) { /* Get node to start iteration. */ auto cur_index = this->GetRoot(); auto cur_node = this->GetNode(cur_index); auto min_index = Index_Leaf; auto min_node = this->GetNode(min_index); auto max_index = Index_Leaf; auto max_node = this->GetNode(max_index); /* Iterate until current is leaf, to find min/max. */ while (cur_index != Index_Leaf) { if (Compare{}(cur_node->m_data, elem)) { cur_index = cur_node->m_right; cur_node = this->GetNode(cur_index); } else { if (max_index == Index_Leaf && Compare{}(elem, cur_node->m_data)) { max_index = cur_index; max_node = this->GetNode(max_index); } min_index = cur_index; min_node = this->GetNode(min_index); cur_index = cur_node->m_left; cur_node = this->GetNode(cur_index); } } /* Iterate again, to find correct range extent for max. */ cur_index = (max_index == Index_Leaf) ? this->GetRoot() : max_node->m_left; cur_node = this->GetNode(cur_index); while (cur_index != Index_Leaf) { if (Compare{}(elem, cur_node->m_data)) { max_index = cur_index; max_node = cur_node; cur_index = cur_node->m_left; } else { cur_index = cur_node->m_right; } cur_node = this->GetNode(cur_index); } AMS_UNUSED(min_node); return IndexPair{min_index, max_index}; } }; }