mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-22 20:06:40 +00:00
util: add FixedMap/FixedTree
This commit is contained in:
parent
201b17f100
commit
c1d93a9495
4 changed files with 1041 additions and 3 deletions
|
@ -46,7 +46,8 @@
|
|||
#include <vapours/util/util_format_string.hpp>
|
||||
#include <vapours/util/util_range.hpp>
|
||||
|
||||
#include <vapours/util/impl/util_available_index_finder.hpp>
|
||||
#include <vapours/util/util_fixed_tree.hpp>
|
||||
#include <vapours/util/util_fixed_map.hpp>
|
||||
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
#include <vapours/util/util_mutex_utils.hpp>
|
||||
|
|
61
libraries/libvapours/include/vapours/util/util_fixed_map.hpp
Normal file
61
libraries/libvapours/include/vapours/util/util_fixed_map.hpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_fixed_tree.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename Key, typename Value, typename Compare = std::less<Key>, size_t BufferAlignment = 8>
|
||||
class FixedMap {
|
||||
private:
|
||||
using KeyValuePair = std::pair<Key const, Value>;
|
||||
|
||||
struct LessTypeForMap {
|
||||
constexpr ALWAYS_INLINE bool operator()(const KeyValuePair &lhs, const KeyValuePair &rhs) const {
|
||||
return Compare{}(lhs.first, rhs.first);
|
||||
}
|
||||
};
|
||||
|
||||
using TreeType = ::ams::util::FixedTree<KeyValuePair, LessTypeForMap, KeyValuePair, BufferAlignment>;
|
||||
|
||||
using iterator = typename TreeType::Iterator;
|
||||
using const_iterator = typename TreeType::ConstIterator;
|
||||
private:
|
||||
TreeType m_tree;
|
||||
public:
|
||||
FixedMap() : m_tree() { /* ... */ }
|
||||
|
||||
void Initialize(size_t num_elements, void *buffer, size_t buffer_size) {
|
||||
return m_tree.Initialize(num_elements, buffer, buffer_size);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE iterator begin() { return m_tree.begin(); }
|
||||
ALWAYS_INLINE const_iterator begin() const { return m_tree.begin(); }
|
||||
|
||||
ALWAYS_INLINE iterator end() { return m_tree.end(); }
|
||||
ALWAYS_INLINE const_iterator end() const { return m_tree.end(); }
|
||||
|
||||
ALWAYS_INLINE bool erase(const Key &key) { const KeyValuePair pair(key, Value{}); return m_tree.erase(pair); }
|
||||
|
||||
ALWAYS_INLINE iterator find(const Key &key) { const KeyValuePair pair(key, Value{}); return m_tree.find(pair); }
|
||||
ALWAYS_INLINE const_iterator find(const Key &key) const { const KeyValuePair pair(key, Value{}); return m_tree.find(pair); }
|
||||
|
||||
ALWAYS_INLINE std::pair<iterator, bool> insert(const KeyValuePair &pair) { return m_tree.insert(pair); }
|
||||
};
|
||||
|
||||
}
|
977
libraries/libvapours/include/vapours/util/util_fixed_tree.hpp
Normal file
977
libraries/libvapours/include/vapours/util/util_fixed_tree.hpp
Normal file
|
@ -0,0 +1,977 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/impl/util_available_index_finder.hpp>
|
||||
#include <vapours/util/util_alignment.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename Member, typename Compare, typename IteratorMember, size_t BufferAlignment = 8> requires std::convertible_to<Member &, IteratorMember &>
|
||||
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 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->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 IteratorBase {
|
||||
protected:
|
||||
const FixedTree *m_this;
|
||||
int m_index;
|
||||
protected:
|
||||
constexpr 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<int>::max()).m_data;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr IteratorBase &IncrementImpl() {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_this);
|
||||
|
||||
this->OperateIndex(true);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr 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<int>(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 Iterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ }
|
||||
constexpr Iterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ }
|
||||
|
||||
constexpr Iterator(const Iterator &rhs) = default;
|
||||
|
||||
constexpr bool operator==(const Iterator &rhs) const {
|
||||
return this->IsEqualImpl(rhs);
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const Iterator &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
constexpr IteratorMember &operator*() const {
|
||||
return static_cast<IteratorMember &>(this->DereferenceImpl());
|
||||
}
|
||||
|
||||
constexpr Iterator &operator++() {
|
||||
return static_cast<Iterator &>(this->IncrementImpl());
|
||||
}
|
||||
|
||||
constexpr Iterator &operator--() {
|
||||
return static_cast<Iterator &>(this->DecrementImpl());
|
||||
}
|
||||
};
|
||||
|
||||
class ConstIterator : public IteratorBase {
|
||||
public:
|
||||
constexpr ConstIterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ }
|
||||
constexpr ConstIterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ }
|
||||
|
||||
constexpr ConstIterator(const ConstIterator &rhs) = default;
|
||||
constexpr ConstIterator(const Iterator &rhs) : IteratorBase(rhs.m_this, rhs.m_index) { /* ... */ }
|
||||
|
||||
constexpr bool operator==(const ConstIterator &rhs) const {
|
||||
return this->IsEqualImpl(rhs);
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const ConstIterator &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
constexpr const IteratorMember &operator*() const {
|
||||
return static_cast<const IteratorMember &>(this->DereferenceImpl());
|
||||
}
|
||||
|
||||
constexpr ConstIterator &operator++() {
|
||||
return static_cast<ConstIterator &>(this->IncrementImpl());
|
||||
}
|
||||
|
||||
constexpr ConstIterator &operator--() {
|
||||
return static_cast<ConstIterator &>(this->DecrementImpl());
|
||||
}
|
||||
};
|
||||
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<size_t>(num_elements) <= max_size);
|
||||
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(buffer), BufferAlignment));
|
||||
AMS_ASSERT(buffer_size == GetRequiredMemorySize(num_elements));
|
||||
|
||||
/* Set buffer. */
|
||||
m_buffer = static_cast<u8 *>(buffer);
|
||||
m_header = reinterpret_cast<Header *>(m_buffer);
|
||||
|
||||
/* Setup memory. */
|
||||
this->InitializeMemory(num_elements, buffer_size, impl::AvailableIndexFinder::GetSignature());
|
||||
|
||||
/* Check that buffer was set up correctly. */
|
||||
AMS_ASSERT(static_cast<u32>(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->max_elements), std::addressof(m_header->cur_elements), m_buffer + sizeof(*m_header) + SizeOfNodes(num_elements));
|
||||
|
||||
/* Set nodes array. */
|
||||
m_nodes = reinterpret_cast<Node *>(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) {
|
||||
/* Seutp 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) {
|
||||
return this->FindIndexSub(this->GetRoot(), elem);
|
||||
}
|
||||
|
||||
int FindIndexSub(int index, const Member &elem) {
|
||||
if (index != Index_Nil) {
|
||||
auto *node = this->GetNode(elem);
|
||||
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(std::addressof(node->m_data), std::addressof(elem), sizeof(elem));
|
||||
|
||||
/* 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->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->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->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->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);
|
||||
|
||||
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(std::addressof(node->m_data), std::addressof(elem), sizeof(elem));
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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<int>(num_elements), buffer, buffer_size);
|
||||
}
|
||||
|
||||
Iterator begin() { return Iterator(*this); }
|
||||
ConstIterator begin() const { return ConstIterator(*this); }
|
||||
|
||||
Iterator end() { return m_end_iterator; }
|
||||
ConstIterator 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<u32>(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();
|
||||
}
|
||||
}
|
||||
|
||||
ConstIterator find(const Member &elem) const {
|
||||
if (const auto index = this->FindIndex(elem); index >= 0) {
|
||||
return Iterator(*this, index);
|
||||
} else {
|
||||
return this->end();
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<Iterator, bool> 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);
|
||||
}
|
||||
|
||||
return IndexPair{min_index, max_index};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -13,8 +13,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
|
||||
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
|
Loading…
Reference in a new issue