From 5c7a2e1ad5994a6a646971ef4ed5a1b66fd2ccaa Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 19 Dec 2025 13:55:19 -0500 Subject: [PATCH] xo-alloc2: work on alloc_range + operator++ for alloc iterators [WIP] --- .../xo/alloc2/alloc/RAllocIterator.hpp | 3 + .../include/xo/alloc2/alloc/RAllocator.hpp | 3 +- xo-alloc2/src/alloc2/DArenaIterator.cpp | 2 +- .../alloc2/IAllocIterator_DArenaIterator.cpp | 9 ++ xo-alloc2/src/alloc2/IAllocator_DArena.cpp | 39 +++++++- xo-alloc2/utest/AllocIterator.test.cpp | 95 ++++++++++++++----- xo-alloc2/utest/random_allocs.cpp | 2 +- xo-facet/include/xo/facet/OObject.hpp | 28 +++++- xo-facet/include/xo/facet/obj.hpp | 36 ++++--- 9 files changed, 167 insertions(+), 50 deletions(-) diff --git a/xo-alloc2/include/xo/alloc2/alloc/RAllocIterator.hpp b/xo-alloc2/include/xo/alloc2/alloc/RAllocIterator.hpp index 6c2dd5ef..6a14218e 100644 --- a/xo-alloc2/include/xo/alloc2/alloc/RAllocIterator.hpp +++ b/xo-alloc2/include/xo/alloc2/alloc/RAllocIterator.hpp @@ -27,6 +27,9 @@ namespace xo { return O::iface()->compare(O::data(), other); } void next() noexcept { O::iface()->next(O::data()); } + /** triggers operator++ in obj> **/ + void _preincrement() noexcept { this->next(); } + static bool _valid; }; diff --git a/xo-alloc2/include/xo/alloc2/alloc/RAllocator.hpp b/xo-alloc2/include/xo/alloc2/alloc/RAllocator.hpp index 12d1a780..706b6a86 100644 --- a/xo-alloc2/include/xo/alloc2/alloc/RAllocator.hpp +++ b/xo-alloc2/include/xo/alloc2/alloc/RAllocator.hpp @@ -22,6 +22,7 @@ namespace xo { using DataPtr = Object::DataPtr; using size_type = std::size_t; using value_type = std::byte *; + using range_type = AAllocator::range_type; RAllocator() {} RAllocator(Object::DataPtr data) : Object{std::move(data)} {} @@ -36,7 +37,7 @@ namespace xo { bool contains(const void * p) const noexcept { return O::iface()->contains(O::data(), p); } AllocError last_error() const noexcept { return O::iface()->last_error(O::data()); } AllocInfo alloc_info(value_type mem) const noexcept { return O::iface()->alloc_info(O::data(), mem); } - //range_type alloc_range(DArena & mm) + range_type alloc_range(DArena & mm) const noexcept { return O::iface()->alloc_range(O::data(), mm); } value_type alloc(size_type z) noexcept { return O::iface()->alloc(O::data(), z); } value_type super_alloc(size_type z) noexcept { return O::iface()->super_alloc(O::data(), z); } diff --git a/xo-alloc2/src/alloc2/DArenaIterator.cpp b/xo-alloc2/src/alloc2/DArenaIterator.cpp index b9873e31..bcad394c 100644 --- a/xo-alloc2/src/alloc2/DArenaIterator.cpp +++ b/xo-alloc2/src/alloc2/DArenaIterator.cpp @@ -91,7 +91,7 @@ namespace xo { cmpresult DArenaIterator::compare(const DArenaIterator & other_ix) const noexcept { - scope log(XO_DEBUG(false), + scope log(XO_DEBUG(true), xtag("arena", arena_), xtag("pos", pos_), xtag("other.arena", other_ix.arena_), diff --git a/xo-alloc2/src/alloc2/IAllocIterator_DArenaIterator.cpp b/xo-alloc2/src/alloc2/IAllocIterator_DArenaIterator.cpp index 4d929ded..cfb9a343 100644 --- a/xo-alloc2/src/alloc2/IAllocIterator_DArenaIterator.cpp +++ b/xo-alloc2/src/alloc2/IAllocIterator_DArenaIterator.cpp @@ -5,6 +5,7 @@ #include "arena/IAllocIterator_DArenaIterator.hpp" #include "AllocIterator.hpp" +#include #include namespace xo { @@ -21,6 +22,10 @@ namespace xo { IAllocIterator_DArenaIterator::compare(const DArenaIterator & ix, const obj & other_arg) noexcept { + scope log(XO_DEBUG(true), + xtag("&ix", &ix), + xtag("ix.arena", ix.arena_), xtag("ix.pos", ix.pos_)); + /* downcast from variant */ auto other = obj::from(other_arg); @@ -29,6 +34,10 @@ namespace xo { DArenaIterator & other_ix = *other; + log && log(xtag("&other_ix", &other_ix), + xtag("other_ix.arena", other_ix.arena_), + xtag("other_ix.pos", other_ix.pos_)); + return ix.compare(other_ix); } diff --git a/xo-alloc2/src/alloc2/IAllocator_DArena.cpp b/xo-alloc2/src/alloc2/IAllocator_DArena.cpp index a9edc22a..4580bb32 100644 --- a/xo-alloc2/src/alloc2/IAllocator_DArena.cpp +++ b/xo-alloc2/src/alloc2/IAllocator_DArena.cpp @@ -74,13 +74,46 @@ namespace xo { IAllocator_DArena::alloc_range(const DArena & s, DArena & ialloc) noexcept -> range_type { + scope log(XO_DEBUG(true)); + DArenaIterator * begin_ix = construct_with(ialloc, &s, s.begin_header()); DArenaIterator * end_ix = construct_with(ialloc, &s, s.end_header()); - obj begin_obj = with_facet::mkobj(begin_ix); - obj end_obj = with_facet::mkobj( end_ix); + obj begin_obj = with_facet::mkobj(begin_ix); + obj end_obj = with_facet::mkobj( end_ix); - return std::make_pair(begin_obj, end_obj); + log && log(xtag("begin_obj.typeseq", begin_obj._typeseq())); + + obj begin_vt = begin_obj; + obj end_vt = end_obj; + + log && log(xtag("begin_vt.typeseq", begin_vt._typeseq())); + + log && log(xtag("begin_ix", begin_ix), + xtag("begin_ix.arena", begin_ix->arena_), + xtag("begin_ix.pos", begin_ix->pos_)); + + range_type retval = std::make_pair(begin_vt, end_vt); + + log && log(xtag("1.retval.first.typeseq", retval.first._typeseq())); + + retval.first.from_obj(begin_vt); + retval.second.from_obj(end_vt); + + // this gets correct typeseq. so first.from_obj() works + log && log(xtag("2.retval.first.typeseq", retval.first._typeseq())); + + obj begin_vt2; + + begin_vt2 = retval.first; + + log && log(xtag("3.begin_vt2.typeseq", begin_vt2._typeseq())); + + retval = std::make_pair(begin_vt2, begin_vt2); + + log && log(xtag("4.retval.first.typeseq", retval.first._typeseq())); + + return retval; } bool diff --git a/xo-alloc2/utest/AllocIterator.test.cpp b/xo-alloc2/utest/AllocIterator.test.cpp index 52391efd..9dc4d745 100644 --- a/xo-alloc2/utest/AllocIterator.test.cpp +++ b/xo-alloc2/utest/AllocIterator.test.cpp @@ -8,6 +8,8 @@ #include "arena/IAllocator_DArena.hpp" #include "arena/IAllocIterator_DArenaIterator.hpp" #include "padding.hpp" +#include +#include #include namespace xo { @@ -27,6 +29,10 @@ namespace xo { using xo::mm::padding; using xo::mm::error; + using xo::facet::DVariantPlaceholder; + using xo::facet::obj; + using xo::facet::typeseq; + using std::byte; namespace ut { @@ -146,6 +152,8 @@ namespace xo { TEST_CASE("IAllocIterator-singlearena", "[alloc2]") { + scope log(XO_DEBUG(true)); + ArenaConfig cfg { .name_ = "testarena", .size_ = 64*1024, .store_header_flag_ = true, @@ -166,37 +174,80 @@ namespace xo { REQUIRE(arena.error_count_ == 0); REQUIRE(mem != nullptr); - DArenaIterator ix = arena.begin(); - DArenaIterator end_ix = arena.end(); - - REQUIRE(ix.is_valid()); - REQUIRE(end_ix.is_valid()); - - /* arena is non-empty, so begin!=end */ - REQUIRE (ix != end_ix); - - REQUIRE(arena.error_count_ == 0); - - /* valid iterator can be dereferenced */ { - AllocInfo info = *ix; + DArenaIterator ix = arena.begin(); + DArenaIterator end_ix = arena.end(); + + REQUIRE(ix.is_valid()); + REQUIRE(end_ix.is_valid()); + + /* arena is non-empty, so begin!=end */ + REQUIRE (ix != end_ix); REQUIRE(arena.error_count_ == 0); - REQUIRE(info.is_valid()); - REQUIRE(info.size() == padding::with_padding(req_z)); - auto [payload_lo, payload_hi] = info.payload(); + /* valid iterator can be dereferenced */ + { + AllocInfo info = *ix; - REQUIRE(payload_lo == mem); - REQUIRE(payload_hi == mem + info.size()); + REQUIRE(arena.error_count_ == 0); + REQUIRE(info.is_valid()); + REQUIRE(info.size() == padding::with_padding(req_z)); + + auto [payload_lo, payload_hi] = info.payload(); + + REQUIRE(payload_lo == mem); + REQUIRE(payload_hi == mem + info.size()); + } + + /* valid iterator can be advanced */ + { + ix.next(); + + REQUIRE(arena.error_count_ == 0); + REQUIRE(ix == end_ix); + } } - /* valid iterator can be advanced */ + // repeat, this time with generic iterators { - ix.next(); + log && log(xtag("section", "obj"), + xtag("arena", &arena), + xtag("arena.lo", arena.lo_), + xtag("arena.free", arena.free_)); - REQUIRE(arena.error_count_ == 0); - REQUIRE(ix == end_ix); + DArena scratch_mm + = DArena::map( + ArenaConfig{ + .size_ = 4*1024, + .hugepage_z_ = 4*1024}); + + auto range = a1o.alloc_range(scratch_mm); + + obj ix = range.first; + obj end_ix = range.second; + + REQUIRE(ix.iface()); + REQUIRE(ix.data()); + REQUIRE(end_ix.iface()); + REQUIRE(end_ix.data()); + + REQUIRE(scratch_mm.allocated() >= 2*sizeof(DArenaIterator)); + REQUIRE(scratch_mm.available() > 0); + + log && log(xtag("ix._typeseq", ix._typeseq()), + xtag("ix.data", ix.data())); + + log && log(xtag("typeseq", + typeseq::id())); + log && log(xtag("typeseq", + typeseq::id())); + log && log(xtag("typeseq", + typeseq::id())); + + REQUIRE(ix.compare(ix).is_equal()); + + //REQUIRE(ix.compare(end_ix).is_equal()); } } diff --git a/xo-alloc2/utest/random_allocs.cpp b/xo-alloc2/utest/random_allocs.cpp index b3fb5147..0d638ec0 100644 --- a/xo-alloc2/utest/random_allocs.cpp +++ b/xo-alloc2/utest/random_allocs.cpp @@ -170,7 +170,7 @@ namespace utest { DArena scratch_mm = DArena::map(ArenaConfig{.name_ = "scratch", .size_ = 4*1024, .hugepage_z_ = 4*1024 }); - + auto range = mm.alloc_range(scratch_mm); #ifdef NOT_YET // to verify iteration here, need iterator support in AAllocator diff --git a/xo-facet/include/xo/facet/OObject.hpp b/xo-facet/include/xo/facet/OObject.hpp index 0d5ca263..754e3ee1 100644 --- a/xo-facet/include/xo/facet/OObject.hpp +++ b/xo-facet/include/xo/facet/OObject.hpp @@ -133,6 +133,28 @@ namespace xo { void reset() { data_ = nullptr; } void reset_opaque(Opaque data) { data_ = (DataPtr)data; } + template + OObject & from_obj(const OObject & other) { + if constexpr (std::is_same_v) { + /* Actual runtime type of other encoded in other.iface() + * (whether or not DOther says other is variant). + * Either way need to force vtable replacement, hence memcpy here + */ + ::memcpy((void*)this, (void*)&other, sizeof(*this)); + } else if constexpr (std::is_convertible_v) { + /* other is typed, consistently with *this */ + this->from_data(other.data()); + } else + { + // downcast from variant must be explicit + // + may fail at runtime + + static_assert(std::is_same_v + || std::is_convertible_v); + } + return *this; + } + /** * We're either: * - assigning from pointer with compatible representation @@ -144,8 +166,10 @@ namespace xo { static_assert(std::is_same_v || std::is_convertible_v); - if constexpr (std::is_convertible_v) { - /* assigning from data with same representation, + if constexpr (!std::is_same_v + && std::is_convertible_v) + { + /* assigning typed data with consistent representation * keep .iface_ pointer */ this->data_ = other; diff --git a/xo-facet/include/xo/facet/obj.hpp b/xo-facet/include/xo/facet/obj.hpp index e796f6bd..49fb96ac 100644 --- a/xo-facet/include/xo/facet/obj.hpp +++ b/xo-facet/include/xo/facet/obj.hpp @@ -49,10 +49,14 @@ namespace xo { struct obj : public RoutingType> { using Super = RoutingType>; - obj() {} + obj() : Super() {} explicit obj(Super::DataPtr d) : Super(d) {} - /** copy constructor + obj(const obj & rhs) : Super() { + this->from_data(rhs.data_); + } + + /** pseudo copy constructor * * Intended for use cases: * obj lhs = obj // same type on rhs @@ -65,16 +69,7 @@ namespace xo { || std::is_convertible_v) : Super() { - if constexpr (std::is_convertible_v) { - /* preserving .iface */ - this->data_ = other.data_; - } else { - /* replacing .iface_ - * - * WARNING: only works if .data_ is POD - */ - this->from_data(other.data_); - } + this->from_obj(other); } /** move constructor from a different representation. @@ -88,21 +83,22 @@ namespace xo { || std::is_convertible_v) : Super() { - if constexpr (std::is_convertible_v) { - /* move .data_, keeping .iface_ */ - this->data_ = std::move(other.data_); - } else { - // TODO: instead, have other move itself, + /* replacing .iface_ along w/ .data_ */ + this->from_obj(other); + } - /* replacing .iface_ + .data_ */ - this->from_data(other.data_); - } + obj & operator=(const obj & rhs) { + /* ensure we replace .iface_ along w/ .ata_ */ + this->from_obj(rhs); + return *this; } /** safe downcast from variant. null if downcast fails **/ static obj from(const OObject & other) { return obj(other.template downcast()); } + + obj & operator++() noexcept { this->_preincrement(); return *this; } }; template