xo-alloc / xo-ordinaltree: + concepts + allocator-aware

This commit is contained in:
Roland Conybeare 2025-12-03 15:36:59 -05:00
commit 72e8db30e1
9 changed files with 198 additions and 111 deletions

View file

@ -25,47 +25,6 @@
namespace xo {
namespace tree {
/* concept for the 'Reduce' argument to RedBlackTree<...>
*
* here:
* T = class implementing reduce feature, e.g. SumReduce<...>
* T::value_type = type for output of reduce function.
*
* Value = value_type for rb-tree that supports ordinal statistics
*
* e.g.
* struct ReduceCountAndSum {
* using value_type = std::pair<uint32_t, int64_t>:
*
* value_type nil() { return value_type(0, 0); }
* value_type operator()(value_type const & acc, int64_t val)
* { return value_type(acc.first + val.first, acc.second + val.second); }
* value_type operator()(value_type const & a1, value_type const & a2)
* { return value_type(a1.first + a2.first, a1.second + a2.second); }
* };
*
* Reduce.nil() -> nominal reduction i.e. reduce on empty set
* Reduce.leaf(v) -> reduction on set {v}
*
* in general: at some internal node, tree splits set of key/value pairs on some key k1,
* with a left subtree lh, and a right subtree rh.
*
* for a binary tree we want to maintain:
* - r1: reduce applied to collection
* lh + {k1} = reduce(reduce(lh), k1)
* - r2: reduce applied to collection
* lh + {k1} + rh = reduce.combine(r1, reduce(r2))
*
*/
template <class T, typename Value>
concept ReduceConcept = requires(T r, Value v, typename T::value_type a) {
typename T::value_type;
{ r.nil() } -> std::same_as<typename T::value_type>;
{ r.leaf(v) } -> std::same_as<typename T::value_type>;
{ r(a, v) } -> std::same_as<typename T::value_type>;
{ r.combine(a, a) } -> std::same_as<typename T::value_type>;
};
/** @class RedBlackTree
* @brief red-black tree with order statistics
*
@ -100,8 +59,8 @@ namespace xo {
typename Reduce,
typename Allocator>
class RedBlackTree {
static_assert(ReduceConcept<Reduce, Value>);
//static_assert(requires(Reduce r) { r.nil(); }, "missing .nil() method");
static_assert(ordered_key<Key>);
static_assert(valid_rbtree_reduce_functor<Reduce, Value>);
public:
using key_type = Key;

View file

@ -12,6 +12,7 @@
#include "xo/allocutil/IObject.hpp"
#include "xo/allocutil/ObjectVisitor.hpp"
#include "xo/allocutil/gc_allocator_traits.hpp"
#include <concepts>
#include <cassert>
#include <utility>
@ -33,11 +34,13 @@ namespace xo {
typename Value,
typename Reduce,
typename GcObjectInterface>
requires valid_rbtree_node_params<Key, Value, Reduce, GcObjectInterface>
class Node : public GcObjectInterface {
public:
using ReducedValue = typename Reduce::value_type;
using ContentsType = std::pair<Key const, Value>;
using value_type = std::pair<Key const , Value>;
using rvpair_type = std::pair<ReducedValue, ReducedValue>;
using Reflect = xo::reflect::Reflect;
using TaggedPtr = xo::reflect::TaggedPtr;
using IObject = xo::IObject;
@ -45,13 +48,14 @@ namespace xo {
public:
Node() = default;
Node(value_type const & kv_pair,
std::pair<ReducedValue, ReducedValue> const & r)
: color_(C_Red), size_(1), contents_{kv_pair}, reduced_(r) {}
rvpair_type const & rv_pair)
: color_(C_Red), size_(1), contents_{kv_pair}, reduced_(rv_pair) {}
Node(value_type && kv_pair,
std::pair<ReducedValue, ReducedValue> && r)
rvpair_type && rv_pair)
: color_(C_Red), size_(1),
contents_{std::move(kv_pair)},
reduced_{std::move(r)} {}
reduced_{std::move(rv_pair)} {}
template <typename NodeAllocator>
static Node * make_leaf(NodeAllocator& alloc,
@ -59,12 +63,17 @@ namespace xo {
ReducedValue const & leaf_rv) {
using traits = xo::gc::gc_allocator_traits<NodeAllocator>;
/* verify Node is constructible. instead of relying on traits::construct */
static_assert(std::is_constructible_v<Node,
value_type const &,
rvpair_type const &>);
// get memory
Node * node = traits::allocate(alloc, 1);
try {
// placemenent new
traits::construct(alloc, node, kv_pair,
std::pair<ReducedValue, ReducedValue>(leaf_rv, leaf_rv));
rvpair_type(leaf_rv, leaf_rv));
return node;
} catch(...) {
traits::deallocate(alloc, node, 1);
@ -303,6 +312,60 @@ namespace xo {
xtag("r2", this->reduced2()));
} /*local_recalc_size*/
// ----- inherited from GcObjectInterface -----
virtual TaggedPtr self_tp() const {
return Reflect::make_tp(const_cast<Node *>(this));
}
virtual void display(std::ostream & os) const {
os << "<Node>";
}
virtual std::size_t _shallow_size() const { return sizeof(*this); }
/* note: only relevant when GcObjectInterface is xo::IObject */
virtual IObject * _shallow_copy(gc::IAlloc * gc) const {
if constexpr (GcObjectInterface::_requires_gc_hooks) {
xo::Cpof cpof(gc, this);
return new (cpof) Node(*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;
static_assert(std::is_convertible_v<decltype(parent_), IObject *>,
"parent_ must be convertible to IObject*");
static_assert(std::is_convertible_v<decltype(child_v_[0]), IObject *>,
"child_v_[0] must be convertible to IObject*");
gc->forward_inplace(reinterpret_cast<IObject **>(&parent_));
gc->forward_inplace(reinterpret_cast<IObject **>(&child_v_[0]));
gc->forward_inplace(reinterpret_cast<IObject **>(&child_v_[1]));
/* for key, must cast away const so we can forward */
Key & key = const_cast<Key &>(contents_.first);
ObjectVisitor<Key>::forward_children(key, gc);
Value & value = contents_.second;
ObjectVisitor<Value>::forward_children(value, gc);
ReducedValue & rv1 = reduced_.first;
ObjectVisitor<ReducedValue>::forward_children(rv1, gc);
ReducedValue & rv2 = reduced_.second;
ObjectVisitor<ReducedValue>::forward_children(rv2, gc);
return Node::_shallow_size();
} else {
assert(false && "_forward_children assumes gc enabled");
return 0ul;
}
}
private:
void assign_color(Color x) { this->color_ = x; }
void assign_size(size_t z) { this->size_ = z; }
@ -345,60 +408,6 @@ namespace xo {
}
} /*replace_child_reparent*/
// ----- inherited from GcObjectInterface -----
virtual TaggedPtr self_tp() const {
return Reflect::make_tp(const_cast<Node *>(this));
}
virtual void display(std::ostream & os) const {
os << "<Node>";
}
virtual std::size_t _shallow_size() const { return sizeof(*this); }
/* note: only relevant when GcObjectInterface is xo::IObject */
virtual IObject * _shallow_copy(gc::IAlloc * gc) const {
if constexpr (GcObjectInterface::_requires_gc_hooks) {
xo::Cpof cpof(gc, this);
return new (cpof) Node(*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;
static_assert(std::is_convertible_v<decltype(parent_), IObject *>,
"parent_ must be convertible to IObject*");
static_assert(std::is_convertible_v<decltype(child_v_[0]), IObject *>,
"child_v_[0] must be convertible to IObject*");
gc->forward_inplace(reinterpret_cast<IObject **>(&parent_));
gc->forward_inplace(reinterpret_cast<IObject **>(&child_v_[0]));
gc->forward_inplace(reinterpret_cast<IObject **>(&child_v_[1]));
/* must cast away const so we can forward */
Key & key = const_cast<Key &>(contents_.first);
ObjectVisitor<Key>::forward_children(key, gc);
Value & value = contents_.second;
ObjectVisitor<Value>::forward_children(value, gc);
ReducedValue & rv1 = reduced_.first;
ObjectVisitor<ReducedValue>::forward_children(rv1, gc);
ReducedValue & rv2 = reduced_.second;
ObjectVisitor<ReducedValue>::forward_children(rv2, gc);
return Node::_shallow_size();
} else {
assert(false && "_forward_children assumes gc enabled");
return 0ul;
}
}
private:
friend class RbTreeUtil<Key, Value, Reduce, GcObjectInterface>;
template <typename Key1, typename Value1, typename Reduce1, typename Allocator>
@ -443,6 +452,7 @@ namespace xo {
*/
std::array<Node *, 2> child_v_ = {nullptr, nullptr};
}; /*Node*/
} /*namespace detail*/
} /*namespace tree*/
} /*namespace xo*/

