diff --git a/xo-alloc2/include/xo/alloc2/AllocHeaderConfig.hpp b/xo-alloc2/include/xo/alloc2/AllocHeaderConfig.hpp index 3c2a8f7f..754a5c98 100644 --- a/xo-alloc2/include/xo/alloc2/AllocHeaderConfig.hpp +++ b/xo-alloc2/include/xo/alloc2/AllocHeaderConfig.hpp @@ -51,6 +51,19 @@ namespace xo { age_bits_{a}, size_bits_{z} {} + /** create header tuple (@p t, @p a, @p z) + * with typeseq @p t, age @p a, size @p z + **/ + std::uint64_t mkheader(std::uint64_t t, + std::uint64_t a, + std::uint64_t z) const noexcept { + uint64_t tseq_bits = (t << (age_bits_ + size_bits_)) & tseq_mask(); + uint64_t age_bits = (a << size_bits_) & age_mask(); + uint64_t size_bits = z & size_mask();; + + return (tseq_bits | age_bits | size_bits); + } + std::uint64_t tseq_mask() const noexcept { // e.g. // FF FF FF 00 00 00 00 00 diff --git a/xo-alloc2/include/xo/alloc2/alloc/AAllocator.hpp b/xo-alloc2/include/xo/alloc2/alloc/AAllocator.hpp index 26056fb4..0fce475c 100644 --- a/xo-alloc2/include/xo/alloc2/alloc/AAllocator.hpp +++ b/xo-alloc2/include/xo/alloc2/alloc/AAllocator.hpp @@ -135,6 +135,11 @@ namespace xo { * zero @p z. **/ virtual value_type sub_alloc(Opaque d, size_type z, bool complete_flag) const = 0; + /** Allocate copy of an existing object @p src. + * Existing object must be contained in @p d. + * NOTE: load bearing for copying garbage collector. + **/ + virtual value_type alloc_copy(Opaque d, value_type src) const = 0; /** reset allocator @p d to empty state. **/ virtual void clear(Opaque d) const = 0; /** Destruct allocator @p d. diff --git a/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Any.hpp b/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Any.hpp index df3d18e5..b45e7651 100644 --- a/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Any.hpp +++ b/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Any.hpp @@ -53,6 +53,7 @@ namespace xo { [[noreturn]] value_type alloc(Opaque, typeseq, std::size_t) const override { _fatal(); } [[noreturn]] value_type super_alloc(Opaque, typeseq, std::size_t) const override { _fatal(); } [[noreturn]] value_type sub_alloc(Opaque, std::size_t, bool) const override { _fatal(); } + [[noreturn]] value_type alloc_copy(Opaque, value_type) const override { _fatal(); } [[noreturn]] void clear(Opaque) const override { _fatal(); } [[noreturn]] void destruct_data(Opaque) const 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 7d23f2d6..790bcdc0 100644 --- a/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Xfer.hpp +++ b/xo-alloc2/include/xo/alloc2/alloc/IAllocator_Xfer.hpp @@ -70,6 +70,8 @@ namespace xo { bool complete_flag) const override { return I::sub_alloc(_dcast(d), z, complete_flag); } + value_type alloc_copy(Opaque d, + value_type src) const override { return I::alloc_copy(_dcast(d), src); } void clear(Opaque d) const override { return I::clear(_dcast(d)); } void destruct_data(Opaque d) const override { return I::destruct_data(_dcast(d)); } ///@} diff --git a/xo-alloc2/include/xo/alloc2/alloc/RAllocator.hpp b/xo-alloc2/include/xo/alloc2/alloc/RAllocator.hpp index e39afe8b..2e9e7a80 100644 --- a/xo-alloc2/include/xo/alloc2/alloc/RAllocator.hpp +++ b/xo-alloc2/include/xo/alloc2/alloc/RAllocator.hpp @@ -45,6 +45,7 @@ namespace xo { value_type sub_alloc(size_type z, bool complete_flag) noexcept { return O::iface()->sub_alloc(O::data(), z, complete_flag); } + value_type alloc_copy(value_type src) noexcept { return O::iface()->alloc_copy(O::data(), src); } bool expand(size_type z) { return O::iface()->expand(O::data(), z); } static bool _valid; diff --git a/xo-alloc2/include/xo/alloc2/arena/DArena.hpp b/xo-alloc2/include/xo/alloc2/arena/DArena.hpp index efaf768b..45556d9f 100644 --- a/xo-alloc2/include/xo/alloc2/arena/DArena.hpp +++ b/xo-alloc2/include/xo/alloc2/arena/DArena.hpp @@ -190,12 +190,18 @@ namespace xo { **/ value_type sub_alloc(size_type z, bool complete_flag); + /** alloc copy of @p src **/ + value_type alloc_copy(value_type src); + /** capture error information: advance error count + set last_error **/ void capture_error(error err, size_type target_z = 0) const; /** alloc driver. shared by alloc(), super_alloc(), sub_alloc() **/ - value_type _alloc(std::size_t req_z, alloc_mode mode); + value_type _alloc(std::size_t req_z, + alloc_mode mode, + typeseq tseq, + uint32_t age); /** expand committed space in arena @p d * to size at least @p z diff --git a/xo-alloc2/include/xo/alloc2/arena/IAllocator_DArena.hpp b/xo-alloc2/include/xo/alloc2/arena/IAllocator_DArena.hpp index d7d5c587..9f18adc1 100644 --- a/xo-alloc2/include/xo/alloc2/arena/IAllocator_DArena.hpp +++ b/xo-alloc2/include/xo/alloc2/arena/IAllocator_DArena.hpp @@ -70,14 +70,10 @@ namespace xo { * @p complete_flag to true. **/ static value_type sub_alloc(DArena &, size_type z, bool complete_flag); + /** allocate copy of @p src in arena @p d. **/ + static value_type alloc_copy(DArena & d, value_type src); static void clear(DArena &); static void destruct_data(DArena &); - - private: - /** alloc driver. shared by alloc(), super_alloc(), sub_alloc() **/ - static value_type _alloc(DArena &, - size_type z, - DArena::alloc_mode mode); }; // template <> diff --git a/xo-alloc2/src/alloc2/DArena.cpp b/xo-alloc2/src/alloc2/DArena.cpp index 62dce150..82557e4b 100644 --- a/xo-alloc2/src/alloc2/DArena.cpp +++ b/xo-alloc2/src/alloc2/DArena.cpp @@ -17,6 +17,8 @@ namespace xo { using xo::facet::typeseq; using std::byte; + using std::cerr; + using std::endl; using std::size_t; namespace mm { @@ -241,8 +243,8 @@ namespace xo { last_error_ = AllocError(); } - DArena::header_type * - DArena::obj2hdr(void * obj) noexcept + auto + DArena::obj2hdr(void * obj) noexcept -> header_type * { assert(config_.store_header_flag_); @@ -260,9 +262,11 @@ namespace xo { byte * header_mem = mem - sizeof(AllocHeader); +#ifdef OBSOLETE // relying on cross-alloc header shenanigans in DX1Collector if (!this->contains(header_mem)) { this->capture_error(error::alloc_info_address); } +#endif AllocHeader * header = (AllocHeader *)header_mem; @@ -320,10 +324,7 @@ namespace xo { * exactly 1 header per alloc() call. * - store_header_flag follows configuration */ - - (void)t; - - return _alloc(req_z, alloc_mode::standard); + return _alloc(req_z, alloc_mode::standard, t, 0 /*age*/); } std::byte * @@ -338,7 +339,9 @@ namespace xo { (void)t; return _alloc(req_z, - alloc_mode::super); + alloc_mode::super, + t, + 0 /*age*/); } std::byte * @@ -354,8 +357,29 @@ namespace xo { return _alloc(req_z, (complete_flag ? alloc_mode::sub_complete - : alloc_mode::sub_incomplete)); + : alloc_mode::sub_incomplete), + typeseq::anon() /*typeseq: ignored*/, + 0 /*age - ignored */); + } + std::byte * + DArena::alloc_copy(std::byte * src) + { + /* NOTE: allocator that owns src must have the same header configuration */ + + assert(config_.store_header_flag_); + + /* src will come from an allocator other than this one; + * we rely on header layout from destination + * allocator -> assumes compatible header config + */ + AllocInfo src_info = alloc_info(src); + + size_t req_z = src_info.size(); + typeseq tseq = typeseq(src_info.tseq()); + uint32_t age = src_info.age(); + + return _alloc(req_z, alloc_mode::standard, tseq, age + 1); } void @@ -373,7 +397,10 @@ namespace xo { } byte * - DArena::_alloc(std::size_t req_z, alloc_mode mode) + DArena::_alloc(std::size_t req_z, + alloc_mode mode, + typeseq tseq, + uint32_t age) { scope log(XO_DEBUG(config_.debug_flag_)); @@ -433,11 +460,12 @@ namespace xo { * reminder: * important to store padded size for correct arena iteration */ - uint64_t header = req_z + dz; + uint64_t header = (req_z + dz); if (store_header_flag) { if (config_.header_.is_size_enabled()) [[likely]] { + header = this->config_.header_.mkheader(tseq.seqno(), age, req_z + dz); hz = sizeof(header); } else { /* req_z doesn't fit in configured header_size_mask bits */ diff --git a/xo-alloc2/src/alloc2/IAllocator_DArena.cpp b/xo-alloc2/src/alloc2/IAllocator_DArena.cpp index 12a4a401..ed956240 100644 --- a/xo-alloc2/src/alloc2/IAllocator_DArena.cpp +++ b/xo-alloc2/src/alloc2/IAllocator_DArena.cpp @@ -133,12 +133,11 @@ namespace xo { return s.sub_alloc(req_z, complete_flag); } - byte * - IAllocator_DArena::_alloc(DArena & s, - std::size_t req_z, - DArena::alloc_mode mode) + std::byte * + IAllocator_DArena::alloc_copy(DArena &s, + value_type src) { - return s._alloc(req_z, mode); + return s.alloc_copy(src); } void diff --git a/xo-gc/include/xo/gc/DX1Collector.hpp b/xo-gc/include/xo/gc/DX1Collector.hpp index bd9d5a65..429d2204 100644 --- a/xo-gc/include/xo/gc/DX1Collector.hpp +++ b/xo-gc/include/xo/gc/DX1Collector.hpp @@ -237,6 +237,11 @@ namespace xo { * New allocs always in gen0 to-space **/ value_type sub_alloc(size_type z, bool complete) noexcept; + /** Allocate copy of source object at @p src. + * Source must be owned by this collector instance. + * Copy will have incremented age. + **/ + value_type alloc_copy(value_type src) noexcept; /** expand gen0 committed size to at least @p z. **/ bool expand(size_type z) noexcept; diff --git a/xo-gc/include/xo/gc/detail/IAllocator_DX1Collector.hpp b/xo-gc/include/xo/gc/detail/IAllocator_DX1Collector.hpp index 6ba79b59..0ed1ab1f 100644 --- a/xo-gc/include/xo/gc/detail/IAllocator_DX1Collector.hpp +++ b/xo-gc/include/xo/gc/detail/IAllocator_DX1Collector.hpp @@ -62,6 +62,7 @@ namespace xo { static value_type alloc(DX1Collector & d, typeseq t, size_type z) noexcept; static value_type super_alloc(DX1Collector & d, typeseq t, size_type z) noexcept; static value_type sub_alloc(DX1Collector & d, size_type z, bool complete) noexcept; + static value_type alloc_copy(DX1Collector & d, value_type src) noexcept; /** expand gen0 spaces (both from-space and to-space) **/ static bool expand(DX1Collector & d, size_type z) noexcept; diff --git a/xo-gc/src/gc/DX1Collector.cpp b/xo-gc/src/gc/DX1Collector.cpp index 26223b92..edfd842c 100644 --- a/xo-gc/src/gc/DX1Collector.cpp +++ b/xo-gc/src/gc/DX1Collector.cpp @@ -231,6 +231,11 @@ namespace xo { return with_facet::mkobj(new_space()).sub_alloc(z, complete); } + auto + DX1Collector::alloc_copy(value_type src) noexcept -> value_type { + return with_facet::mkobj(new_space()).alloc_copy(src); + } + bool DX1Collector::expand(size_type z) noexcept { diff --git a/xo-gc/src/gc/IAllocator_DX1Collector.cpp b/xo-gc/src/gc/IAllocator_DX1Collector.cpp index b5b26b5e..6c4dc5bf 100644 --- a/xo-gc/src/gc/IAllocator_DX1Collector.cpp +++ b/xo-gc/src/gc/IAllocator_DX1Collector.cpp @@ -102,6 +102,12 @@ namespace xo { return d.sub_alloc(z, complete); } + auto + IAllocator_DX1Collector::alloc_copy(DX1Collector & d, value_type src) noexcept -> value_type + { + return d.alloc_copy(src); + } + bool IAllocator_DX1Collector::expand(DX1Collector & d, size_type z) noexcept { diff --git a/xo-object2/include/xo/object2/DFloat.hpp b/xo-object2/include/xo/object2/DFloat.hpp index 52a3ba43..821e31d0 100644 --- a/xo-object2/include/xo/object2/DFloat.hpp +++ b/xo-object2/include/xo/object2/DFloat.hpp @@ -5,10 +5,29 @@ #pragma once +#include + namespace xo { namespace scm { - using DFloat = double; - } /*nmaespace obj*/ + struct DFloat { + using AAllocator = xo::mm::AAllocator; + + explicit DFloat(double x) : value_{x} {} + + /** allocate boxed value @p x using memory from @p mm **/ + static DFloat * make(obj mm, + double x); + + double value() const noexcept { return value_; } + + operator double() const noexcept { return value_; } + + private: + + /** boxed floating-oint value **/ + double value_; + }; + } /*nmaespace scm*/ } /*namespace xo*/ /* end DFloat.hpp */ diff --git a/xo-object2/include/xo/object2/DList.hpp b/xo-object2/include/xo/object2/DList.hpp index 3f6a5639..65ed0ad4 100644 --- a/xo-object2/include/xo/object2/DList.hpp +++ b/xo-object2/include/xo/object2/DList.hpp @@ -3,9 +3,11 @@ * @author Roland Conybeare, Dec 2025 **/ -#include "xo/gc/GCObject.hpp" +#pragma once + +#include //#include "xo/alloc2/gcobject/RGCObject.hpp" -#include "xo/facet/obj.hpp" +#include namespace xo { namespace scm { diff --git a/xo-object2/src/object2/CMakeLists.txt b/xo-object2/src/object2/CMakeLists.txt index e2ffee22..fc66c970 100644 --- a/xo-object2/src/object2/CMakeLists.txt +++ b/xo-object2/src/object2/CMakeLists.txt @@ -8,6 +8,7 @@ set(SELF_SRCS ISequence_Any.cpp ISequence_DList.cpp DList.cpp + DFloat.cpp object2_register_types.cpp ) diff --git a/xo-object2/src/object2/DFloat.cpp b/xo-object2/src/object2/DFloat.cpp new file mode 100644 index 00000000..1ef8fa52 --- /dev/null +++ b/xo-object2/src/object2/DFloat.cpp @@ -0,0 +1,24 @@ +/** @file DFloat.cpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#include "DFloat.hpp" + +namespace xo { + using xo::facet::typeseq; + + namespace scm { + DFloat * + DFloat::make(obj mm, + double x) + { + void * mem = mm.alloc(typeseq::id(), + sizeof(DFloat)); + + return new (mem) DFloat(x); + } + } /*namespace scm*/ +} /*namespace xo*/ + +/* end DFloat.cpp */ diff --git a/xo-object2/utest/X1Collector.test.cpp b/xo-object2/utest/X1Collector.test.cpp index 3857f0bf..742e460c 100644 --- a/xo-object2/utest/X1Collector.test.cpp +++ b/xo-object2/utest/X1Collector.test.cpp @@ -3,17 +3,35 @@ * @author Roland Conybeare, Dec 2025 **/ +#include "DFloat.hpp" +#include "DList.hpp" + +#include "IGCObject_DFloat.hpp" +#include "IGCObject_DList.hpp" + #include #include +#include +#include +#include + #include namespace ut { + using xo::scm::DList; + using xo::scm::DFloat; + using xo::mm::AAllocator; + using xo::mm::AllocInfo; + using xo::mm::AGCObject; using xo::mm::DX1Collector; using xo::mm::DArena; using xo::mm::CollectorConfig; using xo::mm::ArenaConfig; using xo::mm::generation; using xo::mm::role; + using xo::mm::padding; + using xo::facet::with_facet; + using xo::facet::typeseq; namespace { struct testcase_x1 { @@ -61,6 +79,7 @@ namespace ut { DX1Collector gc(cfg); + /* verify initial collector state */ { REQUIRE(gc.name() == "x1_test"); @@ -105,6 +124,43 @@ namespace ut { } /* attempt allocation */ + auto gc_o = with_facet::mkobj(&gc); + + DFloat * x0 = DFloat::make(gc_o, 3.1415927); + auto x0_o = with_facet::mkobj(x0); + + DList * l0 = DList::list(gc_o, x0_o); + auto l0_o = with_facet::mkobj(l0); + + { + { + REQUIRE(x0_o.iface() != nullptr); + REQUIRE(x0_o.data() != nullptr); + REQUIRE(gc.contains(role::to_space(), x0_o.data())); + + /* check alloc info for newly-allocated object */ + AllocInfo info = gc.alloc_info((std::byte *)x0_o.data()); + + REQUIRE(info.age() == 0); + REQUIRE(info.tseq() == typeseq::id().seqno()); + REQUIRE(info.size() >= sizeof(DFloat)); + REQUIRE(info.size() < sizeof(DFloat) + padding::c_alloc_alignment); + } + + { + REQUIRE(l0_o.iface() != nullptr); + REQUIRE(l0_o.data() != nullptr); + REQUIRE(gc.contains(role::to_space(), l0_o.data())); + + AllocInfo info = gc.alloc_info((std::byte *)l0_o.data()); + + REQUIRE(info.age() == 0); + REQUIRE(info.tseq() == typeseq::id().seqno()); + REQUIRE(info.size() >= sizeof(DList)); + REQUIRE(info.size() < sizeof(DList) + padding::c_alloc_alignment); + + } + } } catch (std::exception & ex) { std::cerr << "caught exception: " << ex.what() << std::endl;