xo-ordinaltree: RedBlackTree is gc-alloc-aware

This commit is contained in:
Roland Conybeare 2025-12-03 17:13:11 -05:00
commit 113869342c
4 changed files with 88 additions and 9 deletions

View file

@ -58,7 +58,7 @@ namespace xo {
typename Value, typename Value,
typename Reduce, typename Reduce,
typename Allocator> typename Allocator>
class RedBlackTree { class RedBlackTree : public xo::gc::gc_allocator_traits<Allocator>::template object_interface<Allocator> {
static_assert(ordered_key<Key>); static_assert(ordered_key<Key>);
static_assert(valid_rbtree_reduce_functor<Reduce, Value>); static_assert(valid_rbtree_reduce_functor<Reduce, Value>);
@ -87,6 +87,9 @@ namespace xo {
using iterator = detail::Iterator<Key, Value, Reduce, GcObjectInterface>; using iterator = detail::Iterator<Key, Value, Reduce, GcObjectInterface>;
using const_iterator = detail::ConstIterator<Key, Value, Reduce, GcObjectInterface>; using const_iterator = detail::ConstIterator<Key, Value, Reduce, GcObjectInterface>;
using Reflect = xo::reflect::Reflect;
using TaggedPtr = xo::reflect::TaggedPtr;
public: public:
explicit RedBlackTree(const allocator_type & alloc = allocator_type{}, explicit RedBlackTree(const allocator_type & alloc = allocator_type{},
bool debug_flag = false) : bool debug_flag = false) :
@ -115,6 +118,21 @@ namespace xo {
{} {}
#endif #endif
static RedBlackTree * make(const allocator_type & alloc = allocator_type{},
bool debug_flag = false)
{
RedBlackTree * tree = allocator_traits::allocate(alloc, 1);
try {
// placement new
allocator_traits::construct(alloc, tree, alloc, debug_flag);
return tree;
} catch(...) {
allocator_traits::deallocate(alloc, tree, 1);
}
return nullptr;
}
bool empty() const { return size_ == 0; } bool empty() const { return size_ == 0; }
size_type size() const { return size_; } size_type size() const { return size_; }
size_type max_size() const { return std::numeric_limits<difference_type>::max(); } size_type max_size() const { return std::numeric_limits<difference_type>::max(); }
@ -551,23 +569,52 @@ namespace xo {
return true; return true;
} /*verify_ok*/ } /*verify_ok*/
void display() const { RbUtil::display(this->root_, 0); } /*display*/ void display_to_log() const { RbUtil::display(this->root_, 0); } /*display*/
private: // ----- Inherited from GcObjectInterface -----
virtual TaggedPtr self_tp() const {
return Reflect::make_tp(const_cast<RedBlackTree *>(this));
}
virtual void display(std::ostream & os) const {
os << "<RedBlackTree>";
}
virtual std::size_t _shallow_size() const { return sizeof(*this); }
virtual IObject * _shallow_copy(gc::IAlloc * gc) const {
if constexpr (GcObjectInterface::_requires_gc_hooks) {
xo::Cpof cpof(gc, this);
return new (cpof) RedBlackTree(*this);
} else {
assert(false && "_shallow_copy assumes gc enabled");
return nullptr;
}
}
virtual std::size_t _forward_children(gc::IAlloc * gc) {
if constexpr (GcObjectInterface::_requires_gc_hooks) {
using xo::gc::ObjectVisitor;
ObjectVisitor<Reduce>::forward_children(reduce_fn_, gc);
gc->forward_inplace(reinterpret_cast<IObject **>(&root_));
return RedBlackTree::_shallow_size();
} else {
assert(false && "_forward_children assumes gc enabled");
return 0ul;
}
}
private: private:
/** allocator state **/ /** allocator state **/
node_allocator_type node_alloc_; node_allocator_type node_alloc_;
/** number of nodes in this tree. Each node holds one (key,value) pair **/ /** number of nodes in this tree. Each node holds one (key,value) pair **/
size_t size_ = 0; size_t size_ = 0;
/** root of red/black tree. Empty tree has null root. **/
RbNode * root_ = nullptr;
/** accumulates custom order statistics; /** accumulates custom order statistics;
* for example partial sums of @tparam Values * for example partial sums of @tparam Values
* reduce_fn_:: (Accumulator x Key) -> Accumulator * reduce_fn_:: (Accumulator x Key) -> Accumulator
**/ **/
Reduce reduce_fn_; Reduce reduce_fn_;
/** root of red/black tree. Empty tree has null root. **/
RbNode * root_ = nullptr;
/** true to enable debug logging **/ /** true to enable debug logging **/
bool debug_flag_ = false; bool debug_flag_ = false;
}; /*RedBlackTree*/ }; /*RedBlackTree*/
@ -580,7 +627,7 @@ namespace xo {
operator<<(std::ostream & os, operator<<(std::ostream & os,
RedBlackTree<Key, Value, Reduce, Allocator> const & tree) RedBlackTree<Key, Value, Reduce, Allocator> const & tree)
{ {
tree.display(); tree.display(os);
return os; return os;
} /*operator<<*/ } /*operator<<*/

View file

@ -5,6 +5,7 @@
#pragma once #pragma once
#include "xo/allocutil/ObjectVisitor.hpp"
#include <ostream> #include <ostream>
namespace xo { namespace xo {
@ -34,12 +35,22 @@ namespace xo {
}; /*NullReduce*/ }; /*NullReduce*/
inline std::ostream & operator<<(std::ostream & os, inline std::ostream & operator<<(std::ostream & os,
null_reduce_value /*x*/) null_reduce_value /*x*/)
{ {
os << "{}"; os << "{}";
return os; return os;
} /*operator<<*/ } /*operator<<*/
} /*namespace tree*/ } /*namespace tree*/
namespace gc {
template <typename Value>
class ObjectVisitor<xo::tree::NullReduce<Value>> {
public:
static_assert(std::is_empty_v<xo::tree::NullReduce<Value>>);
static void forward_children(xo::tree::NullReduce<Value> &, xo::gc::IAlloc *) {}
};
} /*namespace gc*/
} /*namespace xo*/ } /*namespace xo*/
/* end NullReduce.hpp */ /* end NullReduce.hpp */

