xo-ordinaltree: rbtree ops satisfy gc write barriers

This commit is contained in:
Roland Conybeare 2025-12-04 23:38:56 -05:00
commit 12f40c8049
4 changed files with 77 additions and 88 deletions

View file

@ -121,7 +121,7 @@ namespace xo {
virtual TaggedPtr self_tp() const;
/** print on stream @p os **/
virtual void display(std::ostream & os) const = 0;
virtual void display(std::ostream & os) const;
// Inherited from IObject..

View file

@ -466,10 +466,11 @@ namespace xo {
RbNode * adj_root = this->root_;
std::pair<bool, RbNode *> insert_result
= RbUtil::insert_aux(kv_pair,
true /*allow_replace_flag*/,
this->reduce_fn_,
&adj_root);
= RbUtil::template insert_aux<node_allocator_type>(this->node_alloc_,
kv_pair,
true /*allow_replace_flag*/,
this->reduce_fn_,
&adj_root);
if (insert_result.first) {
++(this->size_);
@ -623,10 +624,10 @@ namespace xo {
virtual TaggedPtr self_tp() const {
return Reflect::make_tp(const_cast<RedBlackTree *>(this));
}
#endif
virtual void display(std::ostream & os) const final override {
os << "<RedBlackTree>";
}
#endif
virtual std::size_t _shallow_size() const final override { return sizeof(*this); }
virtual IObject * _shallow_copy(gc::IAlloc * gc) const final override {
if constexpr (GcObjectInterface::_requires_gc_hooks) {

View file

@ -82,14 +82,6 @@ namespace xo {
}
} /*make_leaf*/
#ifdef OBSOLETE
static Node * make_leaf(value_type && kv_pair,
ReducedValue const & leaf_rv) {
return new Node(kv_pair,
std::pair<ReducedValue, ReducedValue>(leaf_rv, leaf_rv));
} /*make_leaf*/
#endif
/* return #of key/vaue pairs in tree rooted at x. */
static size_t tree_size(Node *x) {
if (x)
@ -146,7 +138,7 @@ namespace xo {
/* replace root pointer *pp_root with x;
* set x parent pointer to nil
*/
static void replace_root_reparent(Node *x, Node **pp_root) {
static void replace_root_reparent(Node * x, Node ** pp_root) {
*pp_root = x;
if (x)
x->parent_ = nullptr;
@ -155,7 +147,11 @@ namespace xo {
/** swap values of all members except @ref contents_
* between @p *lhs and @p *rhs
**/
static void swap_locations(Node * lhs, Node * rhs, bool debug_flag) {
template <typename NodeAllocator>
static void swap_locations(NodeAllocator & alloc,
Node * lhs,
Node * rhs,
bool debug_flag) {
scope log(XO_DEBUG(debug_flag));
assert(lhs->parent() != rhs->parent());
@ -179,7 +175,7 @@ namespace xo {
assert(lhs != rhs->right_child() && "expected left-to-right key order");
if (lhs_parent)
lhs_parent->replace_child_reparent(lhs, rhs);
lhs_parent->replace_child_reparent(alloc, lhs, rhs);
/* now have:
* - rhs->parent() = lhs_parent
@ -187,7 +183,7 @@ namespace xo {
*/
if (rhs_parent)
rhs_parent->replace_child_reparent(rhs, lhs);
rhs_parent->replace_child_reparent(alloc, rhs, lhs);
/* now have:
* - lhs->parent() = rhs_parent
@ -211,10 +207,10 @@ namespace xo {
Node * rhs_left = rhs->left_child();
Node * rhs_right = rhs->right_child();
lhs->assign_child_reparent(D_Left, rhs_left);
lhs->assign_child_reparent(D_Right, rhs_right);
rhs->assign_child_reparent(D_Left, lhs_left);
rhs->assign_child_reparent(D_Right, lhs_right);
lhs->assign_child_reparent(alloc, D_Left, rhs_left);
lhs->assign_child_reparent(alloc, D_Right, rhs_right);
rhs->assign_child_reparent(alloc, D_Left, lhs_left);
rhs->assign_child_reparent(alloc, D_Right, lhs_right);
//std::swap(lhs->child_v_, rhs->child_v_);
@ -318,10 +314,10 @@ namespace xo {
virtual TaggedPtr self_tp() const {
return Reflect::make_tp(const_cast<Node *>(this));
}
#endif
virtual void display(std::ostream & os) const final override {
os << "<Node>";
}
#endif
virtual std::size_t _shallow_size() const final override { return sizeof(*this); }
/* note: only relevant when GcObjectInterface is xo::IObject */
@ -372,27 +368,10 @@ namespace xo {
void assign_color(Color x) { this->color_ = x; }
void assign_size(size_t z) { this->size_ = z; }
void assign_child_reparent(Direction d, Node *new_x) {
Node * old_x = this->child_v_[d];
// trying to fix old_x can be counterproductive,
// since old_x->parent_ may already have been corrected,
//
if (old_x && (old_x->parent_ == this)) {
old_x->parent_ = nullptr;
}
this->child_v_[d] = new_x;
if (new_x) {
new_x->parent_ = this;
}
} /*assign_child_reparent*/
template <typename NodeAllocator>
void assign_child_reparent_2(NodeAllocator & alloc,
Direction d,
Node * new_x)
void assign_child_reparent(NodeAllocator & alloc,
Direction d,
Node * new_x)
{
Node * old_x = this->child_v_[d];
@ -429,11 +408,14 @@ namespace xo {
* - x is nullptr or x.parent is nullptr
* - x_new is nullptr or x_new.parent is this
*/
Direction replace_child_reparent(Node *x, Node *x_new) {
template <typename NodeAllocator>
Direction replace_child_reparent(NodeAllocator & alloc,
Node * x,
Node * x_new) {
Direction d = this->child_direction(x);
if (d == D_Left || d == D_Right) {
this->assign_child_reparent(d, x_new);
if ((d == D_Left) || (d == D_Right)) {
this->assign_child_reparent(alloc, d, x_new);
return d;
} else {
return D_Invalid;

View file

@ -355,8 +355,10 @@ namespace xo {
* (so any key k' in x satisfies k' > h->key)
*
*/
static RbNode *find_glb_aux(RbNode *x, RbNode *h, Key const &k,
bool is_closed) {
static RbNode * find_glb_aux(RbNode * x,
RbNode * h,
Key const & k,
bool is_closed) {
for (;;) {
if (!x)
return h;
@ -458,24 +460,25 @@ namespace xo {
* / \ / \
* T S S R
*/
static RbNode *rotate(Direction d, RbNode *A,
Reduce const & reduce_fn,
RbNode **pp_root) {
template <typename NodeAllocator>
static RbNode * rotate(NodeAllocator & alloc,
Direction d,
RbNode * A,
Reduce const & reduce_fn,
bool debug_flag,
RbNode ** pp_root) {
using xo::scope;
using xo::xtag;
//constexpr char const *c_self = "RbTreeUtil::rotate";
constexpr bool c_logging_enabled = false;
scope log(XO_DEBUG(c_logging_enabled));
scope log(XO_DEBUG(debug_flag));
Direction other_d = other(d);
RbNode *G = A->parent();
RbNode *B = A->child(other_d);
//RbNode *R = A->child(d); // not using
RbNode *S = B->child(d);
//RbNode *T = B->child(other_d); // not using
RbNode * G = A->parent();
RbNode * B = A->child(other_d);
//RbNode * R = A->child(d); // not using
RbNode * S = B->child(d);
//RbNode * T = B->child(other_d); // not using
if (log.enabled()) {
log("rotate-", (d == D_Left) ? "left" : "right",
@ -493,14 +496,14 @@ namespace xo {
}
/* note: this will set A's old child B to have null parent ptr */
A->assign_child_reparent(other_d, S);
A->assign_child_reparent(alloc, other_d, S);
A->local_recalc_size(reduce_fn);
B->assign_child_reparent(d, A);
B->assign_child_reparent(alloc, d, A);
B->local_recalc_size(reduce_fn);
if (G) {
G->replace_child_reparent(A, B);
G->replace_child_reparent(alloc, A, B);
assert(B->parent() == G);
/* note: G.size not affected by rotation */
@ -550,18 +553,20 @@ namespace xo {
* Promise:
* - tree is in RB-shape
*/
static void fixup_red_shape(Direction d, RbNode *G,
template <typename NodeAllocator>
static void fixup_red_shape(NodeAllocator & alloc,
Direction d,
RbNode * G,
Reduce const & reduce_fn,
RbNode **pp_root) {
bool debug_flag,
RbNode ** pp_root) {
using xo::scope;
using xo::xtag;
using xo::print::ccs;
//constexpr char const *c_self = "RbTreeUtil::fixup_red_shape";
constexpr bool c_logging_enabled = false;
constexpr bool c_excessive_verify_enabled = false;
scope log(XO_DEBUG(c_logging_enabled));
scope log(XO_DEBUG(debug_flag));
RbNode *P = G->child(d);
@ -712,7 +717,7 @@ namespace xo {
* / \
* R
*/
RbTreeUtil::rotate(d, P, reduce_fn, pp_root);
RbTreeUtil::rotate(alloc, d, P, reduce_fn, debug_flag, pp_root);
if (c_excessive_verify_enabled)
RbTreeUtil::verify_subtree_ok(reduce_fn, S, nullptr /*&black_height*/);
@ -739,7 +744,7 @@ namespace xo {
(other_d == D_Left) ? "left" : "right", " at G",
xtag("G", G), xtag("G.key", G->key()));
RbTreeUtil::rotate(other_d, G, reduce_fn, pp_root);
RbTreeUtil::rotate(alloc, other_d, G, reduce_fn, debug_flag, pp_root);
if (c_excessive_verify_enabled) {
RbNode *GG = G ? G->parent() : G;
@ -759,7 +764,7 @@ namespace xo {
return;
} /*walk toward root until red violation fixed*/
} /*fixup_red_shape*/
} /*fixup_red_shape*/
/* insert key-value pair (key, value) into *pp_root.
* on exit *pp_root contains new tree with (key, value) inserted.
@ -837,16 +842,16 @@ namespace xo {
kv_pair,
reduce_fn.leaf(kv_pair.second));
N->assign_child_reparent_2(alloc,
d,
new_node);
N->assign_child_reparent(alloc,
d,
new_node);
assert(is_red(N->child(d)));
/* recalculate Node sizes on path [root .. N] */
RbTreeUtil::fixup_ancestor_size(reduce_fn, N, debug_flag);
/* after adding a node, must rebalance to restore RB-shape */
RbTreeUtil::fixup_red_shape(d, N, reduce_fn, pp_root);
RbTreeUtil::fixup_red_shape(alloc, d, N, reduce_fn, debug_flag, pp_root);
//log && log(xtag("path", (char const *)"B"));
@ -918,7 +923,7 @@ namespace xo {
/* d: direction in P to immediate child N;
* also sets N.parent to nil
*/
Direction d = P->replace_child_reparent(N, nullptr);
Direction d = P->replace_child_reparent(alloc, N, nullptr);
traits::deallocate(alloc, N, 1);
@ -1100,7 +1105,7 @@ namespace xo {
assert(is_black(P));
assert(is_black(N));
RbTreeUtil::rotate(d, P, reduce_fn, pp_root);
RbTreeUtil::rotate(alloc, d, P, reduce_fn, debug_flag, pp_root);
/* after rotation d at P:
*
@ -1217,7 +1222,8 @@ namespace xo {
* - D at h
*/
RbTreeUtil::rotate(other_d, S, reduce_fn, pp_root);
RbTreeUtil::rotate(alloc,
other_d, S, reduce_fn, debug_flag, pp_root);
assert(P->child(other_d) == C);
@ -1292,7 +1298,7 @@ namespace xo {
* - S (+also C,D) at h
*/
RbTreeUtil::rotate(d, P, reduce_fn, pp_root);
RbTreeUtil::rotate(alloc, d, P, reduce_fn, debug_flag, pp_root);
/* after rotate at P toward d: *
*
@ -1438,7 +1444,7 @@ namespace xo {
* W
*/
if (P)
P->replace_child_reparent(N, R);
P->replace_child_reparent(alloc, N, R);
/*
* here the triangle ascii art indicates a tree structure,
@ -1452,8 +1458,8 @@ namespace xo {
* / . R Y
* W
*/
R->assign_child_reparent(D_Left, N);
R->assign_child_reparent(D_Right, Y);
R->assign_child_reparent(alloc, D_Left, N);
R->assign_child_reparent(alloc, D_Right, Y);
/*
* here the triangle ascii art indicates a tree structure,
* of arbitrary size
@ -1468,8 +1474,8 @@ namespace xo {
* / \
* R Y
*/
N->assign_child_reparent(D_Left, W);
N->assign_child_reparent(D_Right, nullptr);
N->assign_child_reparent(alloc, D_Left, W);
N->assign_child_reparent(alloc, D_Right, nullptr);
/*
* here the triangle ascii art indicates a tree structure,
* of arbitrary size
@ -1519,7 +1525,7 @@ namespace xo {
* everything except RbNode.contents_.
* Annoying but necessary to have stable Node memory locations
*/
RbNode::swap_locations(R, N, debug_flag);
RbNode::swap_locations(alloc, R, N, debug_flag);
/* swapping locations invalidates RbNode.reduced_.
* But correctness will be restored in erase_1child_aux()
@ -1592,7 +1598,7 @@ namespace xo {
/* replace pointer to N with nil in N's parent. */
if (P) {
P->replace_child_reparent(N, nullptr);
P->replace_child_reparent(alloc, N, nullptr);
log && log("fixup_ancestor_size starting at P");
RbTreeUtil::fixup_ancestor_size(reduce_fn, P, debug_flag);
@ -1613,7 +1619,7 @@ namespace xo {
*/
}
} else /*N->is_black()*/ {
RbNode *R = N->left_child();
RbNode * R = N->left_child();
if (!R)
R = N->right_child();
@ -1628,7 +1634,7 @@ namespace xo {
R->assign_color(C_Black);
if (P) {
P->replace_child_reparent(N, R);
P->replace_child_reparent(alloc, N, R);
log && log("fixup_ancestor_size starting at P");
RbTreeUtil::fixup_ancestor_size(reduce_fn, P, debug_flag);
} else {