slige/runtime/alloc.hpp

209 lines
5.8 KiB
C++
Raw Normal View History

2024-11-26 14:32:21 +01:00
#pragma once
#include "value.hpp"
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>
namespace sliger::heap {
struct Array {
std::vector<Value> values;
2024-12-13 16:03:01 +01:00
inline auto at(int32_t index)& -> Value& {
if (index >= static_cast<int32_t>(this->values.size()) || index < 0) {
std::cout << std::format("index not in range, expected to be in range (0..{}), got: {}", this->values.size(), index);
exit(1);
}
return values.at(index);
}
2024-11-26 14:32:21 +01:00
};
struct Struct {
std::unordered_map<std::string, Value> fields;
};
enum class AllocType {
Value,
Array,
Struct,
};
// clang-format off
template <AllocType type> struct AllocTypeType {};
template <> struct AllocTypeType<AllocType::Value> { using Type = Value; };
template <> struct AllocTypeType<AllocType::Array> { using Type = Array; };
template <> struct AllocTypeType<AllocType::Struct> { using Type = Struct; };
// clang-format on
struct AllocItem {
2024-12-12 16:07:59 +01:00
AllocItem(Value&& val) : type(AllocType::Value), value(val) {}
AllocItem(Array&& val) : type(AllocType::Array), value(val) {}
AllocItem(Struct&& val) : type(AllocType::Struct), value(val) {}
2024-11-26 14:32:21 +01:00
template <AllocType AT> inline auto as() & -> AllocTypeType<AT>::Type&
{
return std::get<typename AllocTypeType<AT>::Type>(this->value);
}
template <AllocType AT>
inline auto as() const& -> const AllocTypeType<AT>::Type&
{
return std::get<typename AllocTypeType<AT>::Type>(this->value);
}
template <AllocType AT> inline auto as() && -> AllocTypeType<AT>::Type&&
{
return std::move(
std::get<typename AllocTypeType<AT>::Type>(this->value));
}
template <AllocType AT>
inline auto as() const&& -> const AllocTypeType<AT>::Type&&
{
return std::move(
std::get<typename AllocTypeType<AT>::Type>(this->value));
}
2024-12-12 16:07:59 +01:00
inline auto as_value() & -> Value& {
return std::get<Value>(this->value);
}
inline auto as_value() const & -> const Value& {
return std::get<Value>(this->value);
}
inline auto as_array() & -> Array& {
return std::get<Array>(this->value);
}
inline auto as_array() const & -> const Array& {
return std::get<Array>(this->value);
}
inline auto as_struct() & -> Struct& {
return std::get<Struct>(this->value);
}
inline auto as_struct() const & -> const Struct& {
return std::get<Struct>(this->value);
}
2024-11-26 14:32:21 +01:00
AllocType type;
std::variant<Value, Array, Struct> value;
};
enum class ErrType {
InvalidPtrAccess,
CannotAllocate,
};
template <typename T> struct Res {
Res(T val)
2024-12-12 16:07:59 +01:00
: m_val(std::forward<T>(val))
2024-11-26 14:32:21 +01:00
{
}
Res(ErrType err)
2024-12-12 16:07:59 +01:00
: m_err(err)
2024-11-26 14:32:21 +01:00
{
}
2024-12-12 16:07:59 +01:00
auto ok() const -> bool { return m_val.has_value(); }
auto val() & -> T& { return m_val.value(); }
auto val() const& -> const T& { return m_val.value(); }
auto val() && -> T&& { return std::move(m_val.value()); }
auto val() const&& -> const T&& { return std::move(m_val.value()); }
auto err() -> ErrType { return m_err.value(); }
std::optional<T> m_val;
std::optional<ErrType> m_err;
2024-11-26 14:32:21 +01:00
};
class Heap {
public:
inline auto can_allocate() const -> bool
{
return this->sel->size() < this->max_size;
}
inline void collect(const std::vector<uint32_t>& ptr_stack_values)
{
this->other->reserve(this->max_size);
for (auto ptr : ptr_stack_values) {
move_item_to_other(ptr);
}
this->sel->clear();
std::swap(this->sel, this->other);
if (this->sel->size() + 1 >= this->max_size) {
this->max_size *= 2;
this->sel->reserve(this->max_size);
} else if (this->sel->size() * 2 < this->max_size) {
this->max_size /= 2;
this->sel->shrink_to_fit();
this->sel->reserve(this->max_size);
}
}
inline auto at(uint32_t ptr) -> Res<AllocItem*>
{
if (ptr >= this->sel->size()) {
return ErrType::InvalidPtrAccess;
}
return &this->sel->at(ptr);
}
template <AllocType type> auto alloc() -> Res<uint32_t>
{
if (not can_allocate()) {
return ErrType::CannotAllocate;
}
auto ptr = static_cast<uint32_t>(this->sel->size());
this->sel->push_back(typename AllocTypeType<type>::Type {});
return ptr;
}
private:
inline void move_item_to_other(uint32_t ptr)
{
auto res = at(ptr);
2024-12-12 16:07:59 +01:00
if (!res.ok())
2024-11-26 14:32:21 +01:00
return;
2024-12-12 16:07:59 +01:00
auto val = res.val();
2024-11-26 14:32:21 +01:00
switch (val->type) {
case AllocType::Value: {
auto& v = val->as<AllocType::Value>();
if (v.type() == ValueType::Ptr) {
move_item_to_other(v.template as<ValueType::Ptr>().value);
}
break;
}
case AllocType::Array: {
auto& vs = val->as<AllocType::Array>();
for (auto& v : vs.values) {
if (v.type() == ValueType::Ptr) {
move_item_to_other(
v.template as<ValueType::Ptr>().value);
}
}
break;
}
case AllocType::Struct: {
auto& vs = val->as<AllocType::Struct>();
for (auto& [key, v] : vs.fields) {
if (v.type() == ValueType::Ptr) {
move_item_to_other(
v.template as<ValueType::Ptr>().value);
}
}
break;
}
}
}
size_t max_size = 4;
std::vector<AllocItem> heap_1;
std::vector<AllocItem> heap_2;
std::vector<AllocItem>* sel = &heap_1;
std::vector<AllocItem>* other = &heap_2;
};
}