#pragma once #include "value.hpp" #include #include #include #include #include #include #include namespace sliger::heap { struct Array { std::vector values; auto at(int32_t index) & -> Value&; }; struct Struct { std::unordered_map fields; }; enum class AllocType { Value, Array, Struct, }; // clang-format off template struct AllocTypeType {}; template <> struct AllocTypeType { using Type = Value; }; template <> struct AllocTypeType { using Type = Array; }; template <> struct AllocTypeType { using Type = Struct; }; // clang-format on struct AllocItem { AllocItem(Value&& val) : type(AllocType::Value) , value(val) { } AllocItem(Array&& val) : type(AllocType::Array) , value(val) { } AllocItem(Struct&& val) : type(AllocType::Struct) , value(val) { } template inline auto as() & -> AllocTypeType::Type& { return std::get::Type>(this->value); } template inline auto as() const& -> const AllocTypeType::Type& { return std::get::Type>(this->value); } template inline auto as() && -> AllocTypeType::Type&& { return std::move( std::get::Type>(this->value)); } template inline auto as() const&& -> const AllocTypeType::Type&& { return std::move( std::get::Type>(this->value)); } inline auto as_value() & -> Value& { return std::get(this->value); } inline auto as_value() const& -> const Value& { return std::get(this->value); } inline auto as_array() & -> Array& { return std::get(this->value); } inline auto as_array() const& -> const Array& { return std::get(this->value); } inline auto as_struct() & -> Struct& { return std::get(this->value); } inline auto as_struct() const& -> const Struct& { return std::get(this->value); } AllocType type; std::variant value; }; enum class ErrType { InvalidPtrAccess, CannotAllocate, }; template struct Res { Res(T val) : m_val(std::forward(val)) { } Res(ErrType err) : m_err(err) { } 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 m_val; std::optional m_err; }; class Heap { public: inline auto can_allocate() const -> bool { return this->sel->size() < this->max_size; } inline void collect(const std::vector& 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 { if (ptr >= this->sel->size()) { return ErrType::InvalidPtrAccess; } return &this->sel->at(ptr); } template auto alloc() -> Res { if (not can_allocate()) { return ErrType::CannotAllocate; } auto ptr = static_cast(this->sel->size()); this->sel->push_back(typename AllocTypeType::Type {}); return ptr; } private: inline void move_item_to_other(uint32_t ptr) { auto res = at(ptr); if (!res.ok()) return; auto val = res.val(); switch (val->type) { case AllocType::Value: { auto& v = val->as(); if (v.type() == ValueType::Ptr) { move_item_to_other(v.template as().value); } break; } case AllocType::Array: { auto& vs = val->as(); for (auto& v : vs.values) { if (v.type() == ValueType::Ptr) { move_item_to_other( v.template as().value); } } break; } case AllocType::Struct: { auto& vs = val->as(); for (auto& [key, v] : vs.fields) { if (v.type() == ValueType::Ptr) { move_item_to_other( v.template as().value); } } break; } } } size_t max_size = 8; std::vector heap_1; std::vector heap_2; std::vector* sel = &heap_1; std::vector* other = &heap_2; }; }