View file

@ -62,6 +62,69 @@ namespace xo {
IL_AfterEnd,
}; /*IteratorLocation*/
} /*namespace detail*/
template <typename Key>
concept ordered_key = (std::copyable<Key>
&& std::default_initializable<Key>
&& std::totally_ordered<Key>);
template <typename Value>
concept valid_rbtree_node_value = (std::copyable<Value>
&& std::default_initializable<Value>);
/* concept for the 'Reduce' argument to RedBlackTree<...>
*
* here:
* T = class implementing reduce feature, e.g. SumReduce<...>
* T::value_type = type for output of reduce function.
*
* Value = value_type for rb-tree that supports ordinal statistics
*
* e.g.
* struct ReduceCountAndSum {
* using value_type = std::pair<uint32_t, int64_t>:
*
* value_type nil() { return value_type(0, 0); }
* value_type operator()(value_type const & acc, int64_t val)
* { return value_type(acc.first + val.first, acc.second + val.second); }
* value_type operator()(value_type const & a1, value_type const & a2)
* { return value_type(a1.first + a2.first, a1.second + a2.second); }
* };
*
* Reduce.nil() -> nominal reduction i.e. reduce on empty set
* Reduce.leaf(v) -> reduction on set {v}
*
* in general: at some internal node, tree splits set of key/value pairs on some key k1,
* with a left subtree lh, and a right subtree rh.
*
* for a binary tree we want to maintain:
* - r1: reduce applied to collection
* lh + {k1} = reduce(reduce(lh), k1)
* - r2: reduce applied to collection
* lh + {k1} + rh = reduce.combine(r1, reduce(r2))
*
*/
template <typename Reduce, typename Value>
concept valid_rbtree_reduce_functor = requires(const Reduce & reduce,
const Value & value,
typename Reduce::value_type const & rv1,
typename Reduce::value_type const & rv2)
{
typename Reduce::value_type;
{ reduce.nil() } -> std::convertible_to<typename Reduce::value_type>;
{ reduce.leaf(value) } -> std::convertible_to<typename Reduce::value_type>;
{ reduce.combine(rv1, rv2) } -> std::convertible_to<typename Reduce::value_type>;
requires std::default_initializable<Reduce>;
};
template <typename Key, typename Value, typename Reduce, typename GcObjectInterface>
concept valid_rbtree_node_params = (ordered_key<Key>
&& valid_rbtree_node_value<Value>
&& valid_rbtree_reduce_functor<Reduce, Value>
);
} /*namespace tree*/
} /*namespace xo*/