+128
-15
@@ -34,7 +34,8 @@ struct thing_type_hash {
|
|||||||
case thing_type::U8:
|
case thing_type::U8:
|
||||||
case thing_type::U16:
|
case thing_type::U16:
|
||||||
case thing_type::U32:
|
case thing_type::U32:
|
||||||
case thing_type::U64: return seed;
|
case thing_type::U64:
|
||||||
|
case thing_type::String: return seed;
|
||||||
case thing_type::Ptr:
|
case thing_type::Ptr:
|
||||||
case thing_type::Ref: return furlang::utility::hash_combine(seed, thing_type_hash{}(*type.value.typeRef));
|
case thing_type::Ref: return furlang::utility::hash_combine(seed, thing_type_hash{}(*type.value.typeRef));
|
||||||
case thing_type::Array:
|
case thing_type::Array:
|
||||||
@@ -93,6 +94,13 @@ class thing final {
|
|||||||
public:
|
public:
|
||||||
using allocator_type = Allocator<std::byte>; /**< Allocator type. */
|
using allocator_type = Allocator<std::byte>; /**< Allocator type. */
|
||||||
public:
|
public:
|
||||||
|
struct string {
|
||||||
|
std::size_t size;
|
||||||
|
u8* data;
|
||||||
|
|
||||||
|
static bool matches(const thing_type& type) { return type.type == thing_type::String; }
|
||||||
|
};
|
||||||
|
|
||||||
struct dynamic_array {
|
struct dynamic_array {
|
||||||
std::size_t size;
|
std::size_t size;
|
||||||
std::byte* data;
|
std::byte* data;
|
||||||
@@ -133,7 +141,11 @@ private:
|
|||||||
public:
|
public:
|
||||||
reference operator*() { return m_thing; }
|
reference operator*() { return m_thing; }
|
||||||
|
|
||||||
value_type operator*() const { return m_thing.clone(); }
|
value_type operator*() const {
|
||||||
|
thing thing = { m_thing.type() };
|
||||||
|
thing.assign(m_thing);
|
||||||
|
return thing;
|
||||||
|
}
|
||||||
|
|
||||||
reference operator[](difference_type n) { return *(*this + n); }
|
reference operator[](difference_type n) { return *(*this + n); }
|
||||||
|
|
||||||
@@ -542,17 +554,30 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void resize(u64 newSize) {
|
void resize(u64 newSize) {
|
||||||
if (!is(thing_type::Array)) throw bad_thing_access();
|
switch (type().type) {
|
||||||
if (type().value.array.size > 0) throw std::runtime_error("cannot resize a static array");
|
case thing_type::Array: {
|
||||||
|
if (type().value.array.size > 0) throw std::runtime_error("cannot resize a static array");
|
||||||
|
|
||||||
auto& array = get<dynamic_array>();
|
auto& array = get<dynamic_array>();
|
||||||
if (newSize < 0 || newSize == array.size) return;
|
if (newSize < 0 || newSize == array.size) return;
|
||||||
std::size_t innerSize = compute_size_na(*type().value.array.type);
|
std::size_t innerSize = compute_size_na(*type().value.array.type);
|
||||||
std::byte* newData = new std::byte[innerSize * newSize];
|
std::byte* newData = new std::byte[innerSize * newSize];
|
||||||
std::memcpy(newData, array.data, innerSize * std::min(static_cast<u64>(array.size), newSize));
|
std::memcpy(newData, array.data, innerSize * std::min(static_cast<u64>(array.size), newSize));
|
||||||
array.size = newSize;
|
array.size = newSize;
|
||||||
delete[] array.data;
|
delete[] array.data;
|
||||||
array.data = newData;
|
array.data = newData;
|
||||||
|
} break;
|
||||||
|
case thing_type::String: {
|
||||||
|
auto& string = get<struct string>();
|
||||||
|
if (newSize < 0 || newSize == string.size) return;
|
||||||
|
u8* newData = new u8[newSize];
|
||||||
|
std::memcpy(newData, string.data, std::min(static_cast<u64>(string.size), newSize));
|
||||||
|
string.size = newSize;
|
||||||
|
delete[] string.data;
|
||||||
|
string.data = newData;
|
||||||
|
} break;
|
||||||
|
default: throw bad_thing_access();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
thing at(u64 index) const {
|
thing at(u64 index) const {
|
||||||
@@ -561,6 +586,13 @@ public:
|
|||||||
ref.m_size = compute_size_na(*(ref.m_type = &inner_type()));
|
ref.m_size = compute_size_na(*(ref.m_type = &inner_type()));
|
||||||
|
|
||||||
switch (type().type) {
|
switch (type().type) {
|
||||||
|
case thing_type::String: {
|
||||||
|
auto& string = get<struct string>();
|
||||||
|
if (index < 0 || index >= string.size) throw std::out_of_range("index out of range");
|
||||||
|
|
||||||
|
ref.m_data = reinterpret_cast<std::byte*>(string.data + index);
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
case thing_type::Array: {
|
case thing_type::Array: {
|
||||||
if (type().value.array.size == 0) {
|
if (type().value.array.size == 0) {
|
||||||
auto& array = get<dynamic_array>();
|
auto& array = get<dynamic_array>();
|
||||||
@@ -598,6 +630,9 @@ public:
|
|||||||
len = std::min(len, length() - begin);
|
len = std::min(len, length() - begin);
|
||||||
|
|
||||||
switch (type().type) {
|
switch (type().type) {
|
||||||
|
case thing_type::String: {
|
||||||
|
data.data = reinterpret_cast<std::byte*>(get<struct string>().data);
|
||||||
|
} break;
|
||||||
case thing_type::Array: {
|
case thing_type::Array: {
|
||||||
data.data = ((type().value.array.size != 0) ? m_data : get<dynamic_array>().data);
|
data.data = ((type().value.array.size != 0) ? m_data : get<dynamic_array>().data);
|
||||||
} break;
|
} break;
|
||||||
@@ -614,6 +649,7 @@ public:
|
|||||||
|
|
||||||
u64 length() const {
|
u64 length() const {
|
||||||
switch (type().type) {
|
switch (type().type) {
|
||||||
|
case thing_type::String: return get<struct string>().size;
|
||||||
case thing_type::Array:
|
case thing_type::Array:
|
||||||
return type().value.array.size == 0 ? get<dynamic_array>().size : type().value.array.size;
|
return type().value.array.size == 0 ? get<dynamic_array>().size : type().value.array.size;
|
||||||
case thing_type::Slice: return get<struct slice>().length;
|
case thing_type::Slice: return get<struct slice>().length;
|
||||||
@@ -644,6 +680,7 @@ public:
|
|||||||
case thing_type::U16:
|
case thing_type::U16:
|
||||||
case thing_type::U32:
|
case thing_type::U32:
|
||||||
case thing_type::U64: std::memcpy(m_data, rhs.m_data, m_size); return;
|
case thing_type::U64: std::memcpy(m_data, rhs.m_data, m_size); return;
|
||||||
|
case thing_type::String:
|
||||||
case thing_type::Ptr:
|
case thing_type::Ptr:
|
||||||
case thing_type::Ref:
|
case thing_type::Ref:
|
||||||
case thing_type::Array:
|
case thing_type::Array:
|
||||||
@@ -652,9 +689,49 @@ public:
|
|||||||
}
|
}
|
||||||
throw std::runtime_error("unreachable");
|
throw std::runtime_error("unreachable");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void assign(const thing& rhs) {
|
||||||
|
if (type() != rhs.type()) throw std::runtime_error("thing type mismatch");
|
||||||
|
// TODO: Move this to another function
|
||||||
|
switch (type().type) {
|
||||||
|
case thing_type::S8:
|
||||||
|
case thing_type::S16:
|
||||||
|
case thing_type::S32:
|
||||||
|
case thing_type::S64:
|
||||||
|
case thing_type::U8:
|
||||||
|
case thing_type::U16:
|
||||||
|
case thing_type::U32:
|
||||||
|
case thing_type::U64: std::memcpy(m_data, rhs.m_data, m_size); return;
|
||||||
|
case thing_type::Ptr:
|
||||||
|
case thing_type::Ref:
|
||||||
|
case thing_type::Array:
|
||||||
|
case thing_type::Slice: throw std::runtime_error("unimplemented");
|
||||||
|
case thing_type::Count: break;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void assign(const T& value) {
|
||||||
|
if constexpr (detail::cassignable_to_thing<T, thing>::value) {
|
||||||
|
detail::thing_traits<T>::assign_to(*this, value);
|
||||||
|
} else {
|
||||||
|
get<T>() = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void assign(T&& value) { // NOLINT
|
||||||
|
if constexpr (detail::massignable_to_thing<T, thing>::value) {
|
||||||
|
detail::thing_traits<T>::assign_to(*this, std::move(value)); // NOLINT
|
||||||
|
} else {
|
||||||
|
get<T>() = std::move(value); // NOLINT
|
||||||
|
}
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
iterator begin() {
|
iterator begin() {
|
||||||
switch (type().type) {
|
switch (type().type) {
|
||||||
|
case thing_type::String: return { this, reinterpret_cast<std::byte*>(get<struct string>().data) };
|
||||||
case thing_type::Array: return { this, (type().value.array.size == 0) ? get<dynamic_array>().data : m_data };
|
case thing_type::Array: return { this, (type().value.array.size == 0) ? get<dynamic_array>().data : m_data };
|
||||||
case thing_type::Slice: return { this, get<struct slice>().data };
|
case thing_type::Slice: return { this, get<struct slice>().data };
|
||||||
default: throw bad_thing_access();
|
default: throw bad_thing_access();
|
||||||
@@ -665,6 +742,7 @@ public:
|
|||||||
|
|
||||||
const_iterator cbegin() const {
|
const_iterator cbegin() const {
|
||||||
switch (type().type) {
|
switch (type().type) {
|
||||||
|
case thing_type::String: return { this, reinterpret_cast<std::byte*>(get<struct string>().data) };
|
||||||
case thing_type::Array: return { this, (type().value.array.size == 0) ? get<dynamic_array>().data : m_data };
|
case thing_type::Array: return { this, (type().value.array.size == 0) ? get<dynamic_array>().data : m_data };
|
||||||
case thing_type::Slice: return { this, get<struct slice>().data };
|
case thing_type::Slice: return { this, get<struct slice>().data };
|
||||||
default: throw bad_thing_access();
|
default: throw bad_thing_access();
|
||||||
@@ -710,6 +788,7 @@ private:
|
|||||||
case thing_type::U16:
|
case thing_type::U16:
|
||||||
case thing_type::U32:
|
case thing_type::U32:
|
||||||
case thing_type::U64:
|
case thing_type::U64:
|
||||||
|
case thing_type::String:
|
||||||
case thing_type::Ptr:
|
case thing_type::Ptr:
|
||||||
case thing_type::Ref:
|
case thing_type::Ref:
|
||||||
case thing_type::Slice: std::memcpy(dst, src, size * elementSize); return;
|
case thing_type::Slice: std::memcpy(dst, src, size * elementSize); return;
|
||||||
@@ -735,6 +814,7 @@ private:
|
|||||||
case thing_type::U16: return sizeof(u16);
|
case thing_type::U16: return sizeof(u16);
|
||||||
case thing_type::U32: return sizeof(u32);
|
case thing_type::U32: return sizeof(u32);
|
||||||
case thing_type::U64: return sizeof(u64);
|
case thing_type::U64: return sizeof(u64);
|
||||||
|
case thing_type::String: return sizeof(string);
|
||||||
case thing_type::Ptr: return sizeof(void*);
|
case thing_type::Ptr: return sizeof(void*);
|
||||||
case thing_type::Array:
|
case thing_type::Array:
|
||||||
return type.value.array.size == 0 ? sizeof(dynamic_array)
|
return type.value.array.size == 0 ? sizeof(dynamic_array)
|
||||||
@@ -855,6 +935,7 @@ private:
|
|||||||
case thing_type::U16: res.get<u16>() = Op{}(cast_to<u16>(), rhs.cast_to<u16>()); return res;
|
case thing_type::U16: res.get<u16>() = Op{}(cast_to<u16>(), rhs.cast_to<u16>()); return res;
|
||||||
case thing_type::U32: res.get<u32>() = Op{}(cast_to<u32>(), rhs.cast_to<u32>()); return res;
|
case thing_type::U32: res.get<u32>() = Op{}(cast_to<u32>(), rhs.cast_to<u32>()); return res;
|
||||||
case thing_type::U64: res.get<u64>() = Op{}(cast_to<u64>(), rhs.cast_to<u64>()); return res;
|
case thing_type::U64: res.get<u64>() = Op{}(cast_to<u64>(), rhs.cast_to<u64>()); return res;
|
||||||
|
case thing_type::String:
|
||||||
case thing_type::Ptr: // TODO: Pointer arithmetics
|
case thing_type::Ptr: // TODO: Pointer arithmetics
|
||||||
case thing_type::Ref:
|
case thing_type::Ref:
|
||||||
case thing_type::Array:
|
case thing_type::Array:
|
||||||
@@ -886,6 +967,10 @@ private:
|
|||||||
|
|
||||||
thing_type& inner_type() const {
|
thing_type& inner_type() const {
|
||||||
switch (type().type) {
|
switch (type().type) {
|
||||||
|
case thing_type::String: {
|
||||||
|
static thing_type s_inner = { thing_type::U8 };
|
||||||
|
return s_inner;
|
||||||
|
}
|
||||||
case thing_type::Array: return *type().value.array.type;
|
case thing_type::Array: return *type().value.array.type;
|
||||||
case thing_type::Slice: return *type().value.slice.type;
|
case thing_type::Slice: return *type().value.slice.type;
|
||||||
default: throw bad_thing_access();
|
default: throw bad_thing_access();
|
||||||
@@ -895,13 +980,41 @@ private:
|
|||||||
// A flag indicating whether the thing instance owns the data, or not.
|
// A flag indicating whether the thing instance owns the data, or not.
|
||||||
bool m_reference = false;
|
bool m_reference = false;
|
||||||
|
|
||||||
thing_type* m_type = nullptr;
|
const thing_type* m_type = nullptr;
|
||||||
std::size_t m_size = 0;
|
std::size_t m_size = 0;
|
||||||
std::byte* m_data = nullptr;
|
std::byte* m_data = nullptr;
|
||||||
|
|
||||||
allocator_type m_allocator;
|
allocator_type m_allocator;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct thing_traits<std::string_view> {
|
||||||
|
template <template <typename...> class Allocator>
|
||||||
|
static void assign_to(thing<Allocator>& thing, const std::string_view& value) {
|
||||||
|
using Thing = furvm::thing<Allocator>;
|
||||||
|
|
||||||
|
auto& string = thing.template get<typename Thing::string>();
|
||||||
|
string.data = new furvm::u8[string.size = value.length()];
|
||||||
|
std::memcpy(string.data, value.data(), value.length());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct thing_traits<std::string> {
|
||||||
|
template <template <typename...> class Allocator>
|
||||||
|
static void assign_to(thing<Allocator>& thing, const std::string& value) {
|
||||||
|
using Thing = furvm::thing<Allocator>;
|
||||||
|
|
||||||
|
auto& string = thing.template get<typename Thing::string>();
|
||||||
|
string.data = new furvm::u8[string.size = value.length()];
|
||||||
|
std::memcpy(string.data, value.data(), value.length());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
} // namespace furvm
|
} // namespace furvm
|
||||||
|
|
||||||
#endif // FURVM_THING_HPP
|
#endif // FURVM_THING_HPP
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ struct thing_type {
|
|||||||
U16,
|
U16,
|
||||||
U32,
|
U32,
|
||||||
U64,
|
U64,
|
||||||
|
String,
|
||||||
Ptr,
|
Ptr,
|
||||||
Ref,
|
Ref,
|
||||||
Array,
|
Array,
|
||||||
@@ -78,7 +79,8 @@ struct thing_type {
|
|||||||
case U8:
|
case U8:
|
||||||
case U16:
|
case U16:
|
||||||
case U32:
|
case U32:
|
||||||
case U64: return true;
|
case U64:
|
||||||
|
case String: return true;
|
||||||
case Ptr:
|
case Ptr:
|
||||||
case Ref: return *value.typeRef == *other.value.typeRef;
|
case Ref: return *value.typeRef == *other.value.typeRef;
|
||||||
case Array: return *value.array.type == *other.value.array.type && value.array.size == other.value.array.size;
|
case Array: return *value.array.type == *other.value.array.type && value.array.size == other.value.array.size;
|
||||||
@@ -100,6 +102,7 @@ struct thing_type {
|
|||||||
case U16:
|
case U16:
|
||||||
case U32:
|
case U32:
|
||||||
case U64: return true;
|
case U64: return true;
|
||||||
|
case String:
|
||||||
case Ptr:
|
case Ptr:
|
||||||
case Ref:
|
case Ref:
|
||||||
case Array:
|
case Array:
|
||||||
@@ -119,6 +122,7 @@ struct thing_type {
|
|||||||
case thing_type::U16: return sizeof(u16);
|
case thing_type::U16: return sizeof(u16);
|
||||||
case thing_type::U32: return sizeof(u32);
|
case thing_type::U32: return sizeof(u32);
|
||||||
case thing_type::U64: return sizeof(u64);
|
case thing_type::U64: return sizeof(u64);
|
||||||
|
case thing_type::String:
|
||||||
case Ptr:
|
case Ptr:
|
||||||
case Ref:
|
case Ref:
|
||||||
case Array:
|
case Array:
|
||||||
@@ -197,6 +201,24 @@ struct thing_traits<Inner*> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Thing, typename = void>
|
||||||
|
struct cassignable_to_thing : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T, typename Thing>
|
||||||
|
struct cassignable_to_thing<T,
|
||||||
|
Thing,
|
||||||
|
std::void_t<decltype(std::declval<thing_traits<T>>().assign_to(std::declval<Thing&>(), std::declval<const T&>()))>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
template <typename T, typename Thing, typename = void>
|
||||||
|
struct massignable_to_thing : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T, typename Thing>
|
||||||
|
struct massignable_to_thing<T,
|
||||||
|
Thing,
|
||||||
|
std::void_t<decltype(std::declval<thing_traits<T>>().assign_to(std::declval<Thing&>(), std::declval<T&&>()))>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
} // namespace furvm
|
} // namespace furvm
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ thing_type executor::thing_type_impl(mod_h mod, mod_type type) const {
|
|||||||
case thing_type::U8:
|
case thing_type::U8:
|
||||||
case thing_type::U16:
|
case thing_type::U16:
|
||||||
case thing_type::U32:
|
case thing_type::U32:
|
||||||
case thing_type::U64: return { static_cast<enum thing_type::type>(type.type) };
|
case thing_type::U64:
|
||||||
|
case thing_type::String: return { static_cast<enum thing_type::type>(type.type) };
|
||||||
case thing_type::Ptr: return { thing_type::Ptr, mod_to_thing_type(mod, *mod->type_at(type.value.typeRef)) };
|
case thing_type::Ptr: return { thing_type::Ptr, mod_to_thing_type(mod, *mod->type_at(type.value.typeRef)) };
|
||||||
case thing_type::Ref: return { thing_type::Ref, mod_to_thing_type(mod, *mod->type_at(type.value.typeRef)) };
|
case thing_type::Ref: return { thing_type::Ref, mod_to_thing_type(mod, *mod->type_at(type.value.typeRef)) };
|
||||||
case thing_type::Array: {
|
case thing_type::Array: {
|
||||||
@@ -60,7 +61,8 @@ bool executor::compare_thing_types(const thing_type& lhs, const thing_type& rhs)
|
|||||||
case thing_type::U8:
|
case thing_type::U8:
|
||||||
case thing_type::U16:
|
case thing_type::U16:
|
||||||
case thing_type::U32:
|
case thing_type::U32:
|
||||||
case thing_type::U64: return true;
|
case thing_type::U64:
|
||||||
|
case thing_type::String: return true;
|
||||||
case thing_type::Ptr:
|
case thing_type::Ptr:
|
||||||
case thing_type::Ref: return compare_thing_types(*lhs.value.typeRef, *rhs.value.typeRef);
|
case thing_type::Ref: return compare_thing_types(*lhs.value.typeRef, *rhs.value.typeRef);
|
||||||
case thing_type::Array:
|
case thing_type::Array:
|
||||||
|
|||||||
@@ -55,6 +55,21 @@ TEST(ThingOps, Slice) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ThingOps, String) {
|
||||||
|
static constexpr std::string_view STRING = "femboje na topie";
|
||||||
|
static constexpr auto LENGTH = STRING.length();
|
||||||
|
|
||||||
|
furvm::thing string{ furvm::thing_type{ furvm::thing_type::String } };
|
||||||
|
string.assign(STRING);
|
||||||
|
EXPECT_EQ(string.length(), LENGTH);
|
||||||
|
furvm::thing slice = string.slice(0, LENGTH);
|
||||||
|
EXPECT_EQ(slice.length(), LENGTH);
|
||||||
|
for (std::size_t i = 0; i < LENGTH; ++i) {
|
||||||
|
auto el = slice.at(i);
|
||||||
|
EXPECT_EQ(slice.at(i).get<furvm::u8>(), STRING[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ThingOps, Iterators) {
|
TEST(ThingOps, Iterators) {
|
||||||
furvm::thing_type innerType = { furvm::thing_type::U32 };
|
furvm::thing_type innerType = { furvm::thing_type::U32 };
|
||||||
|
|
||||||
@@ -85,6 +100,9 @@ TEST(ThingOps, Access) {
|
|||||||
EXPECT_THROW(thing.get<furvm::s32>(), furvm::bad_thing_access);
|
EXPECT_THROW(thing.get<furvm::s32>(), furvm::bad_thing_access);
|
||||||
EXPECT_THROW(thing.get<void*>(), furvm::bad_thing_access);
|
EXPECT_THROW(thing.get<void*>(), furvm::bad_thing_access);
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(thing.assign<furvm::u8>(67));
|
||||||
|
EXPECT_THROW(thing.assign<furvm::s32>(1337), furvm::bad_thing_access);
|
||||||
|
EXPECT_EQ(thing.get<furvm::u8>(), 67);
|
||||||
EXPECT_NO_THROW(thing.set<furvm::u8>(67)); // He talkin' 'bout sum 6-7 while I want sixty ni-
|
EXPECT_NO_THROW(thing.set<furvm::u8>(67)); // He talkin' 'bout sum 6-7 while I want sixty ni-
|
||||||
EXPECT_THROW(thing.set<furvm::s32>(1337), furvm::bad_thing_access);
|
EXPECT_THROW(thing.set<furvm::s32>(1337), furvm::bad_thing_access);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user