From 09252c3ec3f7fe0db51565a536e56224e47f6fa8 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Wed, 1 Jul 2026 21:39:59 +0200 Subject: [PATCH] feat: introduce list type Closes: #33 --- furvm/include/furvm/thing.hpp | 115 +++++++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 17 deletions(-) diff --git a/furvm/include/furvm/thing.hpp b/furvm/include/furvm/thing.hpp index 02f91a4..17d0afd 100644 --- a/furvm/include/furvm/thing.hpp +++ b/furvm/include/furvm/thing.hpp @@ -5,6 +5,7 @@ #include "furvm/exceptions.hpp" #include "furvm/fwd.hpp" +#include #include #include #include @@ -20,26 +21,42 @@ namespace furvm { enum class type_t : std::uint32_t { Primitive = 0, Reference, + List, }; using primitive_type = std::uint64_t; using reference_type = std::shared_ptr; +using list_type = std::shared_ptr; struct type { type_t t; union { primitive_type primitive; reference_type reference; + list_type list; }; + type(type_t type) + : t(type), primitive(0) {} + type(primitive_type primitive) : t(type_t::Primitive), primitive(primitive) {} type(const reference_type& reference) : t(type_t::Reference), reference(reference) {} + static type make_list(const list_type& list) { + type type(type_t::List); + new (&type.list) list_type(list); + return type; + } + ~type() { - if (t == type_t::Reference) reference.~reference_type(); + switch (t) { + case type_t::Reference: reference.~reference_type(); break; + case type_t::List: list.~list_type(); break; + default: break; + } } type(type&& other) noexcept @@ -47,6 +64,7 @@ struct type { switch (t) { case type_t::Primitive: primitive = other.primitive; break; case type_t::Reference: reference = std::move(other.reference); break; + case type_t::List: list = std::move(other.list); break; } } @@ -56,6 +74,7 @@ struct type { switch (t) { case type_t::Primitive: primitive = other.primitive; break; case type_t::Reference: reference = std::move(other.reference); break; + case type_t::List: list = std::move(other.list); break; } return *this; @@ -66,6 +85,7 @@ struct type { switch (t) { case type_t::Primitive: primitive = other.primitive; break; case type_t::Reference: reference = other.reference; break; + case type_t::List: list = other.list; break; } } @@ -75,6 +95,7 @@ struct type { switch (t) { case type_t::Primitive: primitive = other.primitive; break; case type_t::Reference: reference = other.reference; break; + case type_t::List: list = other.list; break; } return *this; @@ -88,6 +109,11 @@ using long_t = std::int64_t; /**< An 8-byte integer. */ using reference_t = std::byte*; +struct list_t { + long_t size; + std::byte* data; +}; + /** * @brief A 1-byte integer thing type. */ @@ -133,13 +159,14 @@ public: : m_type(type), m_size(compute_size(type)), m_allocator(allocator) { // TODO: Account for alignment m_data = m_allocator.allocate(m_size); + std::memset(m_data, 0, m_size); } /** * @brief Destructs a thing. */ ~thing() { - if (m_data != nullptr) m_allocator.deallocate(m_data, m_size); + if (m_data != nullptr && m_size > 0) m_allocator.deallocate(m_data, m_size); } /** @@ -184,6 +211,9 @@ public: case type_t::Reference: { std::memcpy(res.m_data, m_data, m_size); } break; + case type_t::List: { + copy_list(m_type->list, res.get(), get()); + } break; } return std::move(res); } @@ -216,7 +246,8 @@ public: */ template T& get() { - if (m_size != sizeof(T)) throw bad_thing_access(); + std::size_t size = m_size > 0 ? m_size : compute_size_na(m_type); + if (size != sizeof(T)) throw bad_thing_access(); return *std::launder(reinterpret_cast(m_data)); } @@ -227,7 +258,8 @@ public: */ template const T& get() const { - if (m_size != sizeof(T)) throw bad_thing_access(); + std::size_t size = m_size > 0 ? m_size : compute_size_na(m_type); + if (size != sizeof(T)) throw bad_thing_access(); return *std::launder(reinterpret_cast(m_data)); } public: @@ -336,22 +368,10 @@ public: } thing reference() const { - thing res = { std::make_shared(m_type), m_allocator }; + thing res = { std::make_shared({ m_type }), m_allocator }; res.get() = m_data; return std::move(res); } -private: - std::size_t compute_size(const type_p& type) const { - switch (m_type->t) { - case type_t::Primitive: return (m_type->primitive + 3) & ~3; - case type_t::Reference: return sizeof(reference_t); - } - - throw std::runtime_error("unreachable"); - } -private: - thing(const type_p& type, std::byte* data, const allocator_type& allocator = {}) - : m_type(type), m_size(compute_size(type)), m_data(data), m_allocator(allocator) {} thing resolve() const { thing rsv = { m_type, m_data, m_allocator }; @@ -359,6 +379,67 @@ private: rsv = { rsv.m_type->reference, rsv.get(), std::move(rsv.m_allocator) }; return rsv; } + + void resize(long_t newSize) { + if (m_type->t != type_t::List) throw bad_thing_access(); + auto& list = get(); + if (newSize < 0 || newSize == list.size) return; + std::byte* newData = new std::byte[compute_size_na(m_type->list) * newSize]; + std::memcpy(newData, list.data, compute_size_na(m_type->list) * std::min(list.size, newSize)); + list.size = newSize; + delete[] list.data; + list.data = newData; + } + + thing at(long_t index) const { + if (m_type->t != type_t::List) throw bad_thing_access(); + auto& list = get(); + if (index < 0 || index >= list.size) throw std::out_of_range("index out of range"); + thing res = { m_type->list, m_allocator }; + res.get() = list.data; // TODO: Account for padding, alignment and stuff + return std::move(res); + } +private: + static void copy_list(const type_p& innerType, list_t& dst, const list_t& src) { + dst.size = src.size; + if (dst.size <= 0) { + dst.data = nullptr; + return; + } + if (innerType == nullptr) throw std::runtime_error("inner type should not be null!"); + + std::size_t size = compute_size_na(innerType) * dst.size; + dst.data = new std::byte[size]; + switch (innerType->t) { + case type_t::Primitive: + case type_t::Reference: { + std::memcpy(dst.data, src.data, size); + } break; + case type_t::List: { + for (std::size_t i = 0; i < size; ++i) { + copy_list(innerType->list, + *std::launder(reinterpret_cast(dst.data)), + *std::launder(reinterpret_cast(src.data))); + } + } break; + } + } +private: + static std::size_t compute_size_na(const type_p& type) { + switch (type->t) { + case type_t::Primitive: return type->primitive; + case type_t::Reference: return sizeof(reference_t); + case type_t::List: return sizeof(list_t); + } + + throw std::runtime_error("unreachable"); + } + + // NOTE: Align to 4 bytes + static std::size_t compute_size(const type_p& type) { return (compute_size_na(type) + 3) & ~3; } +private: + thing(const type_p& type, std::byte* data, const allocator_type& allocator = {}) + : m_type(type), m_size(0), m_data(data), m_allocator(allocator) {} private: template thing binary_op(const thing& rhs, const Op& op) const {