65e5fdf324
Refs: #12
247 lines
6.5 KiB
C++
247 lines
6.5 KiB
C++
#ifndef FURVM_THING_HPP
|
|
#define FURVM_THING_HPP
|
|
|
|
#include "furlang/arena.hpp"
|
|
#include "furvm/exceptions.hpp"
|
|
#include "furvm/fwd.hpp"
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <memory>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace furvm {
|
|
|
|
enum class thing_t : std::uint8_t {
|
|
Int32,
|
|
};
|
|
|
|
/**
|
|
* @brief Returns data size of a thing.
|
|
*
|
|
* @param type Type of the thing.
|
|
* @return The data size of the thing.
|
|
*/
|
|
static inline std::size_t thing_type_size(thing_t type) {
|
|
switch (type) {
|
|
case thing_t::Int32: return sizeof(std::int32_t);
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
template <template <typename> typename Allocator>
|
|
class thing final {
|
|
friend class executor;
|
|
public:
|
|
using allocator_type = Allocator<std::byte>;
|
|
public:
|
|
thing(thing_t type, const allocator_type& allocator = {})
|
|
: m_type(type), m_allocator(allocator) {
|
|
m_data = m_allocator.allocate(thing_type_size(type));
|
|
}
|
|
|
|
~thing() {
|
|
if (m_data != nullptr) m_allocator.deallocate(m_data, thing_type_size(thing_t::Int32));
|
|
}
|
|
|
|
/**
|
|
* @brief Move constructor.
|
|
*/
|
|
thing(thing&& other) noexcept
|
|
: m_type(other.m_type), m_data(other.m_data), m_allocator(std::move(other.m_allocator)) {
|
|
other.m_type = {};
|
|
other.m_data = nullptr;
|
|
}
|
|
|
|
/**
|
|
* @brief Move constructor.
|
|
*/
|
|
thing& operator=(thing&& other) noexcept {
|
|
if (this == &other) return *this;
|
|
m_type = other.m_type;
|
|
m_data = other.m_data;
|
|
m_allocator = std::move(other.m_allocator);
|
|
other.m_type = {};
|
|
other.m_data = nullptr;
|
|
return *this;
|
|
}
|
|
|
|
thing(const thing&) = delete;
|
|
thing& operator=(const thing&) = delete;
|
|
public:
|
|
/**
|
|
* @brief Returns a clone of the thing.
|
|
*
|
|
* @param thing Thing to clone.
|
|
* @return Shared pointer to a clone of the thing.
|
|
*/
|
|
thing clone() const {
|
|
class thing res(m_type, m_allocator);
|
|
switch (m_type) {
|
|
default: {
|
|
std::memcpy(res.m_data, m_data, thing_type_size(m_type));
|
|
} break;
|
|
}
|
|
return std::move(res);
|
|
}
|
|
public:
|
|
/**
|
|
* @brief Returns an int32 value from this thing.
|
|
*
|
|
* @return The value.
|
|
*/
|
|
std::int32_t& int32() {
|
|
if (m_type != thing_t::Int32) throw bad_thing_access();
|
|
return *reinterpret_cast<std::int32_t*>(m_data);
|
|
}
|
|
|
|
/**
|
|
* @brief Returns an int32 value from this thing.
|
|
*
|
|
* @return The value.
|
|
*/
|
|
const std::int32_t& int32() const {
|
|
if (m_type != thing_t::Int32) throw bad_thing_access();
|
|
return *reinterpret_cast<std::int32_t*>(m_data);
|
|
}
|
|
public:
|
|
thing add(const thing& rhs) const {
|
|
thing res(m_type, m_allocator);
|
|
res.int32() = int32() + rhs.int32();
|
|
return res;
|
|
}
|
|
|
|
thing sub(const thing& rhs) const {
|
|
thing res(m_type, m_allocator);
|
|
res.int32() = int32() - rhs.int32();
|
|
return res;
|
|
}
|
|
|
|
thing mul(const thing& rhs) const {
|
|
thing res(m_type, m_allocator);
|
|
res.int32() = int32() * rhs.int32();
|
|
return res;
|
|
}
|
|
|
|
thing div(const thing& rhs) const {
|
|
thing res(m_type, m_allocator);
|
|
res.int32() = int32() / rhs.int32();
|
|
return res;
|
|
}
|
|
|
|
thing mod(const thing& rhs) const {
|
|
thing res(m_type, m_allocator);
|
|
res.int32() = int32() % rhs.int32();
|
|
return res;
|
|
}
|
|
|
|
thing equals(const thing& rhs) const {
|
|
thing res(m_type, m_allocator);
|
|
res.int32() = int32() == rhs.int32();
|
|
return res;
|
|
}
|
|
|
|
thing not_equals(const thing& rhs) const {
|
|
thing res(m_type, m_allocator);
|
|
res.int32() = int32() != rhs.int32();
|
|
return res;
|
|
}
|
|
|
|
thing less_than(const thing& rhs) const {
|
|
thing res(m_type, m_allocator);
|
|
res.int32() = int32() < rhs.int32();
|
|
return res;
|
|
}
|
|
|
|
thing greater_than(const thing& rhs) const {
|
|
thing res(m_type, m_allocator);
|
|
res.int32() = int32() > rhs.int32();
|
|
return res;
|
|
}
|
|
|
|
thing less_equals(const thing& rhs) const {
|
|
thing res(m_type, m_allocator);
|
|
res.int32() = int32() <= rhs.int32();
|
|
return res;
|
|
}
|
|
|
|
thing greater_equals(const thing& rhs) const {
|
|
thing res(m_type, m_allocator);
|
|
res.int32() = int32() >= rhs.int32();
|
|
return res;
|
|
}
|
|
private:
|
|
thing_t m_type{};
|
|
std::byte* m_data;
|
|
|
|
allocator_type m_allocator;
|
|
};
|
|
|
|
template <typename T>
|
|
class thing_allocator {
|
|
template <typename>
|
|
friend class thing_allocator;
|
|
|
|
using dead_things = std::vector<std::pair<T*, std::size_t>>;
|
|
public:
|
|
using value_type = T;
|
|
public:
|
|
explicit thing_allocator(furlang::arena& arena) noexcept
|
|
: m_arena(&arena), m_deadThings(std::make_shared<dead_things>()) {}
|
|
|
|
template <typename U>
|
|
thing_allocator(const thing_allocator<U>& other) noexcept
|
|
: m_arena(other.m_arena), m_deadThings(other.m_deadThings) {}
|
|
|
|
template <typename U>
|
|
thing_allocator& operator=(const thing_allocator<U>& other) noexcept {
|
|
if (this == &other) return *this;
|
|
m_arena = other.m_arena;
|
|
m_deadThings = other.m_deadThings;
|
|
return *this;
|
|
}
|
|
|
|
template <typename U>
|
|
thing_allocator(thing_allocator<U>&& other) noexcept
|
|
: m_arena(std::move(other.m_arena)), m_deadThings(std::move(other.m_deadThings)) {}
|
|
|
|
template <typename U>
|
|
thing_allocator& operator=(thing_allocator<U>&& other) noexcept {
|
|
if (this == &other) return *this;
|
|
m_arena = std::move(other.m_arena);
|
|
m_deadThings = std::move(other.m_deadThings);
|
|
return *this;
|
|
}
|
|
public:
|
|
[[nodiscard]] T* allocate(std::size_t count = 1) {
|
|
for (auto it = m_deadThings->begin(); it != m_deadThings->end(); ++it) {
|
|
if (it->second != count) continue;
|
|
m_deadThings->erase(it);
|
|
return it->first;
|
|
}
|
|
return m_arena->allocate<T>(count);
|
|
}
|
|
|
|
void deallocate(T* ptr, std::size_t count) noexcept { m_deadThings->emplace_back(ptr, count); }
|
|
public:
|
|
template <typename U>
|
|
bool operator==(const thing_allocator<U>& other) const noexcept {
|
|
return m_arena == other.m_arena && m_deadThings == other.m_deadThings;
|
|
}
|
|
|
|
template <typename U>
|
|
bool operator!=(const thing_allocator<U>& other) const noexcept {
|
|
return m_arena != other.m_arena || m_deadThings != other.m_deadThings;
|
|
}
|
|
private:
|
|
furlang::arena* m_arena;
|
|
|
|
std::shared_ptr<dead_things> m_deadThings;
|
|
};
|
|
|
|
} // namespace furvm
|
|
|
|
#endif // FURVM_THING_HPP
|