From 70ce4712e1bb4190f523eb3f4818b8ff14d214de Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 18 Dec 2025 18:34:54 -0500 Subject: [PATCH] xo-alloc2: + Allocator.alloc_range() with DArena input --- .../xo/alloc2/alloc/AAllocIterator.hpp | 2 +- .../include/xo/alloc2/alloc/AAllocator.hpp | 23 ++++++---- .../include/xo/alloc2/alloc/AllocIterator.hpp | 24 ---------- .../xo/alloc2/alloc/IAllocator_Any.hpp | 1 + .../xo/alloc2/alloc/IAllocator_Xfer.hpp | 7 +-- .../xo/alloc2/arena/DArenaIterator.hpp | 8 ++++ .../xo/alloc2/arena/IAllocator_DArena.hpp | 23 +++++++--- .../xo/alloc2/gc/IAllocator_DX1Collector.hpp | 6 +++ xo-alloc2/src/alloc2/DArenaIterator.cpp | 46 ++++++++++++++----- xo-alloc2/src/alloc2/IAllocator_DArena.cpp | 36 ++++++++++++++- .../src/alloc2/IAllocator_DX1Collector.cpp | 28 +++++++++++ xo-alloc2/src/alloc2/cmpresult.cpp | 4 +- xo-alloc2/utest/AllocIterator.test.cpp | 9 +++- xo-facet/include/xo/facet/OObject.hpp | 9 +++- xo-facet/include/xo/facet/obj.hpp | 24 +++++++--- 15 files changed, 182 insertions(+), 68 deletions(-) delete mode 100644 xo-alloc2/include/xo/alloc2/alloc/AllocIterator.hpp diff --git a/xo-alloc2/include/xo/alloc2/alloc/AAllocIterator.hpp b/xo-alloc2/include/xo/alloc2/alloc/AAllocIterator.hpp index d8f4c42f..f2d332b3 100644 --- a/xo-alloc2/include/xo/alloc2/alloc/AAllocIterator.hpp +++ b/xo-alloc2/include/xo/alloc2/alloc/AAllocIterator.hpp @@ -37,4 +37,4 @@ namespace xo { } /*namespace mm*/ } /*namespace xo*/ -/* end AllocIterator.hpp */ +/* end AAllocIterator.hpp */ diff --git a/xo-alloc2/include/xo/alloc2/alloc/AAllocator.hpp b/xo-alloc2/include/xo/alloc2/alloc/AAllocator.hpp index 57802def..6b83c506 100644 --- a/xo-alloc2/include/xo/alloc2/alloc/AAllocator.hpp +++ b/xo-alloc2/include/xo/alloc2/alloc/AAllocator.hpp @@ -7,8 +7,10 @@ #include "AllocError.hpp" #include "AllocInfo.hpp" -#include "xo/facet/facet_implementation.hpp" -#include "xo/facet/typeseq.hpp" +#include "AllocIterator.hpp" +#include +#include +#include #include namespace xo { @@ -32,6 +34,8 @@ namespace xo { using value_type = std::byte *; /** object header, if configured **/ using header_type = std::uint64_t; + /** iterator range. These are forward iterators over allocs **/ + using range_type = std::pair, obj>; ///@} /* @@ -85,20 +89,21 @@ namespace xo { * Non-const @p d because may stash error details **/ virtual AllocInfo alloc_info(Copaque d, value_type mem) const noexcept = 0; - /** Ideally we want to control allocation for iterator here. - * Awkward to describe to compiler since we don't have vt yet. - * OTOH iteration over allocs is a niche feature. - * Consider alternatives: + /** Ideally we want to control allocator for iterator here. + * Awkward to supply to compiler since we don't have obj yet. + * OTOH iteration over allocs is a super-niche feature. + * + * Rejected alternatives: * - put begin/end in separate interface. e.g. extend AAllocator * - layer of indirection: begin/end return iterator factory. * Then allocator can be passed to iterator factory separately. * Helps because factory can be static * - abandon allocator support in this case. Instead will need to * reinstate uvt (unique variant), use heap - * - just pass DArena& for alloc-iterator-allocation + * + * @p mm is allocator for resulting iterator range **/ - //virtual facet::vt begin(Copaque d, DArena & ialloc) const noexcept; - // virtual obj end() const noexcept = 0; + virtual range_type alloc_range(Copaque d, DArena & mm) const noexcept = 0; /** expand committed space in arena @p d * to size at least @p z diff --git a/xo-alloc2/include/xo/alloc2/alloc/AllocIterator.hpp b/xo-alloc2/include/xo/alloc2/alloc/AllocIterator.hpp deleted file mode 100644 index af5c7e8e..00000000 --- a/xo-alloc2/include/xo/alloc2/alloc/AllocIterator.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/** @file AllocIterator.hpp - * - * @author Roland Conybeare, Dec 2025 - **/ - -#pragma once - -namespace xo { - namespace mm { - - /** @class AllocIterator - * @brief iterator over arena allocations. - * - * Intended for instrumentation/diagnostics. - * Not needed for normal operator - **/ - struct AllocIterator { - - }; - - } /*namespace mm*/ -} /*namespace xo*/ - -/* end AllocIterator.hpp */ diff --git a/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Any.hpp b/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Any.hpp index 9de4170c..842d147e 100644 --- a/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Any.hpp +++ b/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Any.hpp @@ -45,6 +45,7 @@ namespace xo { [[noreturn]] AllocInfo alloc_info(Copaque, value_type) const noexcept override { _fatal(); } // defn in .cpp - problematic to require compiler know vt defn here //[[noreturn]] facet::vt begin(Copaque, DArena &) const noexcept override; // { _fatal(); } + [[noreturn]] range_type alloc_range(Copaque, DArena &) const noexcept override { _fatal(); } // non-const methods [[noreturn]] bool expand(Opaque, std::size_t) const noexcept override { _fatal(); } diff --git a/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Xfer.hpp b/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Xfer.hpp index 79f76c8d..61c9adbf 100644 --- a/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Xfer.hpp +++ b/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Xfer.hpp @@ -39,12 +39,13 @@ namespace xo { return I::contains(_dcast(d), p); } AllocError last_error(Copaque d) const noexcept override { return I::last_error(_dcast(d)); } - - // non-const methods - AllocInfo alloc_info(Copaque d, value_type mem) const noexcept override { return I::alloc_info(_dcast(d), mem); } + range_type alloc_range(Copaque d, DArena & mm) const noexcept override { return I::alloc_range(_dcast(d), mm); } + + // non-const methods + bool expand(Opaque d, std::size_t z) const noexcept override { return I::expand(_dcast(d), z); } value_type alloc(Opaque d, diff --git a/xo-alloc2/include/xo/alloc2/arena/DArenaIterator.hpp b/xo-alloc2/include/xo/alloc2/arena/DArenaIterator.hpp index 5a237c4a..05cf7b7e 100644 --- a/xo-alloc2/include/xo/alloc2/arena/DArenaIterator.hpp +++ b/xo-alloc2/include/xo/alloc2/arena/DArenaIterator.hpp @@ -59,6 +59,14 @@ namespace xo { **/ static DArenaIterator end(const DArena * arena); + /** Address of allocation header for beginning of alloc range in @p arena **/ + static AllocHeader * begin_header(const DArena * arena); + /** Address of allocation header for end of alloc range. + * This is the address of header for _next_ allocation in @p arena + * i.e. free pointer + **/ + static AllocHeader * end_header(const DArena * arena); + /** A valid iterator can be compared, at least with itself * It can be dereferenced if is also non-empty **/ diff --git a/xo-alloc2/include/xo/alloc2/arena/IAllocator_DArena.hpp b/xo-alloc2/include/xo/alloc2/arena/IAllocator_DArena.hpp index e340ff32..1fcb064d 100644 --- a/xo-alloc2/include/xo/alloc2/arena/IAllocator_DArena.hpp +++ b/xo-alloc2/include/xo/alloc2/arena/IAllocator_DArena.hpp @@ -19,15 +19,20 @@ namespace xo { } namespace mm { - /* changes here coordinate with: - * AAllocator AAllocator.hpp - * IAllocator_Any IAllocator_Any.hpp - * IAllocator_Xfer IAllocator_Xfer.hpp - * RAllocator RAllocator.hpp - */ + /** @class IAllocator_DArena + * @brief Provide AAllocator interface for DArena state + **/ struct IAllocator_DArena { + /* changes here coordinate with: + * AAllocator AAllocator.hpp + * IAllocator_Any IAllocator_Any.hpp + * IAllocator_Xfer IAllocator_Xfer.hpp + * RAllocator RAllocator.hpp + */ using size_type = std::size_t; using value_type = std::byte *; + using range_type = std::pair, + obj>; enum class alloc_mode : uint8_t { standard, @@ -44,9 +49,13 @@ namespace xo { static size_type allocated(const DArena &) noexcept; static bool contains(const DArena &, const void * p) noexcept; static AllocError last_error(const DArena &) noexcept; - /** retrieve allocation bookkeeping info for @p mem from arena @p d **/ static AllocInfo alloc_info(const DArena &, value_type mem) noexcept; + /** create alloc-iterator range over allocs on @d, + * Iterators themselves allocated from @p ialloc. + **/ + static range_type alloc_range(const DArena & d, DArena & ialloc) noexcept; + /** expand committed space in arena @p d * to size at least @p z * In practice will round up to a multiple of @ref page_z_. diff --git a/xo-alloc2/include/xo/alloc2/gc/IAllocator_DX1Collector.hpp b/xo-alloc2/include/xo/alloc2/gc/IAllocator_DX1Collector.hpp index 958770cf..1fb48b0d 100644 --- a/xo-alloc2/include/xo/alloc2/gc/IAllocator_DX1Collector.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/IAllocator_DX1Collector.hpp @@ -31,6 +31,8 @@ namespace xo { struct IAllocator_DX1Collector { using size_type = std::size_t; using value_type = std::byte *; + using range_type = std::pair, + obj>; // todo: available() @@ -51,6 +53,10 @@ namespace xo { static AllocError last_error(const DX1Collector &) noexcept; /** fetch allocation bookkeeping info **/ static AllocInfo alloc_info(const DX1Collector & d, value_type mem) noexcept; + /** create alloc-iterator range over allocs in @d, + * with iterator working storage obtained from @p ialloc + **/ + static range_type alloc_range(const DX1Collector & d, DArena & ialloc) noexcept; /** always alloc in gen0 to-space **/ static value_type alloc(DX1Collector & d, size_type z) noexcept; diff --git a/xo-alloc2/src/alloc2/DArenaIterator.cpp b/xo-alloc2/src/alloc2/DArenaIterator.cpp index 47047462..65258eba 100644 --- a/xo-alloc2/src/alloc2/DArenaIterator.cpp +++ b/xo-alloc2/src/alloc2/DArenaIterator.cpp @@ -19,16 +19,10 @@ namespace xo { constexpr bool c_debug_flag = false; scope log(XO_DEBUG(c_debug_flag)); - assert(arena); - - if (arena->config_.store_header_flag_ == false) { - arena->capture_error(error::alloc_iterator_not_supported); + AllocHeader * begin_hdr = begin_header(arena); + if (!begin_hdr) return DArenaIterator::invalid(); - } - - byte * begin_byte = arena->lo_; - AllocHeader * begin_hdr = (AllocHeader *)begin_byte; log && log(xtag("begin_hdr", begin_hdr)); @@ -41,20 +35,48 @@ namespace xo { constexpr bool c_debug_flag = false; scope log(XO_DEBUG(c_debug_flag)); + AllocHeader * end_hdr = end_header(arena); + + if (!end_hdr) + return DArenaIterator::invalid(); + + log && log(xtag("end_hdr", end_hdr)); + + return DArenaIterator(arena, end_hdr); + } + + AllocHeader * + DArenaIterator::begin_header(const DArena * arena) + { assert(arena); if (arena->config_.store_header_flag_ == false) { arena->capture_error(error::alloc_iterator_not_supported); - return DArenaIterator::invalid(); + return nullptr; + } + + byte * begin_byte = arena->lo_; + AllocHeader * begin_hdr = (AllocHeader *)begin_byte; + + return begin_hdr; + } + + AllocHeader * + DArenaIterator::end_header(const DArena * arena) + { + assert(arena); + + if (arena->config_.store_header_flag_ == false) { + arena->capture_error(error::alloc_iterator_not_supported); + + return nullptr; } byte * end_byte = arena->free_; AllocHeader * end_hdr = (AllocHeader *)end_byte; - log && log(xtag("end_hdr", end_hdr)); - - return DArenaIterator(arena, end_hdr); + return end_hdr; } AllocInfo diff --git a/xo-alloc2/src/alloc2/IAllocator_DArena.cpp b/xo-alloc2/src/alloc2/IAllocator_DArena.cpp index a34a224b..4636dbe1 100644 --- a/xo-alloc2/src/alloc2/IAllocator_DArena.cpp +++ b/xo-alloc2/src/alloc2/IAllocator_DArena.cpp @@ -3,15 +3,20 @@ * @author Roland Conybeare, Dec 2025 **/ +#include "AllocIterator.hpp" #include "arena/IAllocator_DArena.hpp" +#include "arena/IAllocIterator_DArenaIterator.hpp" // for alloc_range +#include "arena/DArenaIterator.hpp" #include "padding.hpp" -#include "xo/indentlog/scope.hpp" +#include +#include #include #include #include #include namespace xo { + using xo::facet::with_facet; using std::size_t; using std::byte; @@ -65,6 +70,35 @@ namespace xo { return s.alloc_info(mem); } + void dummy(const DArena & s) { + byte * begin_mem = nullptr; + DArenaIterator * ix = new (begin_mem) DArenaIterator(&s, DArenaIterator::begin_header(&s)); + obj ix_vt{ix}; + + + } + + auto + IAllocator_DArena::alloc_range(const DArena & s, + DArena & ialloc) noexcept -> range_type + { + byte * begin_mem = IAllocator_DArena::alloc(ialloc, + sizeof(DArenaIterator)); + byte * end_mem = IAllocator_DArena::alloc(ialloc, + sizeof(DArenaIterator)); + + assert(begin_mem); + assert(end_mem); + + DArenaIterator * begin_ix = new (begin_mem) DArenaIterator(&s, DArenaIterator::begin_header(&s)); + DArenaIterator * end_ix = new ( end_mem) DArenaIterator(&s, DArenaIterator::end_header(&s)); + + obj begin_obj = with_facet::mkobj(begin_ix); + obj end_obj = with_facet::mkobj( end_ix); + + return std::make_pair(begin_obj, end_obj); + } + bool IAllocator_DArena::expand(DArena & s, size_t target_z) noexcept { diff --git a/xo-alloc2/src/alloc2/IAllocator_DX1Collector.cpp b/xo-alloc2/src/alloc2/IAllocator_DX1Collector.cpp index ea8cf76e..ffebc138 100644 --- a/xo-alloc2/src/alloc2/IAllocator_DX1Collector.cpp +++ b/xo-alloc2/src/alloc2/IAllocator_DX1Collector.cpp @@ -6,9 +6,14 @@ **/ #include "gc/IAllocator_DX1Collector.hpp" +#include "gc/IAllocIterator_DX1CollectorIterator.hpp" +#include "gc/DX1CollectorIterator.hpp" +#include "arena/IAllocator_DArena.hpp" namespace xo { + using xo::facet::with_facet; using std::size_t; + using std::byte; namespace mm { using value_type = IAllocator_DX1Collector::value_type; @@ -61,6 +66,29 @@ namespace xo { return d.last_error(); } + auto + IAllocator_DX1Collector::alloc_range(const DX1Collector & d, + DArena & ialloc) noexcept -> range_type + { + byte * begin_mem = IAllocator_DArena::alloc(ialloc, + sizeof(DX1CollectorIterator)); + byte * end_mem = IAllocator_DArena::alloc(ialloc, + sizeof(DX1CollectorIterator)); + + assert(begin_mem); + assert(end_mem); + + DX1CollectorIterator * begin_ix + = new (begin_mem) DX1CollectorIterator(d.begin()); + DX1CollectorIterator * end_ix + = new ( end_mem) DX1CollectorIterator(d.end()); + + obj begin_obj = with_facet::mkobj(begin_ix); + obj end_obj = with_facet::mkobj( end_ix); + + return std::make_pair(begin_obj, end_obj); + } + auto IAllocator_DX1Collector::alloc(DX1Collector & d, size_type z) noexcept -> value_type { diff --git a/xo-alloc2/src/alloc2/cmpresult.cpp b/xo-alloc2/src/alloc2/cmpresult.cpp index d9f47137..cd7c2998 100644 --- a/xo-alloc2/src/alloc2/cmpresult.cpp +++ b/xo-alloc2/src/alloc2/cmpresult.cpp @@ -14,12 +14,14 @@ namespace xo { { switch (x) { case comparison::invalid: - return "?comparison"; + break; case comparison::comparable: return "cmp"; case comparison::incomparable: return "!cmp"; } + + return "?comparison"; } void diff --git a/xo-alloc2/utest/AllocIterator.test.cpp b/xo-alloc2/utest/AllocIterator.test.cpp index d941f034..52391efd 100644 --- a/xo-alloc2/utest/AllocIterator.test.cpp +++ b/xo-alloc2/utest/AllocIterator.test.cpp @@ -110,7 +110,14 @@ namespace xo { REQUIRE(ix.is_valid()); REQUIRE(end_ix.is_valid()); - /* iteration not supported since we did not set */ + /* verify obj 'fat pointer' packaging */ + obj ix_vt{&ix}; + obj end_ix_vt{&end_ix}; + + REQUIRE(ix_vt.iface()); + REQUIRE(ix_vt.data()); + REQUIRE(end_ix_vt.iface()); + REQUIRE(end_ix_vt.data()); /* arena is empty, so begin==end */ REQUIRE(ix == end_ix); diff --git a/xo-facet/include/xo/facet/OObject.hpp b/xo-facet/include/xo/facet/OObject.hpp index 08327d04..0d5ca263 100644 --- a/xo-facet/include/xo/facet/OObject.hpp +++ b/xo-facet/include/xo/facet/OObject.hpp @@ -145,10 +145,15 @@ namespace xo { || std::is_convertible_v); if constexpr (std::is_convertible_v) { - /** assigning from data with same representation **/ + /* assigning from data with same representation, + * keep .iface_ pointer + */ this->data_ = other; } else /*DRepr is DVariantPlaceholder*/ { - /** assigning to variant **/ + /** assigning to variant + * + * This implementation only valid for POD pointers. + **/ /* acquire fat object pointer for (AFacet, DOther) */ OObject oother(other); diff --git a/xo-facet/include/xo/facet/obj.hpp b/xo-facet/include/xo/facet/obj.hpp index 05dc1938..e796f6bd 100644 --- a/xo-facet/include/xo/facet/obj.hpp +++ b/xo-facet/include/xo/facet/obj.hpp @@ -6,6 +6,8 @@ #pragma once #include "RRouter.hpp" +#include +#include namespace xo { namespace facet { @@ -64,8 +66,13 @@ namespace xo { : 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_); } } @@ -78,15 +85,18 @@ namespace xo { template obj(const obj && other) requires (std::is_same_v - || std::is_convertible_v) + || std::is_convertible_v) : Super() { - static_assert(sizeof(obj) - == sizeof(obj)); + if constexpr (std::is_convertible_v) { + /* move .data_, keeping .iface_ */ + this->data_ = std::move(other.data_); + } else { + // TODO: instead, have other move itself, - other.move2any(this); - - assert(other.data_ = nullptr); + /* replacing .iface_ + .data_ */ + this->from_data(other.data_); + } } /** safe downcast from variant. null if downcast fails **/ @@ -99,7 +109,7 @@ namespace xo { using vt = obj; /** Use: - * auto o = with_facet::obj(&data); + * auto o = with_facet::mkobj(&data); **/ template struct with_facet {