diff --git a/furvm/include/furvm/thing.hpp b/furvm/include/furvm/thing.hpp index e5db73b..de1bd4b 100644 --- a/furvm/include/furvm/thing.hpp +++ b/furvm/include/furvm/thing.hpp @@ -34,7 +34,8 @@ struct thing_type_hash { case thing_type::U8: case thing_type::U16: 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::Ref: return furlang::utility::hash_combine(seed, thing_type_hash{}(*type.value.typeRef)); case thing_type::Array: @@ -93,6 +94,13 @@ class thing final { public: using allocator_type = Allocator; /**< Allocator type. */ 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 { std::size_t size; std::byte* data; @@ -133,7 +141,11 @@ private: public: 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); } @@ -542,17 +554,30 @@ public: } void resize(u64 newSize) { - if (!is(thing_type::Array)) throw bad_thing_access(); - if (type().value.array.size > 0) throw std::runtime_error("cannot resize a static array"); + switch (type().type) { + case thing_type::Array: { + if (type().value.array.size > 0) throw std::runtime_error("cannot resize a static array"); - auto& array = get(); - if (newSize < 0 || newSize == array.size) return; - std::size_t innerSize = compute_size_na(*type().value.array.type); - std::byte* newData = new std::byte[innerSize * newSize]; - std::memcpy(newData, array.data, innerSize * std::min(static_cast(array.size), newSize)); - array.size = newSize; - delete[] array.data; - array.data = newData; + auto& array = get(); + if (newSize < 0 || newSize == array.size) return; + std::size_t innerSize = compute_size_na(*type().value.array.type); + std::byte* newData = new std::byte[innerSize * newSize]; + std::memcpy(newData, array.data, innerSize * std::min(static_cast(array.size), newSize)); + array.size = newSize; + delete[] array.data; + array.data = newData; + } break; + case thing_type::String: { + auto& string = get(); + if (newSize < 0 || newSize == string.size) return; + u8* newData = new u8[newSize]; + std::memcpy(newData, string.data, std::min(static_cast(string.size), newSize)); + string.size = newSize; + delete[] string.data; + string.data = newData; + } break; + default: throw bad_thing_access(); + } } thing at(u64 index) const { @@ -561,6 +586,13 @@ public: ref.m_size = compute_size_na(*(ref.m_type = &inner_type())); switch (type().type) { + case thing_type::String: { + auto& string = get(); + if (index < 0 || index >= string.size) throw std::out_of_range("index out of range"); + + ref.m_data = reinterpret_cast(string.data + index); + return ref; + } case thing_type::Array: { if (type().value.array.size == 0) { auto& array = get(); @@ -598,6 +630,9 @@ public: len = std::min(len, length() - begin); switch (type().type) { + case thing_type::String: { + data.data = reinterpret_cast(get().data); + } break; case thing_type::Array: { data.data = ((type().value.array.size != 0) ? m_data : get().data); } break; @@ -614,6 +649,7 @@ public: u64 length() const { switch (type().type) { + case thing_type::String: return get().size; case thing_type::Array: return type().value.array.size == 0 ? get().size : type().value.array.size; case thing_type::Slice: return get().length; @@ -644,6 +680,7 @@ public: 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::String: case thing_type::Ptr: case thing_type::Ref: case thing_type::Array: @@ -652,9 +689,49 @@ public: } 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 + void assign(const T& value) { + if constexpr (detail::cassignable_to_thing::value) { + detail::thing_traits::assign_to(*this, value); + } else { + get() = value; + } + } + + template + void assign(T&& value) { // NOLINT + if constexpr (detail::massignable_to_thing::value) { + detail::thing_traits::assign_to(*this, std::move(value)); // NOLINT + } else { + get() = std::move(value); // NOLINT + } + } public: iterator begin() { switch (type().type) { + case thing_type::String: return { this, reinterpret_cast(get().data) }; case thing_type::Array: return { this, (type().value.array.size == 0) ? get().data : m_data }; case thing_type::Slice: return { this, get().data }; default: throw bad_thing_access(); @@ -665,6 +742,7 @@ public: const_iterator cbegin() const { switch (type().type) { + case thing_type::String: return { this, reinterpret_cast(get().data) }; case thing_type::Array: return { this, (type().value.array.size == 0) ? get().data : m_data }; case thing_type::Slice: return { this, get().data }; default: throw bad_thing_access(); @@ -710,6 +788,7 @@ private: case thing_type::U16: case thing_type::U32: case thing_type::U64: + case thing_type::String: case thing_type::Ptr: case thing_type::Ref: 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::U32: return sizeof(u32); case thing_type::U64: return sizeof(u64); + case thing_type::String: return sizeof(string); case thing_type::Ptr: return sizeof(void*); case thing_type::Array: return type.value.array.size == 0 ? sizeof(dynamic_array) @@ -855,6 +935,7 @@ private: case thing_type::U16: res.get() = Op{}(cast_to(), rhs.cast_to()); return res; case thing_type::U32: res.get() = Op{}(cast_to(), rhs.cast_to()); return res; case thing_type::U64: res.get() = Op{}(cast_to(), rhs.cast_to()); return res; + case thing_type::String: case thing_type::Ptr: // TODO: Pointer arithmetics case thing_type::Ref: case thing_type::Array: @@ -886,6 +967,10 @@ private: thing_type& inner_type() const { 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::Slice: return *type().value.slice.type; default: throw bad_thing_access(); @@ -895,13 +980,41 @@ private: // A flag indicating whether the thing instance owns the data, or not. bool m_reference = false; - thing_type* m_type = nullptr; - std::size_t m_size = 0; - std::byte* m_data = nullptr; + const thing_type* m_type = nullptr; + std::size_t m_size = 0; + std::byte* m_data = nullptr; allocator_type m_allocator; }; +namespace detail { + +template <> +struct thing_traits { + template