From bf555ddd10a72b66afa54a633df38059469cd1f0 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 15 Jan 2026 13:15:18 -0500 Subject: [PATCH] xo-object2: + DArray [WIP] + utest --- include/xo/object2/DArray.hpp | 121 ++++++++++++++++++++++++++++++++++ src/object2/CMakeLists.txt | 1 + src/object2/DArray.cpp | 53 +++++++++++++++ utest/CMakeLists.txt | 1 + utest/DArray.test.cpp | 96 +++++++++++++++++++++++++++ 5 files changed, 272 insertions(+) create mode 100644 include/xo/object2/DArray.hpp create mode 100644 src/object2/DArray.cpp create mode 100644 utest/DArray.test.cpp diff --git a/include/xo/object2/DArray.hpp b/include/xo/object2/DArray.hpp new file mode 100644 index 0000000..2de3b31 --- /dev/null +++ b/include/xo/object2/DArray.hpp @@ -0,0 +1,121 @@ +/** @file DArray.hpp +* + * @author Roland Conybeare, Jan 2026 + **/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace xo { + namespace scm { + /** @class DArray + * @brief Polymorphic array implementation with gc hooks + * + * 1D Array implementation for Schematika + * Like DString, this implementation has max capacity + * fixed at construction time, but not part of type. + * Can reallocate to change + **/ + struct DArray { + public: + /** @defgroup darray-types type traits **/ + ///@{ + + /** type for array size **/ + using size_type = std::uint32_t; + /** xo allocator facet **/ + using AAllocator = xo::mm::AAllocator; + /** garbage collector facet **/ + using ACollector = xo::mm::ACollector; + /** gc-aware object facet **/ + using AGCObject = xo::mm::AGCObject; + /** pretty-printer state for APrintable **/ + using ppindentinfo = xo::print::ppindentinfo; + + ///@} + /** @defgroup darray-ctors constructors **/ + ///@{ + + /** default ctor. zero capacity sentinel **/ + DArray() = default; + + /** not simply copyable because of flexible array. + * Need allocator. See @ref clone + **/ + DArray(const DArray &) = delete; + + /** create empty array with space for @p cap elements + * using memory from allocator @p mm. + * Nullptr if space exhausted + **/ + static DArray * empty(obj mm, + size_type cap); + + ///@} + /** @defgroup darray-access acecss methods **/ + ///@{ + /** true iff array is empty **/ + bool is_empty() const noexcept { return size_ == 0; } + /** only support finite arrays :-) **/ + bool is_finite() const noexcept { return true; } + /** return element @p index of this array (0-based) **/ + obj at(size_type index) const; + ///@} + /** @defgroup darray-iterators iterators **/ + ///@{ + + ///@} + /** @defgroup darray-assign assignment **/ + ///@{ + /** append @p elt at the end of array. + * true on success, false otherwise + **/ + bool push_back(obj elt) noexcept; + ///@} + /** @defgroup darray-general general methods **/ + ///@{ + + ///@} + /** @defgroup darray-conversion-operators conversion operators **/ + ///@{ + + ///@} + /** @defgroup darray-sequence-methods **/ + ///@{ + + ///@} + /** @defgroup darray-printable-methods **/ + ///@{ + + ///@} + /** @defgroup darray-gcobject-methods **/ + ///@{ + + ///@} + + private: + /** @defgroup darray-instance-variables instance variables **/ + ///@{ + + /** extent of @ref elts_ array **/ + size_type capacity_ = 0; + /** array size + * Invariant: size_ <= capacity_ + **/ + size_type size_ = 0; + /** array elements, using flexible array **/ + obj elts_[]; + + ///@} + }; + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end DArray.hpp */ diff --git a/src/object2/CMakeLists.txt b/src/object2/CMakeLists.txt index 5764d98..6e3ba70 100644 --- a/src/object2/CMakeLists.txt +++ b/src/object2/CMakeLists.txt @@ -12,6 +12,7 @@ set(SELF_SRCS IPrintable_DFloat.cpp IPrintable_DInteger.cpp IPrintable_DString.cpp + DArray.cpp DList.cpp DFloat.cpp DInteger.cpp diff --git a/src/object2/DArray.cpp b/src/object2/DArray.cpp new file mode 100644 index 0000000..1039427 --- /dev/null +++ b/src/object2/DArray.cpp @@ -0,0 +1,53 @@ +/** @file DArray.cpp +* + * @author Roland Conybeare, Jan 2026 + **/ + +#include "DArray.hpp" + +namespace xo { + using xo::mm::AGCObject; + using xo::facet::typeseq; + + namespace scm { + DArray * + DArray::empty(obj mm, + size_type cap) + { + assert(cap > 0); + + DArray * result = nullptr; + + if (cap > 0) { + void * mem = mm.alloc(typeseq::id(), + sizeof(DArray) + cap * sizeof(obj)); + + result = new (mem) DArray(); + + assert(result); + + result->capacity_ = cap; + result->size_ = 0; + } + + return result; + } + + bool + DArray::push_back(obj elt) noexcept { + if (size_ >= capacity_) { + return false; + } else { + static_assert(!std::is_trivially_constructible_v>); + + void * mem = &(elts_[size_]); + + new (mem) obj(elt); + + ++(this->size_); + + return true; + } + } + } /*namespace scm*/ +} /*namespace xo*/ diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index 7207b7d..573fefd 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -3,6 +3,7 @@ set(UTEST_EXE utest.object2) set(UTEST_SRCS object2_utest_main.cpp + DArray.test.cpp DString.test.cpp StringOps.test.cpp X1Collector.test.cpp diff --git a/utest/DArray.test.cpp b/utest/DArray.test.cpp new file mode 100644 index 0000000..14282f8 --- /dev/null +++ b/utest/DArray.test.cpp @@ -0,0 +1,96 @@ +/** @file DArray.test.cpp + * + * @author Roland Conybeare, Jan 2026 + **/ + +#include +#include +#include +#include +#include + +namespace xo { + using xo::scm::DArray; + using xo::scm::DInteger; + using xo::mm::AAllocator; + using xo::mm::AGCObject; + using xo::mm::DArena; + using xo::mm::ArenaConfig; + using xo::facet::with_facet; + using xo::facet::obj; + + namespace ut { + TEST_CASE("DArray-empty", "[object2][DArray]") + { + ArenaConfig cfg { .name_ = "testarena", + .size_ = 4*1024 }; + DArena arena = DArena::map(cfg); + auto alloc = with_facet::mkobj(&arena); + + DArray * arr = DArray::empty(alloc, 16); + + REQUIRE(arr != nullptr); + REQUIRE(arr->is_empty() == true); + REQUIRE(arr->is_finite() == true); + } + + TEST_CASE("DArray-push_back", "[object2][DArray]") + { + ArenaConfig cfg { .name_ = "testarena", + .size_ = 4*1024 }; + DArena arena = DArena::map(cfg); + auto alloc = with_facet::mkobj(&arena); + + DArray * arr = DArray::empty(alloc, 16); + REQUIRE(arr != nullptr); + + obj elt = DInteger::box(alloc, 42); + + bool ok = arr->push_back(elt); + + REQUIRE(ok == true); + REQUIRE(arr->is_empty() == false); + } + + TEST_CASE("DArray-push_back-multiple", "[object2][DArray]") + { + ArenaConfig cfg { .name_ = "testarena", + .size_ = 4*1024 }; + DArena arena = DArena::map(cfg); + auto alloc = with_facet::mkobj(&arena); + + DArray * arr = DArray::empty(alloc, 4); + REQUIRE(arr != nullptr); + + for (int i = 0; i < 4; ++i) { + obj elt = DInteger::box(alloc, 100 + i); + bool ok = arr->push_back(elt); + REQUIRE(ok == true); + } + + REQUIRE(arr->is_empty() == false); + } + + TEST_CASE("DArray-push_back-overflow", "[object2][DArray]") + { + ArenaConfig cfg { .name_ = "testarena", + .size_ = 4*1024 }; + DArena arena = DArena::map(cfg); + auto alloc = with_facet::mkobj(&arena); + + DArray * arr = DArray::empty(alloc, 2); + REQUIRE(arr != nullptr); + + obj e1 = DInteger::box(alloc, 1); + obj e2 = DInteger::box(alloc, 2); + obj e3 = DInteger::box(alloc, 3); + + REQUIRE(arr->push_back(e1) == true); + REQUIRE(arr->push_back(e2) == true); + REQUIRE(arr->push_back(e3) == false); + } + + } /*namespace ut*/ +} /*namespace xo*/ + +/* end DArray.test.cpp */