@@ -5,6 +5,7 @@
|
|||||||
#include "furvm/exceptions.hpp"
|
#include "furvm/exceptions.hpp"
|
||||||
#include "furvm/fwd.hpp"
|
#include "furvm/fwd.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -20,26 +21,42 @@ namespace furvm {
|
|||||||
enum class type_t : std::uint32_t {
|
enum class type_t : std::uint32_t {
|
||||||
Primitive = 0,
|
Primitive = 0,
|
||||||
Reference,
|
Reference,
|
||||||
|
List,
|
||||||
};
|
};
|
||||||
|
|
||||||
using primitive_type = std::uint64_t;
|
using primitive_type = std::uint64_t;
|
||||||
using reference_type = std::shared_ptr<type>;
|
using reference_type = std::shared_ptr<type>;
|
||||||
|
using list_type = std::shared_ptr<type>;
|
||||||
|
|
||||||
struct type {
|
struct type {
|
||||||
type_t t;
|
type_t t;
|
||||||
union {
|
union {
|
||||||
primitive_type primitive;
|
primitive_type primitive;
|
||||||
reference_type reference;
|
reference_type reference;
|
||||||
|
list_type list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type(type_t type)
|
||||||
|
: t(type), primitive(0) {}
|
||||||
|
|
||||||
type(primitive_type primitive)
|
type(primitive_type primitive)
|
||||||
: t(type_t::Primitive), primitive(primitive) {}
|
: t(type_t::Primitive), primitive(primitive) {}
|
||||||
|
|
||||||
type(const reference_type& reference)
|
type(const reference_type& reference)
|
||||||
: t(type_t::Reference), reference(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() {
|
~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
|
type(type&& other) noexcept
|
||||||
@@ -47,6 +64,7 @@ struct type {
|
|||||||
switch (t) {
|
switch (t) {
|
||||||
case type_t::Primitive: primitive = other.primitive; break;
|
case type_t::Primitive: primitive = other.primitive; break;
|
||||||
case type_t::Reference: reference = std::move(other.reference); 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) {
|
switch (t) {
|
||||||
case type_t::Primitive: primitive = other.primitive; break;
|
case type_t::Primitive: primitive = other.primitive; break;
|
||||||
case type_t::Reference: reference = std::move(other.reference); break;
|
case type_t::Reference: reference = std::move(other.reference); break;
|
||||||
|
case type_t::List: list = std::move(other.list); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
@@ -66,6 +85,7 @@ struct type {
|
|||||||
switch (t) {
|
switch (t) {
|
||||||
case type_t::Primitive: primitive = other.primitive; break;
|
case type_t::Primitive: primitive = other.primitive; break;
|
||||||
case type_t::Reference: reference = other.reference; 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) {
|
switch (t) {
|
||||||
case type_t::Primitive: primitive = other.primitive; break;
|
case type_t::Primitive: primitive = other.primitive; break;
|
||||||
case type_t::Reference: reference = other.reference; break;
|
case type_t::Reference: reference = other.reference; break;
|
||||||
|
case type_t::List: list = other.list; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
@@ -88,6 +109,11 @@ using long_t = std::int64_t; /**< An 8-byte integer. */
|
|||||||
|
|
||||||
using reference_t = std::byte*;
|
using reference_t = std::byte*;
|
||||||
|
|
||||||
|
struct list_t {
|
||||||
|
long_t size;
|
||||||
|
std::byte* data;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A 1-byte integer thing type.
|
* @brief A 1-byte integer thing type.
|
||||||
*/
|
*/
|
||||||
@@ -133,13 +159,14 @@ public:
|
|||||||
: m_type(type), m_size(compute_size(type)), m_allocator(allocator) {
|
: m_type(type), m_size(compute_size(type)), m_allocator(allocator) {
|
||||||
// TODO: Account for alignment
|
// TODO: Account for alignment
|
||||||
m_data = m_allocator.allocate(m_size);
|
m_data = m_allocator.allocate(m_size);
|
||||||
|
std::memset(m_data, 0, m_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destructs a thing.
|
* @brief Destructs a thing.
|
||||||
*/
|
*/
|
||||||
~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: {
|
case type_t::Reference: {
|
||||||
std::memcpy(res.m_data, m_data, m_size);
|
std::memcpy(res.m_data, m_data, m_size);
|
||||||
} break;
|
} break;
|
||||||
|
case type_t::List: {
|
||||||
|
copy_list(m_type->list, res.get<list_t>(), get<list_t>());
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
return std::move(res);
|
return std::move(res);
|
||||||
}
|
}
|
||||||
@@ -216,7 +246,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T& get() {
|
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));
|
return *std::launder(reinterpret_cast<T*>(m_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +258,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const T& get() const {
|
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));
|
return *std::launder(reinterpret_cast<const T*>(m_data));
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
@@ -336,22 +368,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
thing reference() const {
|
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;
|
res.get<reference_t>() = m_data;
|
||||||
return std::move(res);
|
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 resolve() const {
|
||||||
thing rsv = { m_type, m_data, m_allocator };
|
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) };
|
rsv = { rsv.m_type->reference, rsv.get<reference_t>(), std::move(rsv.m_allocator) };
|
||||||
return rsv;
|
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:
|
private:
|
||||||
template <typename Op>
|
template <typename Op>
|
||||||
thing binary_op(const thing& rhs, const Op& op) const {
|
thing binary_op(const thing& rhs, const Op& op) const {
|
||||||
|
|||||||
Reference in New Issue
Block a user