feat: introduce list type

Closes: #33
This commit is contained in:
2026-07-01 21:39:59 +02:00
parent 3d461048cc
commit 09252c3ec3
+98 -17
View File
@@ -5,6 +5,7 @@
#include "furvm/exceptions.hpp"
#include "furvm/fwd.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
@@ -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<type>;
using list_type = std::shared_ptr<type>;
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<list_t>(), get<list_t>());
} break;
}
return std::move(res);
}
@@ -216,7 +246,8 @@ public:
*/
template <typename T>
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<T*>(m_data));
}
@@ -227,7 +258,8 @@ public:
*/
template <typename T>
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<const T*>(m_data));
}
public:
@@ -336,22 +368,10 @@ public:
}
thing reference() const {
thing res = { std::make_shared<type>(m_type), m_allocator };
thing res = { std::make_shared<type>({ m_type }), m_allocator };
res.get<reference_t>() = 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<reference_t>(), 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<list_t>();
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<list_t>();
if (index < 0 || index >= list.size) throw std::out_of_range("index out of range");
thing res = { m_type->list, m_allocator };
res.get<reference_t>() = 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<list_t*>(dst.data)),
*std::launder(reinterpret_cast<list_t*>(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 <typename Op>
thing binary_op(const thing& rhs, const Op& op) const {