mirror of
https://github.com/zeldaret/botw
synced 2026-08-21 14:16:48 -04:00
Switch to subrepos
git subrepo clone https://github.com/open-ead/sead lib/sead subrepo: subdir: "lib/sead" merged: "1b66e825d" upstream: origin: "https://github.com/open-ead/sead" branch: "master" commit: "1b66e825d" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo" commit: "2f68596" git subrepo clone (merge) https://github.com/open-ead/nnheaders lib/NintendoSDK subrepo: subdir: "lib/NintendoSDK" merged: "9ee21399f" upstream: origin: "https://github.com/open-ead/nnheaders" branch: "master" commit: "9ee21399f" git-subrepo: version: "0.4.3" origin: "ssh://git@github.com/ingydotnet/git-subrepo" commit: "2f68596" git subrepo clone https://github.com/open-ead/agl lib/agl subrepo: subdir: "lib/agl" merged: "7c063271b" upstream: origin: "https://github.com/open-ead/agl" branch: "master" commit: "7c063271b" git-subrepo: version: "0.4.3" origin: "ssh://git@github.com/ingydotnet/git-subrepo" commit: "2f68596" git subrepo clone https://github.com/open-ead/EventFlow lib/EventFlow subrepo: subdir: "lib/EventFlow" merged: "c35d21b34" upstream: origin: "https://github.com/open-ead/EventFlow" branch: "master" commit: "c35d21b34" git-subrepo: version: "0.4.3" origin: "ssh://git@github.com/ingydotnet/git-subrepo" commit: "2f68596"
This commit is contained in:
@@ -0,0 +1,584 @@
|
||||
#pragma once
|
||||
|
||||
#include "basis/seadRawPrint.h"
|
||||
#include "container/seadFreeList.h"
|
||||
#include "prim/seadBitUtil.h"
|
||||
#include "prim/seadDelegate.h"
|
||||
#include "prim/seadSafeString.h"
|
||||
|
||||
namespace sead
|
||||
{
|
||||
template <typename Key>
|
||||
class TreeMapNode;
|
||||
|
||||
/// Sorted associative container, implemented using a left-leaning red-black tree.
|
||||
/// For an explanation of the algorithm, see https://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf
|
||||
template <typename Key>
|
||||
class TreeMapImpl
|
||||
{
|
||||
public:
|
||||
using Node = TreeMapNode<Key>;
|
||||
|
||||
void insert(Node* node);
|
||||
void erase(const Key& key);
|
||||
void clear();
|
||||
|
||||
Node* find(const Key& key) const { return find(mRoot, key); }
|
||||
|
||||
template <typename Callable>
|
||||
void forEach(const Callable& callable) const
|
||||
{
|
||||
if (mRoot)
|
||||
forEach(mRoot, callable);
|
||||
}
|
||||
|
||||
Node* startIterating() const
|
||||
{
|
||||
if (!mRoot)
|
||||
return nullptr;
|
||||
return startIterating(mRoot);
|
||||
}
|
||||
|
||||
Node* nextNode(Node* node) const
|
||||
{
|
||||
if (!node)
|
||||
return nullptr;
|
||||
|
||||
// If there is a right child node, explore that branch first.
|
||||
if (node->mRight)
|
||||
{
|
||||
node->mRight->setParent(node);
|
||||
return startIterating(node->mRight);
|
||||
}
|
||||
|
||||
// Otherwise, walk back up to the node P from which we reached this node
|
||||
// by following P's left child pointer.
|
||||
while (auto* const parent = node->getParent())
|
||||
{
|
||||
if (parent->mLeft == node)
|
||||
return parent;
|
||||
node = parent;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Returns the left most child of a given node, marking each node with its parent
|
||||
/// along the way.
|
||||
static Node* startIterating(Node* node)
|
||||
{
|
||||
while (node->mLeft)
|
||||
{
|
||||
node->mLeft->setParent(node);
|
||||
node = node->mLeft;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
Node* insert(Node* root, Node* node);
|
||||
Node* erase(Node* root, const Key& key);
|
||||
Node* find(Node* root, const Key& key) const;
|
||||
|
||||
static inline Node* rotateLeft(Node* node);
|
||||
static inline Node* rotateRight(Node* node);
|
||||
static inline Node* moveRedLeft(Node* node);
|
||||
static inline Node* moveRedRight(Node* node);
|
||||
static Node* findMin(Node* node);
|
||||
static Node* eraseMin(Node* node);
|
||||
static inline Node* fixUp(Node* node);
|
||||
static bool isRed(const Node* node) { return node && node->isRed(); }
|
||||
static inline void flipColors(Node* node);
|
||||
|
||||
template <typename Callable>
|
||||
static void forEach(Node* start, const Callable& callable);
|
||||
|
||||
Node* mRoot = nullptr;
|
||||
};
|
||||
|
||||
/// Requires Key to have a compare() member function, which returns -1 if lhs < rhs, 0 if lhs = rhs
|
||||
/// and 1 if lhs > rhs.
|
||||
template <typename Key>
|
||||
class TreeMapNode
|
||||
{
|
||||
public:
|
||||
TreeMapNode()
|
||||
{
|
||||
mLeft = mRight = nullptr;
|
||||
mColorAndPtr = 0;
|
||||
}
|
||||
|
||||
virtual ~TreeMapNode() = default;
|
||||
virtual void erase_() = 0;
|
||||
|
||||
const Key& key() const { return mKey; }
|
||||
|
||||
protected:
|
||||
friend class TreeMapImpl<Key>;
|
||||
|
||||
enum class Color
|
||||
{
|
||||
Red = 0,
|
||||
Black = 1,
|
||||
};
|
||||
|
||||
void flipColor() { BitUtil::bitCastWrite(mColorAndPtr ^ 1u, &mColorAndPtr); }
|
||||
void setColor(Color color) { mColorAndPtr = uintptr_t(color); }
|
||||
|
||||
void setParent(TreeMapNode* parent) { mColorAndPtr = (mColorAndPtr & 1) | uintptr_t(parent); }
|
||||
/// @warning Only valid if setParent has been called!
|
||||
TreeMapNode* getParent() const { return reinterpret_cast<TreeMapNode*>(mColorAndPtr & ~1); }
|
||||
|
||||
bool isRed() const { return (mColorAndPtr & 1u) == bool(Color::Red); }
|
||||
|
||||
TreeMapNode* mLeft;
|
||||
TreeMapNode* mRight;
|
||||
uintptr_t mColorAndPtr;
|
||||
Key mKey;
|
||||
};
|
||||
|
||||
/// Requires Key to have operator< defined
|
||||
/// This can be specialized, but all specializations must define `compare` and `key` as follows.
|
||||
template <typename Key>
|
||||
struct TreeMapKeyImpl
|
||||
{
|
||||
TreeMapKeyImpl() = default;
|
||||
TreeMapKeyImpl(const Key& key_) : key(key_) {}
|
||||
TreeMapKeyImpl& operator=(const Key& key_)
|
||||
{
|
||||
key = key_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns -1 if mKey < rhs, 0 if mKey = rhs and 1 if mKey > rhs.
|
||||
s32 compare(const TreeMapKeyImpl& rhs) const
|
||||
{
|
||||
if (key < rhs.key)
|
||||
return -1;
|
||||
if (rhs.key < key)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Key key;
|
||||
};
|
||||
|
||||
/// Sorted associative container.
|
||||
/// This is essentially std::map<Key, Value>
|
||||
template <typename Key, typename Value>
|
||||
class TreeMap : public TreeMapImpl<TreeMapKeyImpl<Key>>
|
||||
{
|
||||
public:
|
||||
using MapImpl = TreeMapImpl<TreeMapKeyImpl<Key>>;
|
||||
class Node : public MapImpl::Node
|
||||
{
|
||||
public:
|
||||
Node(TreeMap* map, const Key& key, const Value& value) : mValue(value), mMap(map)
|
||||
{
|
||||
this->mKey = key;
|
||||
}
|
||||
|
||||
void erase_() override;
|
||||
|
||||
Value& value() { return mValue; }
|
||||
const Value& value() const { return mValue; }
|
||||
|
||||
private:
|
||||
friend class TreeMap;
|
||||
|
||||
Value mValue;
|
||||
TreeMap* mMap;
|
||||
};
|
||||
|
||||
~TreeMap();
|
||||
|
||||
void allocBuffer(s32 node_max, Heap* heap, s32 alignment = sizeof(void*));
|
||||
void setBuffer(s32 node_max, void* buffer);
|
||||
void freeBuffer();
|
||||
|
||||
Value* insert(const Key& key, const Value& value);
|
||||
void clear();
|
||||
|
||||
Node* find(const Key& key) const;
|
||||
|
||||
// Callable must have the signature Key&, Value&
|
||||
template <typename Callable>
|
||||
void forEach(const Callable& delegate) const;
|
||||
|
||||
Node* startIterating() const { return static_cast<Node*>(MapImpl::startIterating()); }
|
||||
Node* nextNode(Node* node) const { return static_cast<Node*>(MapImpl::nextNode(node)); }
|
||||
|
||||
private:
|
||||
void eraseNodeForClear_(typename MapImpl::Node* node);
|
||||
|
||||
FreeList mFreeList;
|
||||
s32 mSize = 0;
|
||||
s32 mCapacity = 0;
|
||||
};
|
||||
|
||||
template <typename Key, typename Node>
|
||||
class IntrusiveTreeMap : public TreeMapImpl<Key>
|
||||
{
|
||||
public:
|
||||
using MapImpl = TreeMapImpl<Key>;
|
||||
|
||||
Node* find(const Key& key) const { return static_cast<Node*>(MapImpl::find(key)); }
|
||||
|
||||
// Callable must have the signature Node*
|
||||
template <typename Callable>
|
||||
void forEach(const Callable& delegate) const
|
||||
{
|
||||
MapImpl::forEach([delegate](auto* base_node) {
|
||||
auto* node = static_cast<Node*>(base_node);
|
||||
delegate(node);
|
||||
});
|
||||
}
|
||||
|
||||
Node* startIterating() const { return static_cast<Node*>(MapImpl::startIterating()); }
|
||||
Node* nextNode(Node* node) const { return static_cast<Node*>(MapImpl::nextNode(node)); }
|
||||
};
|
||||
|
||||
template <typename Key>
|
||||
inline void TreeMapImpl<Key>::insert(Node* node)
|
||||
{
|
||||
mRoot = insert(mRoot, node);
|
||||
mRoot->setColor(Node::Color::Black);
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
inline TreeMapNode<Key>* TreeMapImpl<Key>::insert(Node* root, Node* node)
|
||||
{
|
||||
if (!root)
|
||||
{
|
||||
node->mLeft = node->mRight = nullptr;
|
||||
node->setColor(Node::Color::Red);
|
||||
return node;
|
||||
}
|
||||
|
||||
const s32 cmp = node->key().compare(root->key());
|
||||
|
||||
if (cmp < 0)
|
||||
{
|
||||
root->mLeft = insert(root->mLeft, node);
|
||||
}
|
||||
else if (cmp > 0)
|
||||
{
|
||||
root->mRight = insert(root->mRight, node);
|
||||
}
|
||||
else if (root != node)
|
||||
{
|
||||
node->mRight = root->mRight;
|
||||
node->mLeft = root->mLeft;
|
||||
node->mColorAndPtr = root->mColorAndPtr;
|
||||
root->erase_();
|
||||
root = node;
|
||||
}
|
||||
|
||||
if (isRed(root->mRight) && !isRed(root->mLeft))
|
||||
root = rotateLeft(root);
|
||||
|
||||
if (isRed(root->mLeft) && isRed(root->mLeft->mLeft))
|
||||
root = rotateRight(root);
|
||||
|
||||
if (isRed(root->mLeft) && isRed(root->mRight))
|
||||
flipColors(root);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
inline void TreeMapImpl<Key>::erase(const Key& key)
|
||||
{
|
||||
mRoot = erase(mRoot, key);
|
||||
if (mRoot)
|
||||
mRoot->setColor(Node::Color::Black);
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
inline TreeMapNode<Key>* TreeMapImpl<Key>::erase(Node* root, const Key& key)
|
||||
{
|
||||
if (key.compare(root->key()) < 0)
|
||||
{
|
||||
if (!isRed(root->mLeft) && !isRed(root->mLeft->mLeft))
|
||||
root = moveRedLeft(root);
|
||||
root->mLeft = erase(root->mLeft, key);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isRed(root->mLeft))
|
||||
root = rotateRight(root);
|
||||
|
||||
if (key.compare(root->key()) == 0 && !root->mRight)
|
||||
{
|
||||
root->erase_();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!isRed(root->mRight) && !isRed(root->mRight->mLeft))
|
||||
root = moveRedRight(root);
|
||||
|
||||
if (key.compare(root->key()) == 0)
|
||||
{
|
||||
Node* const min_node = findMin(root->mRight);
|
||||
|
||||
Node* target = root->mRight;
|
||||
if (root->mRight)
|
||||
target = find(root->mRight, min_node->key());
|
||||
|
||||
target->mRight = eraseMin(root->mRight);
|
||||
target->mLeft = root->mLeft;
|
||||
target->mColorAndPtr = root->mColorAndPtr;
|
||||
root->erase_();
|
||||
root = target;
|
||||
}
|
||||
else
|
||||
{
|
||||
root->mRight = erase(root->mRight, key);
|
||||
}
|
||||
}
|
||||
return fixUp(root);
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
inline void TreeMapImpl<Key>::clear()
|
||||
{
|
||||
mRoot = nullptr;
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
inline TreeMapNode<Key>* TreeMapImpl<Key>::find(Node* root, const Key& key) const
|
||||
{
|
||||
Node* node = root;
|
||||
while (node)
|
||||
{
|
||||
const s32 cmp = key.compare(node->key());
|
||||
if (cmp < 0)
|
||||
node = node->mLeft;
|
||||
else if (cmp > 0)
|
||||
node = node->mRight;
|
||||
else
|
||||
return node;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
template <typename Callable>
|
||||
inline void TreeMapImpl<Key>::forEach(Node* start, const Callable& callable)
|
||||
{
|
||||
Node* i = start;
|
||||
do
|
||||
{
|
||||
Node* node = i;
|
||||
if (i->mLeft)
|
||||
forEach(i->mLeft, callable);
|
||||
i = i->mRight;
|
||||
callable(node);
|
||||
} while (i);
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
inline TreeMapNode<Key>* TreeMapImpl<Key>::rotateLeft(Node* node)
|
||||
{
|
||||
TreeMapNode<Key>* j = node->mRight;
|
||||
node->mRight = j->mLeft;
|
||||
j->mLeft = node;
|
||||
j->mColorAndPtr = node->mColorAndPtr;
|
||||
node->setColor(Node::Color::Red);
|
||||
return j;
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
inline TreeMapNode<Key>* TreeMapImpl<Key>::rotateRight(Node* node)
|
||||
{
|
||||
TreeMapNode<Key>* j = node->mLeft;
|
||||
node->mLeft = j->mRight;
|
||||
j->mRight = node;
|
||||
j->mColorAndPtr = node->mColorAndPtr;
|
||||
node->setColor(Node::Color::Red);
|
||||
return j;
|
||||
}
|
||||
|
||||
// NON_MATCHING: this version matches the LLRB tree implementation and is better optimized;
|
||||
// there is a useless store to node->mRight in the original version
|
||||
template <typename Key>
|
||||
inline TreeMapNode<Key>* TreeMapImpl<Key>::moveRedLeft(Node* node)
|
||||
{
|
||||
flipColors(node);
|
||||
if (isRed(node->mRight->mLeft))
|
||||
{
|
||||
node->mRight = rotateRight(node->mRight);
|
||||
node = rotateLeft(node);
|
||||
flipColors(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
inline TreeMapNode<Key>* TreeMapImpl<Key>::moveRedRight(Node* node)
|
||||
{
|
||||
flipColors(node);
|
||||
if (isRed(node->mLeft->mLeft))
|
||||
{
|
||||
node = rotateRight(node);
|
||||
flipColors(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
inline TreeMapNode<Key>* TreeMapImpl<Key>::findMin(Node* node)
|
||||
{
|
||||
while (node->mLeft)
|
||||
node = node->mLeft;
|
||||
return node;
|
||||
}
|
||||
|
||||
// NON_MATCHING: this version matches the LLRB tree implementation and is better optimized
|
||||
template <typename Key>
|
||||
inline TreeMapNode<Key>* TreeMapImpl<Key>::eraseMin(Node* node)
|
||||
{
|
||||
if (!node->mLeft)
|
||||
return nullptr;
|
||||
|
||||
if (!isRed(node->mLeft) && !isRed(node->mLeft->mLeft))
|
||||
node = moveRedLeft(node);
|
||||
|
||||
node->mLeft = eraseMin(node->mLeft);
|
||||
#ifdef MATCHING_HACK_NX_CLANG
|
||||
asm("");
|
||||
#endif
|
||||
return fixUp(node);
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
inline TreeMapNode<Key>* TreeMapImpl<Key>::fixUp(Node* node)
|
||||
{
|
||||
if (isRed(node->mRight))
|
||||
node = rotateLeft(node);
|
||||
|
||||
if (isRed(node->mLeft) && isRed(node->mLeft->mLeft))
|
||||
node = rotateRight(node);
|
||||
|
||||
if (isRed(node->mLeft) && isRed(node->mRight))
|
||||
flipColors(node);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
inline void TreeMapImpl<Key>::flipColors(Node* node)
|
||||
{
|
||||
node->flipColor();
|
||||
node->mLeft->flipColor();
|
||||
node->mRight->flipColor();
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
inline void TreeMap<Key, Value>::Node::erase_()
|
||||
{
|
||||
TreeMap* const map = mMap;
|
||||
void* const this_ = this;
|
||||
// Note: Nintendo does not call the destructor, which is dangerous...
|
||||
map->mFreeList.free(this_);
|
||||
--map->mSize;
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
inline TreeMap<Key, Value>::~TreeMap()
|
||||
{
|
||||
void* work = mFreeList.work();
|
||||
if (!work)
|
||||
return;
|
||||
|
||||
clear();
|
||||
freeBuffer();
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
inline void TreeMap<Key, Value>::allocBuffer(s32 node_max, Heap* heap, s32 alignment)
|
||||
{
|
||||
SEAD_ASSERT(mFreeList.work() == nullptr);
|
||||
if (node_max <= 0)
|
||||
{
|
||||
SEAD_ASSERT_MSG(false, "node_max[%d] must be larger than zero", node_max);
|
||||
AllocFailAssert(heap, node_max * sizeof(Node), alignment);
|
||||
}
|
||||
|
||||
void* work = AllocBuffer(node_max * sizeof(Node), heap, alignment);
|
||||
if (work)
|
||||
setBuffer(node_max, work);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
inline void TreeMap<Key, Value>::setBuffer(s32 node_max, void* buffer)
|
||||
{
|
||||
mCapacity = node_max;
|
||||
mFreeList.setWork(buffer, sizeof(Node), node_max);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
inline void TreeMap<Key, Value>::freeBuffer()
|
||||
{
|
||||
void* buffer = mFreeList.work();
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
::operator delete[](buffer);
|
||||
mCapacity = 0;
|
||||
mFreeList.reset();
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
inline Value* TreeMap<Key, Value>::insert(const Key& key, const Value& value)
|
||||
{
|
||||
if (mSize >= mCapacity)
|
||||
{
|
||||
if (Node* node = find(key))
|
||||
{
|
||||
node->value() = value;
|
||||
return &node->value();
|
||||
}
|
||||
SEAD_ASSERT_MSG(false, "map is full.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Node* node = new (mFreeList.alloc()) Node(this, key, value);
|
||||
++mSize;
|
||||
MapImpl::insert(node);
|
||||
return &node->value();
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
inline void TreeMap<Key, Value>::clear()
|
||||
{
|
||||
Delegate1<TreeMap<Key, Value>, typename MapImpl::Node*> delegate(this,
|
||||
&TreeMap::eraseNodeForClear_);
|
||||
MapImpl::forEach(delegate);
|
||||
mSize = 0;
|
||||
MapImpl::clear();
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
inline typename TreeMap<Key, Value>::Node* TreeMap<Key, Value>::find(const Key& key) const
|
||||
{
|
||||
return static_cast<Node*>(MapImpl::find(key));
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename Callable>
|
||||
inline void TreeMap<Key, Value>::forEach(const Callable& delegate) const
|
||||
{
|
||||
MapImpl::forEach([&delegate](auto* base_node) {
|
||||
auto* node = static_cast<Node*>(base_node);
|
||||
delegate(node->key(), node->value());
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
inline void TreeMap<Key, Value>::eraseNodeForClear_(typename MapImpl::Node* node)
|
||||
{
|
||||
// Note: Nintendo does not call the destructor, which is dangerous...
|
||||
mFreeList.free(node);
|
||||
}
|
||||
} // namespace sead
|
||||
Reference in New Issue
Block a user