mirror of https://github.com/inolen/redream.git
convert interval tree from using a randomized bst to a red-black tree
This commit is contained in:
parent
0d455e710a
commit
07a1156eb5
|
@ -3,102 +3,87 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "core/assert.h"
|
||||
#include "core/intrusive_tree.h"
|
||||
|
||||
namespace dreavm {
|
||||
|
||||
// Interval tree implemented using a randomized bst. Based on implementation at
|
||||
// http://algs4.cs.princeton.edu/93intersection/IntervalST.java
|
||||
//
|
||||
// Parent pointers have been added in order to make removal by node (as opposed
|
||||
// to key) possible.
|
||||
typedef int64_t interval_type;
|
||||
|
||||
template <typename IT, typename VT>
|
||||
class IntervalTree {
|
||||
template <typename T>
|
||||
struct IntervalNode : public IntrusiveTreeNode<IntervalNode<T>> {
|
||||
IntervalNode(const interval_type &low, const interval_type &high,
|
||||
const T &value)
|
||||
: low(low), high(high), max(high), value(value), num(1), height(1) {}
|
||||
|
||||
bool operator<(const IntervalNode<T> &rhs) const {
|
||||
return low < rhs.low || (low == rhs.low && high < rhs.high);
|
||||
}
|
||||
|
||||
interval_type low, high, max;
|
||||
T value;
|
||||
int num, height;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class IntervalTree : public IntrusiveTree<IntervalNode<T>> {
|
||||
public:
|
||||
struct Node;
|
||||
|
||||
typedef IT interval_type;
|
||||
typedef VT value_type;
|
||||
typedef IntervalTree<interval_type, value_type> self_type;
|
||||
typedef std::function<void(const self_type &, Node *)> iterate_cb;
|
||||
|
||||
struct Node {
|
||||
Node(const interval_type &low, const interval_type &high,
|
||||
const value_type &value)
|
||||
: parent(nullptr),
|
||||
left(nullptr),
|
||||
right(nullptr),
|
||||
low(low),
|
||||
high(high),
|
||||
max(high),
|
||||
value(value),
|
||||
num(1) {}
|
||||
|
||||
bool operator<(const Node &rhs) const {
|
||||
return low < rhs.low || (low == rhs.low && high < rhs.high);
|
||||
}
|
||||
|
||||
Node *parent, *left, *right;
|
||||
interval_type low, high, max;
|
||||
value_type value;
|
||||
int num;
|
||||
};
|
||||
|
||||
IntervalTree() : root_(nullptr) {}
|
||||
typedef IntervalTree<T> self_type;
|
||||
typedef IntervalNode<T> node_type;
|
||||
typedef std::function<void(const self_type &, node_type *)> iterate_cb;
|
||||
|
||||
~IntervalTree() { Clear(); }
|
||||
|
||||
int Size() { return Size(root_); }
|
||||
int Size() { return Size(this->root_); }
|
||||
|
||||
int Height() { return Height(root_); }
|
||||
int Height() { return Height(this->root_); }
|
||||
|
||||
Node *Insert(const interval_type &low, const interval_type &high,
|
||||
const value_type &value) {
|
||||
Node *n = new Node(low, high, value);
|
||||
node_type *Insert(const interval_type &low, const interval_type &high,
|
||||
const T &value) {
|
||||
node_type *n = new node_type(low, high, value);
|
||||
|
||||
SetRoot(RandomizedInsert(root_, n));
|
||||
// add new node into the correct location in the tree, then link it in
|
||||
// to recolor the tree
|
||||
node_type *parent = this->root_;
|
||||
while (parent) {
|
||||
if (*n < *parent) {
|
||||
if (!parent->left) {
|
||||
parent->left = n;
|
||||
break;
|
||||
}
|
||||
|
||||
parent = parent->left;
|
||||
} else {
|
||||
if (!parent->right) {
|
||||
parent->right = n;
|
||||
break;
|
||||
}
|
||||
|
||||
parent = parent->right;
|
||||
}
|
||||
}
|
||||
n->parent = parent;
|
||||
|
||||
this->Link(n);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void Remove(Node *n) {
|
||||
// join left and right subtrees, assign new joined subtree to parent
|
||||
Node *joined = Join(n->left, n->right);
|
||||
void Remove(node_type *n) {
|
||||
this->Unlink(n);
|
||||
|
||||
if (!n->parent) {
|
||||
// removed node had no parent, must have been root
|
||||
CHECK_EQ(root_, n);
|
||||
SetRoot(joined);
|
||||
} else if (n->parent->left == n) {
|
||||
SetLeft(n->parent, joined);
|
||||
} else {
|
||||
SetRight(n->parent, joined);
|
||||
}
|
||||
|
||||
// fix up each node in the parent chain
|
||||
Node *parent = n->parent;
|
||||
while (parent) {
|
||||
FixCounts(parent);
|
||||
parent = parent->parent;
|
||||
}
|
||||
|
||||
// remove the node
|
||||
delete n;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
Clear(root_);
|
||||
Clear(this->root_);
|
||||
|
||||
SetRoot(nullptr);
|
||||
this->root_ = nullptr;
|
||||
}
|
||||
|
||||
Node *Find(interval_type low, interval_type high) {
|
||||
Node *n = root_;
|
||||
node_type *Find(interval_type low, interval_type high) {
|
||||
node_type *n = this->root_;
|
||||
|
||||
while (n) {
|
||||
if (high >= n->low && n->high >= low) {
|
||||
|
@ -114,58 +99,39 @@ class IntervalTree {
|
|||
}
|
||||
|
||||
void Iterate(interval_type low, interval_type high, iterate_cb cb) {
|
||||
Iterate(root_, low, high, cb);
|
||||
Iterate(this->root_, low, high, cb);
|
||||
}
|
||||
|
||||
protected:
|
||||
void AugmentPropagate(node_type *n) {
|
||||
while (n) {
|
||||
FixCounts(n);
|
||||
n = n->parent;
|
||||
}
|
||||
}
|
||||
|
||||
void AugmentRotate(node_type *oldn, node_type *newn) {
|
||||
FixCounts(oldn);
|
||||
FixCounts(newn);
|
||||
FixCounts(newn->parent);
|
||||
}
|
||||
|
||||
private:
|
||||
int Size(Node *n) { return n ? n->num : 0; }
|
||||
int Size(node_type *n) { return n ? n->num : 0; }
|
||||
int Height(node_type *n) { return n ? n->height : 0; }
|
||||
interval_type Max(node_type *n) { return n ? n->max : 0; }
|
||||
|
||||
int Height(Node *n) {
|
||||
return n ? 1 + std::max(Height(n->left), Height(n->right)) : 0;
|
||||
void FixCounts(node_type *n) {
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
|
||||
n->num = 1 + Size(n->left) + Size(n->right);
|
||||
n->height = 1 + std::max(Height(n->left), Height(n->right));
|
||||
n->max = std::max(std::max(n->high, Max(n->left)), Max(n->right));
|
||||
}
|
||||
|
||||
//
|
||||
// insertion
|
||||
//
|
||||
Node *RootInsert(Node *root, Node *n) {
|
||||
if (!root) {
|
||||
return n;
|
||||
}
|
||||
|
||||
if (*n < *root) {
|
||||
SetLeft(root, RootInsert(root->left, n));
|
||||
root = RotateRight(root);
|
||||
} else {
|
||||
SetRight(root, RootInsert(root->right, n));
|
||||
root = RotateLeft(root);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
Node *RandomizedInsert(Node *root, Node *n) {
|
||||
if (!root) {
|
||||
return n;
|
||||
}
|
||||
|
||||
// make new node the root with uniform probability
|
||||
if (rand() % (Size(root) + 1) == 0) {
|
||||
return RootInsert(root, n);
|
||||
}
|
||||
|
||||
if (*n < *root) {
|
||||
SetLeft(root, RandomizedInsert(root->left, n));
|
||||
} else {
|
||||
SetRight(root, RandomizedInsert(root->right, n));
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
//
|
||||
// removal
|
||||
//
|
||||
void Clear(Node *n) {
|
||||
void Clear(node_type *n) {
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
|
@ -176,10 +142,8 @@ class IntervalTree {
|
|||
delete n;
|
||||
}
|
||||
|
||||
//
|
||||
// iteration
|
||||
//
|
||||
bool Iterate(Node *n, interval_type low, interval_type high, iterate_cb cb) {
|
||||
bool Iterate(node_type *n, interval_type low, interval_type high,
|
||||
iterate_cb cb) {
|
||||
if (!n) {
|
||||
return false;
|
||||
}
|
||||
|
@ -203,106 +167,6 @@ class IntervalTree {
|
|||
|
||||
return found1 || found2 || found3;
|
||||
}
|
||||
|
||||
//
|
||||
// helper methods
|
||||
//
|
||||
void SetRoot(Node *n) {
|
||||
root_ = n;
|
||||
|
||||
if (root_) {
|
||||
root_->parent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SetLeft(Node *parent, Node *n) {
|
||||
parent->left = n;
|
||||
|
||||
if (parent->left) {
|
||||
parent->left->parent = parent;
|
||||
}
|
||||
|
||||
FixCounts(parent);
|
||||
}
|
||||
|
||||
void SetRight(Node *parent, Node *n) {
|
||||
parent->right = n;
|
||||
|
||||
if (parent->right) {
|
||||
parent->right->parent = parent;
|
||||
}
|
||||
|
||||
FixCounts(parent);
|
||||
}
|
||||
|
||||
void FixCounts(Node *n) {
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
|
||||
n->num = 1 + Size(n->left) + Size(n->right);
|
||||
n->max = n->high;
|
||||
if (n->left) {
|
||||
n->max = std::max(n->max, n->left->max);
|
||||
}
|
||||
if (n->right) {
|
||||
n->max = std::max(n->max, n->right->max);
|
||||
}
|
||||
}
|
||||
|
||||
Node *RotateRight(Node *root) {
|
||||
Node *parent = root->parent;
|
||||
Node *n = root->left;
|
||||
|
||||
SetLeft(root, n->right);
|
||||
SetRight(n, root);
|
||||
|
||||
if (parent) {
|
||||
if (parent->left == root) {
|
||||
SetLeft(parent, n);
|
||||
} else {
|
||||
SetRight(parent, n);
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
Node *RotateLeft(Node *root) {
|
||||
Node *parent = root->parent;
|
||||
Node *n = root->right;
|
||||
|
||||
SetRight(root, n->left);
|
||||
SetLeft(n, root);
|
||||
|
||||
if (parent) {
|
||||
if (parent->left == root) {
|
||||
SetLeft(parent, n);
|
||||
} else {
|
||||
SetRight(parent, n);
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
Node *Join(Node *a, Node *b) {
|
||||
if (!a) {
|
||||
return b;
|
||||
} else if (!b) {
|
||||
return a;
|
||||
}
|
||||
|
||||
if ((rand() % (Size(a) + Size(b))) < Size(a)) {
|
||||
SetRight(a, Join(a->right, b));
|
||||
return a;
|
||||
} else {
|
||||
SetLeft(b, Join(a, b->left));
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
Node *root_;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,452 @@
|
|||
#ifndef INTRUSIVE_TREE_H
|
||||
#define INTRUSIVE_TREE_H
|
||||
|
||||
#include <functional>
|
||||
#include <stdlib.h>
|
||||
#include "core/assert.h"
|
||||
|
||||
namespace dreavm {
|
||||
|
||||
enum Color { RED = true, BLACK = false };
|
||||
|
||||
template <typename T>
|
||||
struct IntrusiveTreeNode {
|
||||
IntrusiveTreeNode()
|
||||
: parent(nullptr), left(nullptr), right(nullptr), color(RED) {}
|
||||
|
||||
T *parent, *left, *right;
|
||||
Color color;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class IntrusiveTree {
|
||||
protected:
|
||||
IntrusiveTree() : root_(nullptr) {}
|
||||
virtual ~IntrusiveTree() {}
|
||||
|
||||
virtual void AugmentPropagate(T *n) = 0;
|
||||
virtual void AugmentRotate(T *oldn, T *newn) = 0;
|
||||
|
||||
T *Link(T *n) {
|
||||
// set initial root
|
||||
if (!root_) {
|
||||
root_ = n;
|
||||
}
|
||||
|
||||
// adjust tree, starting at the newly inserted node, to satisfy the
|
||||
// properties of a valid red-black tree
|
||||
LinkCase1(n);
|
||||
|
||||
// force root to black
|
||||
root_->color = BLACK;
|
||||
|
||||
AugmentPropagate(n->parent);
|
||||
|
||||
#ifdef VERIFY_INTRUSIVE_TREE
|
||||
VerifyProperties();
|
||||
#endif
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void Unlink(T *n) {
|
||||
// when deleting a node with two non-leaf children, we swap the node with
|
||||
// its in-order predecessor (the maximum or rightmost element in the left
|
||||
// subtree), and then delete the original node which now has only one
|
||||
// non-leaf child
|
||||
if (n->left && n->right) {
|
||||
T *pred = MaximumNode(n->left);
|
||||
SwapNode(n, pred);
|
||||
}
|
||||
|
||||
// a node with at most one non-leaf child can simply be replaced with its
|
||||
// non-leaf child
|
||||
CHECK(!n->left || !n->right);
|
||||
T *child = n->right ? n->right : n->left;
|
||||
if (color(n) == BLACK) {
|
||||
UnlinkCase1(n);
|
||||
}
|
||||
ReplaceNode(n, child);
|
||||
|
||||
// force root to black
|
||||
if (root_) {
|
||||
root_->color = BLACK;
|
||||
}
|
||||
|
||||
// fix up each node in the parent chain
|
||||
AugmentPropagate(n->parent);
|
||||
|
||||
#ifdef VERIFY_INTRUSIVE_TREE
|
||||
VerifyProperties();
|
||||
#endif
|
||||
}
|
||||
|
||||
T *root_;
|
||||
|
||||
private:
|
||||
static inline T *grandparent(T *n) {
|
||||
CHECK_NOTNULL(n);
|
||||
CHECK_NOTNULL(n->parent); // not the root node
|
||||
CHECK_NOTNULL(n->parent->parent); // not child of root
|
||||
return n->parent->parent;
|
||||
}
|
||||
static inline T *sibling(T *n) {
|
||||
CHECK_NOTNULL(n);
|
||||
CHECK_NOTNULL(n->parent); // root node has no sibling
|
||||
if (n == n->parent->left) {
|
||||
return n->parent->right;
|
||||
} else {
|
||||
return n->parent->left;
|
||||
}
|
||||
}
|
||||
static inline T *uncle(T *n) {
|
||||
CHECK_NOTNULL(n);
|
||||
CHECK_NOTNULL(n->parent); // root node has no uncle
|
||||
CHECK_NOTNULL(n->parent->parent); // children of root have no uncle
|
||||
return sibling(n->parent);
|
||||
}
|
||||
static inline Color color(T *n) { return n ? n->color : BLACK; }
|
||||
|
||||
static void VerifyProperty1(T *root) { CHECK_EQ(color(root), BLACK); }
|
||||
|
||||
// Every red node has two children, and both are black (or equivalently, the
|
||||
// parent of every red node is black).
|
||||
static void VerifyProperty2(T *n) {
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
if (color(n) == RED) {
|
||||
CHECK_EQ(color(n->left), BLACK);
|
||||
CHECK_EQ(color(n->right), BLACK);
|
||||
CHECK_EQ(color(n->parent), BLACK);
|
||||
}
|
||||
VerifyProperty2(n->left);
|
||||
VerifyProperty2(n->right);
|
||||
}
|
||||
|
||||
// All paths from any given node to its leaf nodes contain the same number of
|
||||
// black nodes. This one is the trickiest to verify; we do it by traversing
|
||||
// the tree, incrementing a black node count as we go. The first time we reach
|
||||
// a leaf we save the count. We return the count so that when we subsequently
|
||||
// reach other leaves, we compare the count to the saved count.
|
||||
static int VerifyProperty3(T *n, int black_count = 0,
|
||||
int path_black_count = -1) {
|
||||
if (color(n) == BLACK) {
|
||||
black_count++;
|
||||
}
|
||||
|
||||
if (!n) {
|
||||
if (path_black_count == -1) {
|
||||
path_black_count = black_count;
|
||||
} else {
|
||||
CHECK_EQ(black_count, path_black_count);
|
||||
}
|
||||
return path_black_count;
|
||||
}
|
||||
|
||||
path_black_count = VerifyProperty3(n->left, black_count, path_black_count);
|
||||
path_black_count = VerifyProperty3(n->right, black_count, path_black_count);
|
||||
|
||||
return path_black_count;
|
||||
}
|
||||
|
||||
void VerifyProperties() {
|
||||
VerifyProperty1(root_);
|
||||
VerifyProperty2(root_);
|
||||
VerifyProperty3(root_);
|
||||
}
|
||||
|
||||
// n r
|
||||
// r -> n
|
||||
// l l
|
||||
void RotateLeft(T *n) {
|
||||
T *r = n->right;
|
||||
ReplaceNode(n, r);
|
||||
n->right = r->left;
|
||||
if (n->right) {
|
||||
n->right->parent = n;
|
||||
}
|
||||
r->left = n;
|
||||
r->left->parent = r;
|
||||
|
||||
AugmentRotate(n, r);
|
||||
}
|
||||
|
||||
// n l
|
||||
// l -> n
|
||||
// r r
|
||||
void RotateRight(T *n) {
|
||||
T *l = n->left;
|
||||
T *r = l->right;
|
||||
ReplaceNode(n, l);
|
||||
n->left = r;
|
||||
if (n->left) {
|
||||
n->left->parent = n;
|
||||
}
|
||||
l->right = n;
|
||||
l->right->parent = l;
|
||||
|
||||
AugmentRotate(n, l);
|
||||
}
|
||||
|
||||
void ReplaceNode(T *oldn, T *newn) {
|
||||
if (oldn->parent) {
|
||||
if (oldn == oldn->parent->left) {
|
||||
oldn->parent->left = newn;
|
||||
} else {
|
||||
oldn->parent->right = newn;
|
||||
}
|
||||
} else {
|
||||
root_ = newn;
|
||||
}
|
||||
|
||||
if (newn) {
|
||||
newn->parent = oldn->parent;
|
||||
}
|
||||
}
|
||||
|
||||
void SwapNode(T *a, T *b) {
|
||||
T tmp = *a;
|
||||
|
||||
// note, swapping pointers is complicated by the case where a parent is
|
||||
// being swapped with its child, for example:
|
||||
// a -> b
|
||||
// b a
|
||||
// in this case, a swap(a, b) would result in a->parent == a, when it
|
||||
// should be b
|
||||
if ((a->parent = b->parent == a ? b : b->parent)) {
|
||||
if (a->parent->left == b) {
|
||||
a->parent->left = a;
|
||||
} else if (a->parent->right == b) {
|
||||
a->parent->right = a;
|
||||
}
|
||||
} else {
|
||||
root_ = a;
|
||||
}
|
||||
if ((a->left = b->left == a ? b : b->left)) {
|
||||
a->left->parent = a;
|
||||
}
|
||||
if ((a->right = b->right == a ? b : b->right)) {
|
||||
a->right->parent = a;
|
||||
}
|
||||
a->color = b->color;
|
||||
|
||||
if ((b->parent = tmp.parent == b ? a : tmp.parent)) {
|
||||
if (b->parent->left == a) {
|
||||
b->parent->left = b;
|
||||
} else if (b->parent->right == a) {
|
||||
b->parent->right = b;
|
||||
}
|
||||
} else {
|
||||
root_ = b;
|
||||
}
|
||||
if ((b->left = tmp.left == b ? a : tmp.left)) {
|
||||
b->left->parent = b;
|
||||
}
|
||||
if ((b->right = tmp.right == b ? a : tmp.right)) {
|
||||
b->right->parent = b;
|
||||
}
|
||||
b->color = tmp.color;
|
||||
}
|
||||
|
||||
T *MaximumNode(T *n) {
|
||||
while (n->right) {
|
||||
n = n->right;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// In this case, the new node is now the root node of the tree. Since the root
|
||||
// node must be black, and changing its color adds the same number of black
|
||||
// nodes to every path, we simply recolor it black. Because only the root node
|
||||
// has no parent, we can assume henceforth that the node has a parent.
|
||||
void LinkCase1(T *n) {
|
||||
if (!n->parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
LinkCase2(n);
|
||||
}
|
||||
|
||||
// In this case, the new node has a black parent. All the properties are still
|
||||
// satisfied and we return->
|
||||
void LinkCase2(T *n) {
|
||||
if (color(n->parent) == BLACK) {
|
||||
return; // Tree is still valid
|
||||
}
|
||||
|
||||
LinkCase3(n);
|
||||
}
|
||||
|
||||
// In this case, the uncle node is red. We recolor the parent and uncle black
|
||||
// and the grandparent red. However, the red grandparent node may now violate
|
||||
// the red-black tree properties; we recursively invoke this procedure on it
|
||||
// from case 1 to deal with this.
|
||||
void LinkCase3(T *n) {
|
||||
if (color(uncle(n)) == RED) {
|
||||
n->parent->color = BLACK;
|
||||
uncle(n)->color = BLACK;
|
||||
grandparent(n)->color = RED;
|
||||
LinkCase1(grandparent(n));
|
||||
return;
|
||||
}
|
||||
|
||||
LinkCase4(n);
|
||||
}
|
||||
|
||||
// In this case, we deal with two cases that are mirror images of one another:
|
||||
// * The new node is the right child of its parent and the parent is the left
|
||||
// child of the grandparent. In this case we rotate left about the parent.
|
||||
// * The new node is the left child of its parent and the parent is the right
|
||||
// child of the grandparent. In this case we rotate right about the parent.
|
||||
// Neither of these fixes the properties, but they put the tree in the correct
|
||||
// form to apply case 5.
|
||||
void LinkCase4(T *n) {
|
||||
if (n == n->parent->right && n->parent == grandparent(n)->left) {
|
||||
RotateLeft(n->parent);
|
||||
n = n->left;
|
||||
} else if (n == n->parent->left && n->parent == grandparent(n)->right) {
|
||||
RotateRight(n->parent);
|
||||
n = n->right;
|
||||
}
|
||||
|
||||
LinkCase5(n);
|
||||
}
|
||||
|
||||
// In this final case, we deal with two cases that are mirror images of one
|
||||
// another:
|
||||
// * The new node is the left child of its parent and the parent is the left
|
||||
// child of the grandparent. In this case we rotate right about the
|
||||
// grandparent.
|
||||
// * The new node is the right child of its parent and the parent is the right
|
||||
// child of the grandparent. In this case we rotate left about the
|
||||
// grandparent.
|
||||
// Now the properties are satisfied and all cases have been covered.
|
||||
void LinkCase5(T *n) {
|
||||
n->parent->color = BLACK;
|
||||
grandparent(n)->color = RED;
|
||||
if (n == n->parent->left && n->parent == grandparent(n)->left) {
|
||||
RotateRight(grandparent(n));
|
||||
} else {
|
||||
CHECK(n == n->parent->right && n->parent == grandparent(n)->right);
|
||||
RotateLeft(grandparent(n));
|
||||
}
|
||||
}
|
||||
|
||||
// In this case, N has become the root node. The deletion removed one black
|
||||
// node from every path, so no properties are violated.
|
||||
void UnlinkCase1(T *n) {
|
||||
if (!n->parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
UnlinkCase2(n);
|
||||
}
|
||||
|
||||
// N has a red sibling. In this case we exchange the colors of the parent and
|
||||
// sibling, then rotate about the parent so that the sibling becomes the
|
||||
// parent of its former parent. This does not restore the tree properties, but
|
||||
// reduces the problem to one of the remaining cases.
|
||||
void UnlinkCase2(T *n) {
|
||||
if (color(sibling(n)) == RED) {
|
||||
n->parent->color = RED;
|
||||
sibling(n)->color = BLACK;
|
||||
if (n == n->parent->left) {
|
||||
RotateLeft(n->parent);
|
||||
} else {
|
||||
RotateRight(n->parent);
|
||||
}
|
||||
}
|
||||
|
||||
UnlinkCase3(n);
|
||||
}
|
||||
|
||||
// In this case N's parent, sibling, and sibling's children are black. In this
|
||||
// case we paint the sibling red. Now all paths passing through N's parent
|
||||
// have one less black node than before the deletion, so we must recursively
|
||||
// run this procedure from case 1 on N's parent.
|
||||
void UnlinkCase3(T *n) {
|
||||
if (color(n->parent) == BLACK && color(sibling(n)) == BLACK &&
|
||||
color(sibling(n)->left) == BLACK && color(sibling(n)->right) == BLACK) {
|
||||
sibling(n)->color = RED;
|
||||
UnlinkCase1(n->parent);
|
||||
return;
|
||||
}
|
||||
|
||||
UnlinkCase4(n);
|
||||
}
|
||||
|
||||
// N's sibling and sibling's children are black, but its parent is red. We
|
||||
// exchange the colors of the sibling and parent; this restores the tree
|
||||
// properties.
|
||||
void UnlinkCase4(T *n) {
|
||||
if (color(n->parent) == RED && color(sibling(n)) == BLACK &&
|
||||
color(sibling(n)->left) == BLACK && color(sibling(n)->right) == BLACK) {
|
||||
sibling(n)->color = RED;
|
||||
n->parent->color = BLACK;
|
||||
return;
|
||||
}
|
||||
|
||||
UnlinkCase5(n);
|
||||
}
|
||||
|
||||
// There are two cases handled here which are mirror images of one another:
|
||||
// * N's sibling S is black, S's left child is red, S's right child is black,
|
||||
// and N is the left child of its parent. We exchange the colors of S and its
|
||||
// left sibling and rotate right at S.
|
||||
// * N's sibling S is black, S's right child is red, S's left child is black,
|
||||
// and N is the right child of its parent. We exchange the colors of S and its
|
||||
// right sibling and rotate left at S.
|
||||
// Both of these function to reduce us to the situation described in case 6.
|
||||
void UnlinkCase5(T *n) {
|
||||
if (n == n->parent->left && color(sibling(n)) == BLACK &&
|
||||
color(sibling(n)->left) == RED && color(sibling(n)->right) == BLACK) {
|
||||
sibling(n)->color = RED;
|
||||
sibling(n)->left->color = BLACK;
|
||||
RotateRight(sibling(n));
|
||||
} else if (n == n->parent->right && color(sibling(n)) == BLACK &&
|
||||
color(sibling(n)->right) == RED &&
|
||||
color(sibling(n)->left) == BLACK) {
|
||||
sibling(n)->color = RED;
|
||||
sibling(n)->right->color = BLACK;
|
||||
RotateLeft(sibling(n));
|
||||
}
|
||||
|
||||
UnlinkCase6(n);
|
||||
}
|
||||
|
||||
// There are two cases handled here which are mirror images of one another:
|
||||
// * N's sibling S is black, S's right child is red, and N is the left child
|
||||
// of its parent. We exchange the colors of N's parent and sibling, make S's
|
||||
// right child black, then rotate left at N's parent.
|
||||
// * N's sibling S is black, S's left child is red, and N is the right child
|
||||
// of its parent. We exchange the colors of N's parent and sibling, make S's
|
||||
// left child black, then rotate right at N's parent.
|
||||
//
|
||||
// This accomplishes three things at once:
|
||||
// * We add a black node to all paths through N, either by adding a black S to
|
||||
// those paths or by recoloring N's parent black.
|
||||
// * We remove a black node from all paths through S's red child, either by
|
||||
// removing P from those paths or by recoloring S.
|
||||
// * We recolor S's red child black, adding a black node back to all paths
|
||||
// through S's red child.
|
||||
//
|
||||
// S's left child has become a child of N's parent during the rotation and so
|
||||
// is unaffected.
|
||||
void UnlinkCase6(T *n) {
|
||||
sibling(n)->color = color(n->parent);
|
||||
n->parent->color = BLACK;
|
||||
if (n == n->parent->left) {
|
||||
CHECK_EQ(color(sibling(n)->right), RED);
|
||||
sibling(n)->right->color = BLACK;
|
||||
RotateLeft(n->parent);
|
||||
} else {
|
||||
CHECK_EQ(color(sibling(n)->left), RED);
|
||||
sibling(n)->left->color = BLACK;
|
||||
RotateRight(n->parent);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -106,12 +106,14 @@ void TextureCache::Clear() {
|
|||
for (auto it = textures_.begin(), e = textures_.end(); it != e; ++it) {
|
||||
Invalidate(it);
|
||||
}
|
||||
|
||||
watches_.Clear();
|
||||
}
|
||||
|
||||
void TextureCache::CheckWrite(uint32_t addr) {
|
||||
PROFILER_GPU("TextureCache::CheckWrite");
|
||||
|
||||
TextureWatchTree::Node *node = watches_.Find(addr, addr);
|
||||
TextureWatchTree::node_type *node = watches_.Find(addr, addr);
|
||||
|
||||
bool handled = node != nullptr;
|
||||
|
||||
|
|
|
@ -18,15 +18,15 @@ namespace holly {
|
|||
|
||||
struct TextureEntry;
|
||||
typedef std::unordered_map<TextureKey, TextureEntry> TextureCacheMap;
|
||||
typedef IntervalTree<uint32_t, TextureKey> TextureWatchTree;
|
||||
typedef IntervalTree<TextureKey> TextureWatchTree;
|
||||
|
||||
struct TextureEntry {
|
||||
TextureEntry(renderer::TextureHandle handle)
|
||||
: handle(handle), texture_watch(nullptr), palette_watch(nullptr) {}
|
||||
|
||||
renderer::TextureHandle handle;
|
||||
TextureWatchTree::Node *texture_watch;
|
||||
TextureWatchTree::Node *palette_watch;
|
||||
TextureWatchTree::node_type *texture_watch;
|
||||
TextureWatchTree::node_type *palette_watch;
|
||||
};
|
||||
|
||||
class TextureCache : public TextureProvider {
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
#include <set>
|
||||
#include "gtest/gtest.h"
|
||||
#include "core/core.h"
|
||||
#define VERIFY_INTRUSIVE_TREE
|
||||
#include "core/interval_tree.h"
|
||||
|
||||
using namespace dreavm;
|
||||
|
||||
typedef IntervalTree<uint32_t, void *> TestTree;
|
||||
typedef IntervalTree<void *> TestTree;
|
||||
typedef TestTree::node_type TestNode;
|
||||
|
||||
class IntervalTreeTest : public ::testing::Test {
|
||||
public:
|
||||
|
@ -18,7 +20,7 @@ class IntervalTreeTest : public ::testing::Test {
|
|||
|
||||
void SetUp() {
|
||||
// insert dummy intervals
|
||||
for (int i = 0; i < 0x10000; i++) {
|
||||
for (int i = 0; i < 0x1000; i++) {
|
||||
uint32_t low = 0;
|
||||
uint32_t high = 0;
|
||||
|
||||
|
@ -27,13 +29,13 @@ class IntervalTreeTest : public ::testing::Test {
|
|||
high = low + INTERVAL;
|
||||
}
|
||||
|
||||
TestTree::Node *n = intervals.Insert(low, high, nullptr);
|
||||
TestNode *n = intervals.Insert(low, high, nullptr);
|
||||
nodes.insert(n);
|
||||
}
|
||||
}
|
||||
|
||||
TestTree intervals;
|
||||
std::set<TestTree::Node *> nodes;
|
||||
std::set<TestNode *> nodes;
|
||||
};
|
||||
|
||||
TEST_F(IntervalTreeTest, ValidateRelations) {
|
||||
|
@ -53,10 +55,9 @@ TEST_F(IntervalTreeTest, Size) {
|
|||
}
|
||||
|
||||
TEST_F(IntervalTreeTest, Height) {
|
||||
// make sure height is within expected range of ~2*log2(n)
|
||||
int height = intervals.Height();
|
||||
int size = intervals.Size();
|
||||
ASSERT_TRUE(height >= log2(size) && height < (3 * log2(size)));
|
||||
ASSERT_TRUE(height <= 2 * log2(size + 1));
|
||||
}
|
||||
|
||||
TEST_F(IntervalTreeTest, Remove) {
|
||||
|
@ -82,7 +83,7 @@ TEST_F(IntervalTreeTest, Clear) {
|
|||
TEST_F(IntervalTreeTest, Find) {
|
||||
for (uint32_t i = 0; i < HIGH; i += 0x1000) {
|
||||
// manually generate a list of results
|
||||
std::set<TestTree::Node *> expected;
|
||||
std::set<TestNode *> expected;
|
||||
|
||||
for (auto n : nodes) {
|
||||
if (i < n->low || i > n->high) {
|
||||
|
@ -94,7 +95,7 @@ TEST_F(IntervalTreeTest, Find) {
|
|||
|
||||
// query the tree for nodes and compare with the expected results
|
||||
int found = 0;
|
||||
TestTree::Node *n = intervals.Find(i, i);
|
||||
TestNode *n = intervals.Find(i, i);
|
||||
|
||||
while (n) {
|
||||
// validate that it's in the expected set
|
||||
|
@ -102,7 +103,7 @@ TEST_F(IntervalTreeTest, Find) {
|
|||
ASSERT_NE(it, expected.end());
|
||||
found++;
|
||||
|
||||
// remove from nodes so the node isn't expected by the next loop iteration
|
||||
// remove from nodes so the node isn't expected by the next loop
|
||||
auto it2 = nodes.find(n);
|
||||
ASSERT_NE(it2, nodes.end());
|
||||
nodes.erase(it2);
|
||||
|
@ -122,7 +123,7 @@ TEST_F(IntervalTreeTest, Find) {
|
|||
TEST_F(IntervalTreeTest, Iterate) {
|
||||
for (uint32_t i = 0; i < HIGH; i += 0x1000) {
|
||||
// manually generate a list of expected nodes
|
||||
std::set<TestTree::Node *> expected;
|
||||
std::set<TestNode *> expected;
|
||||
|
||||
for (auto n : nodes) {
|
||||
if (i < n->low || i > n->high) {
|
||||
|
@ -133,9 +134,9 @@ TEST_F(IntervalTreeTest, Iterate) {
|
|||
}
|
||||
|
||||
// query the tree for nodes
|
||||
std::set<TestTree::Node *> results;
|
||||
std::set<TestNode *> results;
|
||||
|
||||
intervals.Iterate(i, i, [&](const TestTree &tree, TestTree::Node *node) {
|
||||
intervals.Iterate(i, i, [&](const TestTree &tree, TestNode *node) {
|
||||
results.insert(node);
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue