diff --git a/furvm/include/furvm/thing.hpp b/furvm/include/furvm/thing.hpp index 2510048..813dd87 100644 --- a/furvm/include/furvm/thing.hpp +++ b/furvm/include/furvm/thing.hpp @@ -94,16 +94,18 @@ public: * @return A clone of this thing. */ thing clone() const { - thing res(resolve_type(m_type, m_modules), m_modules, m_allocator); - switch (m_type->t) { + auto type = resolve_type(m_type, m_modules); + thing res(type, m_modules, m_allocator); + switch (type->t) { case type_t::Primitive: case type_t::Reference: { std::memcpy(res.m_data, m_data, m_size); } break; case type_t::Array: { - copy_list(resolve_type(*m_type->list, m_modules), res.get(), get()); + copy_list(type, res.get(), get()); } break; - case type_t::Import: throw std::runtime_error("unreachable"); + case type_t::Import: + default: throw std::runtime_error("unreachable"); } return std::move(res); } @@ -279,12 +281,13 @@ public: void resize(long_t newSize) { if (m_type->t != type_t::Array) throw bad_thing_access(); + if (m_type->array.size > 0) throw std::runtime_error("cannot resize a static array"); + auto& list = get(); if (newSize < 0 || newSize == list.size) return; - std::byte* newData = new std::byte[compute_size_na(resolve_type(*m_type->list, m_modules)) * newSize]; - std::memcpy(newData, - list.data, - compute_size_na(resolve_type(*m_type->list, m_modules)) * std::min(list.size, newSize)); + std::size_t innerSize = compute_size_na(*m_type->array.type); + std::byte* newData = new std::byte[innerSize * newSize]; + std::memcpy(newData, list.data, innerSize * std::min(list.size, newSize)); list.size = newSize; delete[] list.data; list.data = newData; @@ -294,7 +297,7 @@ public: if (m_type->t != type_t::Array) 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_modules, m_allocator }; + thing res = { std::make_shared(m_type->array.type), m_modules, m_allocator }; res.get() = list.data; // TODO: Account for padding, alignment and stuff return std::move(res); } @@ -306,24 +309,39 @@ public: return rsv; } private: - static void copy_list(const type_p& innerType, array_t& dst, const array_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!"); + static void copy_list(const type_p& arrayType, array_t& dst, const array_t& src) { + if (arrayType == nullptr || arrayType->t != type_t::Array || *arrayType->array.type == nullptr) + throw std::runtime_error("invalid type"); + + auto innerType = *arrayType->array.type; + std::size_t elementSize = compute_size_na(innerType); + + std::byte* data = nullptr; + const std::byte* srcData = nullptr; + std::size_t size = 0; + if (arrayType->array.size == 0) { + size = dst.dynamic.size = src.dynamic.size; + if (dst.dynamic.size < 0) { + dst.dynamic.data = nullptr; + return; + } + + srcData = src.dynamic.data; + data = dst.dynamic.data = new std::byte[dst.dynamic.size]; + } else { + data = dst.data; + srcData = src.data; + size = arrayType->array.size; + } - 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::Array: 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))); + copy_list(*innerType->array.type, + *std::launder(reinterpret_cast(data + (i * elementSize))), + *std::launder(reinterpret_cast(srcData + (i * elementSize)))); } break; case type_t::Import: throw std::runtime_error("unresolved type"); @@ -334,7 +352,10 @@ private: switch (type->t) { case type_t::Primitive: return type->primitive; case type_t::Reference: return sizeof(reference_t); - case type_t::Array: return sizeof(array_t); + case type_t::Array: { + if (type->array.size == 0) return sizeof(array_t); + return compute_size_na(*type->array.type) * type->array.size; + } case type_t::Import: throw std::runtime_error("unresolved type"); } diff --git a/furvm/include/furvm/type.hpp b/furvm/include/furvm/type.hpp index 3448a6b..c8eb551 100644 --- a/furvm/include/furvm/type.hpp +++ b/furvm/include/furvm/type.hpp @@ -5,6 +5,7 @@ #include "furvm/handle.hpp" // IWYU pragma: keep #include + namespace furvm { enum class type_t : std::uint32_t { @@ -17,7 +18,20 @@ enum class type_t : std::uint32_t { using primitive_type = std::uint64_t; using reference_type = type_p; -using array_type = type_h; + +/** + * @brief Array type. + */ +struct array_type { + type_h type; /**< Type of the array's elements. */ + + /** + * @brief Size of the array. + * + * Size of the array. If size is equal to zero, then the array becomes dynamic. + */ + std::size_t size; +}; struct import_type { mod_id mod; @@ -29,7 +43,7 @@ struct type { union { primitive_type primitive; reference_type reference; - array_type list; + array_type array; import_type imp; }; @@ -42,8 +56,11 @@ struct type { type(const reference_type& reference) : t(type_t::Reference), reference(reference) {} + type(const type_h& type, std::size_t size = 0) + : t(type_t::Array), array(array_type{ type, size }) {} + type(const array_type& list) - : t(type_t::Array), list(list) {} + : t(type_t::Array), array(list) {} type(const import_type& imp) : t(type_t::Import), imp(imp) {} @@ -51,7 +68,7 @@ struct type { ~type() { switch (t) { case type_t::Reference: reference.~reference_type(); break; - case type_t::Array: list.~array_type(); break; + case type_t::Array: array.~array_type(); break; case type_t::Import: imp.~import_type(); break; default: break; } @@ -62,7 +79,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::Array: list = std::move(other.list); break; + case type_t::Array: array = std::move(other.array); break; case type_t::Import: imp = std::move(other.imp); break; } } @@ -73,7 +90,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::Array: list = std::move(other.list); break; + case type_t::Array: array = std::move(other.array); break; case type_t::Import: imp = std::move(other.imp); break; } @@ -85,7 +102,7 @@ struct type { switch (t) { case type_t::Primitive: primitive = other.primitive; break; case type_t::Reference: reference = other.reference; break; - case type_t::Array: list = other.list; break; + case type_t::Array: array = other.array; break; case type_t::Import: imp = other.imp; break; } } @@ -96,7 +113,7 @@ struct type { switch (t) { case type_t::Primitive: primitive = other.primitive; break; case type_t::Reference: reference = other.reference; break; - case type_t::Array: list = other.list; break; + case type_t::Array: array = other.array; break; case type_t::Import: imp = other.imp; break; } @@ -111,9 +128,15 @@ using long_t = std::int64_t; /**< An 8-byte integer. */ using reference_t = std::byte*; -struct array_t { - long_t size; - std::byte* data; +/** + * @brief Array type's data layout. + */ +union array_t { + std::byte data[]; /**< Static array's elements' data. */ + struct { + long_t size; /**< Size of the array (in items). */ + std::byte* data; /**< Pointer to dynamic array's elements' data array. */ + } dynamic; /**< Dynamic array's info. */ }; /** diff --git a/furvm/src/executor.cpp b/furvm/src/executor.cpp index d12cd17..80fda48 100644 --- a/furvm/src/executor.cpp +++ b/furvm/src/executor.cpp @@ -177,11 +177,14 @@ void executor::step() { } break; case instruction_t::Sizeof: { auto thing = pop_thing()->resolve(); - auto ptr = push_thing({ *m_context->at("core")->type_at(3), m_context, m_context->thing_alloc() }); + auto size = push_thing({ *m_context->at("core")->type_at(3), m_context, m_context->thing_alloc() }); auto type = furvm::thing<>::resolve_type(thing.type(), m_context); switch (type->t) { - case type_t::Primitive: ptr->get() = static_cast(type->primitive); break; - case type_t::Array: ptr->get() = thing.get().size; break; + case type_t::Primitive: size->get() = static_cast(type->primitive); break; + case type_t::Array: + size->get() = + (type->array.size == 0) ? thing.get().dynamic.size : static_cast(type->array.size); + break; case type_t::Reference: case type_t::Import: default: throw std::runtime_error("unreachable"); diff --git a/furvm/src/module.cpp b/furvm/src/module.cpp index 8b5adc0..857b31b 100644 --- a/furvm/src/module.cpp +++ b/furvm/src/module.cpp @@ -58,7 +58,10 @@ std::ostream& mod::serialize(std::ostream& os) const { switch (type->t) { case type_t::Primitive: detail::serialize(os, type->primitive); break; case type_t::Reference: throw std::runtime_error("reference type serialization is unimplemented"); - case type_t::Array: detail::serialize(os, type->list.id()); break; + case type_t::Array: { + detail::serialize(os, type->array.size); + detail::serialize(os, type->array.type.id()); + } break; case type_t::Import: { detail::serialize(os, type->imp.mod); detail::serialize(os, type->imp.type); @@ -128,9 +131,11 @@ mod mod::load(std::istream& is) { } break; case type_t::Reference: throw std::runtime_error("reference type serialization is unimplemented"); case type_t::Array: { + std::size_t size = 0; + detail::load(is, size); type_id typeId = 0; detail::load(is, typeId); - mod.emplace_type(id, std::make_shared(mod.type_at(typeId))).dispatch(); + mod.emplace_type(id, std::make_shared(mod.type_at(typeId), size)).dispatch(); } break; case type_t::Import: { std::string modName;