View file

@ -37,8 +37,17 @@ namespace xo {
value_type combine(value_type x, value_type y) const { return x + y; } value_type combine(value_type x, value_type y) const { return x + y; }
bool is_equal(value_type x, value_type y) const { return x == y; } bool is_equal(value_type x, value_type y) const { return x == y; }
}; /*OrdinalReduce*/ }; /*OrdinalReduce*/
} /*namespace tree*/ } /*namespace tree*/
namespace gc {
template <typename Value>
class ObjectVisitor<xo::tree::OrdinalReduce<Value>> {
public:
static_assert(std::is_empty_v<xo::tree::OrdinalReduce<Value>>);
static void forward_children(xo::tree::OrdinalReduce<Value> &, xo::gc::IAlloc *) {}
};
} /*namespace gc*/
} /*namespace xo*/ } /*namespace xo*/
/* end OrdinalReduce.hpp */ /* end OrdinalReduce.hpp */

View file

@ -6,6 +6,7 @@
#pragma once #pragma once
#include "xo/allocutil/ObjectVisitor.hpp"
#include <limits> #include <limits>
namespace xo { namespace xo {
@ -48,7 +49,18 @@ namespace xo {
bool is_equal(value_type const & x, value_type const & y) const { return x == y; } bool is_equal(value_type const & x, value_type const & y) const { return x == y; }
}; /*SumReduce*/ }; /*SumReduce*/
} } /*namespace tree*/
namespace gc {
template <typename Value>
class ObjectVisitor<xo::tree::SumReduce<Value>> {
public:
static_assert(std::is_empty_v<xo::tree::SumReduce<Value>>);
/* trivial, since SumReduce<Value> is stateless */
static void forward_children(xo::tree::SumReduce<Value> &, xo::gc::IAlloc *) {}
};
} /*namespace gc*/
} }
/* end SumReduce.hpp */ /* end SumReduce.hpp */