diff --git a/CMakeLists.txt b/CMakeLists.txt index 766b7a6..33a17bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.0.14) +project(blt-gp VERSION 0.0.15) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) option(ENABLE_UBSAN "Enable the ub sanitizer" OFF) diff --git a/examples/main.cpp b/examples/main.cpp index 7d2e0d0..c7a04c0 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -23,367 +23,369 @@ #include #include -// small scale -enum class op -{ - ADD, - SUB, - MUL, - DIV, - LIT -}; - -std::string to_string(op o) -{ - switch (o) - { - case op::ADD: - return "ADD"; - case op::SUB: - return "SUB"; - case op::MUL: - return "MUL"; - case op::DIV: - return "DIV"; - case op::LIT: - return "LIT"; - } - return ""; -} - -constexpr static long SEED = 41912; - -op generate_op() -{ - static std::mt19937_64 engine(SEED); - static std::uniform_int_distribution dist(0, static_cast(op::LIT) - 1); - return static_cast(dist(engine)); -} - -bool choice() -{ - static std::mt19937_64 engine(SEED); - static std::uniform_int_distribution dist(0, 1); - return dist(engine); -} - -float random_value() -{ - static std::mt19937_64 engine(SEED); - static std::uniform_real_distribution dist(0.0f, 10.0f); - return dist(engine); -} - -void test() -{ - std::vector operations; - std::vector values; - - std::stack tree_generator; - tree_generator.push(generate_op()); - - while (!tree_generator.empty()) - { - auto opn = tree_generator.top(); - tree_generator.pop(); - - operations.push_back(opn); - if (opn == op::LIT) - { - values.push_back(random_value()); - continue; - } - - // child 1 - if (choice()) - tree_generator.push(generate_op()); - else - tree_generator.push(op::LIT); - - // child 2 - if (choice()) - tree_generator.push(generate_op()); - else - tree_generator.push(op::LIT); - } - - for (const auto& v : operations) - std::cout << to_string(v) << " "; - std::cout << std::endl; - - { - std::stack process; - for (const auto& v : operations) - { - switch (v) - { - case op::ADD: - case op::SUB: - case op::MUL: - case op::DIV: - process.emplace(2); - std::cout << "("; - break; - case op::LIT: - break; - } - std::cout << to_string(v); - while (!process.empty()) - { - auto top = process.top(); - process.pop(); - if (top == 0) - { - std::cout << ")"; - continue; - } else - { - std::cout << " "; - process.push(top - 1); - break; - } - } - } - while (!process.empty()) - { - auto top = process.top(); - process.pop(); - if (top == 0) - { - std::cout << ") "; - continue; - } else - { - std::cerr << "FUCK YOU\n"; - break; - } - } - std::cout << std::endl; - } - - for (const auto& v : values) - std::cout << v << " "; - std::cout << std::endl; - - { - std::stack process; - blt::size_t index = 0; - for (const auto& v : operations) - { - switch (v) - { - case op::ADD: - case op::SUB: - case op::MUL: - case op::DIV: - process.emplace(2); - std::cout << "("; - std::cout << to_string(v); - break; - case op::LIT: - std::cout << values[index++]; - break; - } - - while (!process.empty()) - { - auto top = process.top(); - process.pop(); - if (top == 0) - { - std::cout << ")"; - continue; - } else - { - std::cout << " "; - process.push(top - 1); - break; - } - } - } - while (!process.empty()) - { - auto top = process.top(); - process.pop(); - if (top == 0) - { - std::cout << ") "; - continue; - } else - { - std::cerr << "FUCK YOU\n"; - break; - } - } - std::cout << std::endl; - } - - std::stack process; - std::stack operators; - - for (const auto& v : operations) - operators.push(v); - - while (!operators.empty()) - { - auto oper = operators.top(); - operators.pop(); - if (oper == op::LIT) - { - process.push(values.back()); - values.pop_back(); - } else - { - auto v1 = process.top(); - process.pop(); - auto v2 = process.top(); - process.pop(); - std::cout << "processing oper " << to_string(oper) << " with values " << v1 << " " << v2 << std::endl; - switch (oper) - { - case op::ADD: - values.push_back(v1 + v2); - operators.push(op::LIT); - break; - case op::SUB: - values.push_back(v1 - v2); - operators.push(op::LIT); - break; - case op::MUL: - values.push_back(v1 * v2); - operators.push(op::LIT); - break; - case op::DIV: - if (v2 == 0) - v2 = 1; - values.push_back(v1 / v2); - operators.push(op::LIT); - break; - case op::LIT: - break; - } - std::cout << "\tresult: " << values.back() << std::endl; - } - } - - std::cout << process.size() << std::endl; - std::cout << process.top() << std::endl; - -} - -struct silly -{ - long nyah = 50; - int bruh = 10; - - friend std::ostream& operator<<(std::ostream& stream, const silly& silly) - { - stream << "[" << silly.nyah << " " << silly.bruh << "]"; - return stream; - } -}; - -struct large -{ - unsigned char data[2048]; -}; - -struct super_large -{ - unsigned char data[9582]; -}; +//// small scale +//enum class op +//{ +// ADD, +// SUB, +// MUL, +// DIV, +// LIT +//}; +// +//std::string to_string(op o) +//{ +// switch (o) +// { +// case op::ADD: +// return "ADD"; +// case op::SUB: +// return "SUB"; +// case op::MUL: +// return "MUL"; +// case op::DIV: +// return "DIV"; +// case op::LIT: +// return "LIT"; +// } +// return ""; +//} +// +//constexpr static long SEED = 41912; +// +//op generate_op() +//{ +// static std::mt19937_64 engine(SEED); +// static std::uniform_int_distribution dist(0, static_cast(op::LIT) - 1); +// return static_cast(dist(engine)); +//} +// +//bool choice() +//{ +// static std::mt19937_64 engine(SEED); +// static std::uniform_int_distribution dist(0, 1); +// return dist(engine); +//} +// +//float random_value() +//{ +// static std::mt19937_64 engine(SEED); +// static std::uniform_real_distribution dist(0.0f, 10.0f); +// return dist(engine); +//} +// +//void test() +//{ +// std::vector operations; +// std::vector values; +// +// std::stack tree_generator; +// tree_generator.push(generate_op()); +// +// while (!tree_generator.empty()) +// { +// auto opn = tree_generator.top(); +// tree_generator.pop(); +// +// operations.push_back(opn); +// if (opn == op::LIT) +// { +// values.push_back(random_value()); +// continue; +// } +// +// // child 1 +// if (choice()) +// tree_generator.push(generate_op()); +// else +// tree_generator.push(op::LIT); +// +// // child 2 +// if (choice()) +// tree_generator.push(generate_op()); +// else +// tree_generator.push(op::LIT); +// } +// +// for (const auto& v : operations) +// std::cout << to_string(v) << " "; +// std::cout << std::endl; +// +// { +// std::stack process; +// for (const auto& v : operations) +// { +// switch (v) +// { +// case op::ADD: +// case op::SUB: +// case op::MUL: +// case op::DIV: +// process.emplace(2); +// std::cout << "("; +// break; +// case op::LIT: +// break; +// } +// std::cout << to_string(v); +// while (!process.empty()) +// { +// auto top = process.top(); +// process.pop(); +// if (top == 0) +// { +// std::cout << ")"; +// continue; +// } else +// { +// std::cout << " "; +// process.push(top - 1); +// break; +// } +// } +// } +// while (!process.empty()) +// { +// auto top = process.top(); +// process.pop(); +// if (top == 0) +// { +// std::cout << ") "; +// continue; +// } else +// { +// std::cerr << "FUCK YOU\n"; +// break; +// } +// } +// std::cout << std::endl; +// } +// +// for (const auto& v : values) +// std::cout << v << " "; +// std::cout << std::endl; +// +// { +// std::stack process; +// blt::size_t index = 0; +// for (const auto& v : operations) +// { +// switch (v) +// { +// case op::ADD: +// case op::SUB: +// case op::MUL: +// case op::DIV: +// process.emplace(2); +// std::cout << "("; +// std::cout << to_string(v); +// break; +// case op::LIT: +// std::cout << values[index++]; +// break; +// } +// +// while (!process.empty()) +// { +// auto top = process.top(); +// process.pop(); +// if (top == 0) +// { +// std::cout << ")"; +// continue; +// } else +// { +// std::cout << " "; +// process.push(top - 1); +// break; +// } +// } +// } +// while (!process.empty()) +// { +// auto top = process.top(); +// process.pop(); +// if (top == 0) +// { +// std::cout << ") "; +// continue; +// } else +// { +// std::cerr << "FUCK YOU\n"; +// break; +// } +// } +// std::cout << std::endl; +// } +// +// std::stack process; +// std::stack operators; +// +// for (const auto& v : operations) +// operators.push(v); +// +// while (!operators.empty()) +// { +// auto oper = operators.top(); +// operators.pop(); +// if (oper == op::LIT) +// { +// process.push(values.back()); +// values.pop_back(); +// } else +// { +// auto v1 = process.top(); +// process.pop(); +// auto v2 = process.top(); +// process.pop(); +// std::cout << "processing oper " << to_string(oper) << " with values " << v1 << " " << v2 << std::endl; +// switch (oper) +// { +// case op::ADD: +// values.push_back(v1 + v2); +// operators.push(op::LIT); +// break; +// case op::SUB: +// values.push_back(v1 - v2); +// operators.push(op::LIT); +// break; +// case op::MUL: +// values.push_back(v1 * v2); +// operators.push(op::LIT); +// break; +// case op::DIV: +// if (v2 == 0) +// v2 = 1; +// values.push_back(v1 / v2); +// operators.push(op::LIT); +// break; +// case op::LIT: +// break; +// } +// std::cout << "\tresult: " << values.back() << std::endl; +// } +// } +// +// std::cout << process.size() << std::endl; +// std::cout << process.top() << std::endl; +// +//} +// +//struct silly +//{ +// long nyah = 50; +// int bruh = 10; +// +// friend std::ostream& operator<<(std::ostream& stream, const silly& silly) +// { +// stream << "[" << silly.nyah << " " << silly.bruh << "]"; +// return stream; +// } +//}; +// +//struct large +//{ +// unsigned char data[2048]; +//}; +// +//struct super_large +//{ +// unsigned char data[9582]; +//}; blt::gp::stack_allocator alloc; +float nyah(float f, int i, bool b) +{ + return f + static_cast(i * b); +} + int main() { - constexpr blt::size_t MAX_ALIGNMENT = 8; - test(); - std::cout << alignof(silly) << " " << sizeof(silly) << std::endl; - std::cout << alignof(super_large) << " " << sizeof(super_large) << " " << ((sizeof(super_large) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) - << std::endl; - std::cout << ((sizeof(char) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) << " " - << ((sizeof(short) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) << std::endl; - std::cout << ((sizeof(int) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) << " " << ((sizeof(long) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) - << std::endl; - std::cout << alignof(void*) << " " << sizeof(void*) << std::endl; - std::cout << blt::type_string() << std::endl; - - alloc.push(50); - alloc.push(550.3f); - alloc.push(20.1230345); - alloc.push(true); - alloc.push(false); - alloc.push(std::string("SillyString")); - alloc.push(&"SillyString"); - - std::cout << std::endl; - std::cout << *alloc.pop() << std::endl; - std::cout << alloc.pop() << std::endl; - std::cout << alloc.pop() << std::endl; - std::cout << alloc.pop() << std::endl; - std::cout << alloc.pop() << std::endl; - std::cout << alloc.pop() << std::endl; - std::cout << alloc.pop() << std::endl; - std::cout << std::endl; - - std::cout << "Is empty? " << alloc.empty() << std::endl; - alloc.push(silly{}); - alloc.push(large{}); - alloc.push(super_large{}); - alloc.push(silly{25, 24}); - alloc.push(large{}); - - std::cout << std::endl; - std::cout << "Is empty? " << alloc.empty() << std::endl; - alloc.pop(); - std::cout << "Is empty? " << alloc.empty() << std::endl; - std::cout << alloc.pop() << std::endl; - std::cout << "Is empty? " << alloc.empty() << std::endl; - alloc.pop(); - std::cout << "Is empty? " << alloc.empty() << std::endl; - alloc.pop(); - std::cout << "Is empty? " << alloc.empty() << std::endl; - std::cout << alloc.pop() << std::endl; - std::cout << std::endl; - - std::cout << "Is empty? " << alloc.empty() << " bytes left: " << alloc.bytes_in_head() << std::endl; - std::cout << std::endl; - - alloc.push(silly{2, 5}); - alloc.push(large{}); - alloc.push(super_large{}); - alloc.push(silly{80, 10}); - alloc.push(large{}); - alloc.push(50); - alloc.push(550.3f); - alloc.push(20.1230345); - alloc.push(std::string("SillyString")); +// constexpr blt::size_t MAX_ALIGNMENT = 8; +// test(); +// std::cout << alignof(silly) << " " << sizeof(silly) << std::endl; +// std::cout << alignof(super_large) << " " << sizeof(super_large) << " " << ((sizeof(super_large) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) +// << std::endl; +// std::cout << ((sizeof(char) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) << " " +// << ((sizeof(short) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) << std::endl; +// std::cout << ((sizeof(int) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) << " " << ((sizeof(long) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) +// << std::endl; +// std::cout << alignof(void*) << " " << sizeof(void*) << std::endl; +// std::cout << blt::type_string() << std::endl; +// +// alloc.push(50); +// alloc.push(550.3f); +// alloc.push(20.1230345); +// alloc.push(true); +// alloc.push(false); +// alloc.push(std::string("SillyString")); +// alloc.push(&"SillyString"); +// +// std::cout << std::endl; +// std::cout << *alloc.pop() << std::endl; +// std::cout << alloc.pop() << std::endl; +// std::cout << alloc.pop() << std::endl; +// std::cout << alloc.pop() << std::endl; +// std::cout << alloc.pop() << std::endl; +// std::cout << alloc.pop() << std::endl; +// std::cout << alloc.pop() << std::endl; +// std::cout << std::endl; +// +// std::cout << "Is empty? " << alloc.empty() << std::endl; +// alloc.push(silly{}); +// alloc.push(large{}); +// alloc.push(super_large{}); +// alloc.push(silly{25, 24}); +// alloc.push(large{}); +// +// std::cout << std::endl; +// std::cout << "Is empty? " << alloc.empty() << std::endl; +// alloc.pop(); +// std::cout << "Is empty? " << alloc.empty() << std::endl; +// std::cout << alloc.pop() << std::endl; +// std::cout << "Is empty? " << alloc.empty() << std::endl; +// alloc.pop(); +// std::cout << "Is empty? " << alloc.empty() << std::endl; +// alloc.pop(); +// std::cout << "Is empty? " << alloc.empty() << std::endl; +// std::cout << alloc.pop() << std::endl; +// std::cout << std::endl; +// +// std::cout << "Is empty? " << alloc.empty() << " bytes left: " << alloc.bytes_in_head() << std::endl; +// std::cout << std::endl; +// +// alloc.push(silly{2, 5}); +// alloc.push(large{}); +// alloc.push(super_large{}); +// alloc.push(silly{80, 10}); +// alloc.push(large{}); +// alloc.push(50); +// alloc.push(550.3f); +// alloc.push(20.1230345); +// alloc.push(std::string("SillyString")); alloc.push(33.22f); alloc.push(120); alloc.push(true); + + blt::gp::operation silly_op(nyah); + + std::cout << silly_op(alloc) << std::endl; - blt::gp::operation silly_op([](float f, int i, bool b) -> float { - std::cout << "We found values: " << f << " " << i << " " << b << std::endl; - return f + static_cast(i * b); - }); +// std::cout << std::endl; +// +// //auto* pointer = static_cast(head->metadata.offset); +// //return std::align(alignment, bytes, pointer, remaining_bytes); +// +// float f = 10.5; +// int i = 412; +// bool b = true; +// +// std::array arr{reinterpret_cast(&f), reinterpret_cast(&i), reinterpret_cast(&b)}; +// +// blt::span spv{arr}; +// +// std::cout << silly_op.operator()(spv) << std::endl; - std::cout << alloc.run(silly_op) << std::endl; - - std::cout << std::endl; - - //auto* pointer = static_cast(head->metadata.offset); - //return std::align(alignment, bytes, pointer, remaining_bytes); - - float f = 10.5; - int i = 412; - bool b = true; - - std::array arr{reinterpret_cast(&f), reinterpret_cast(&i), reinterpret_cast(&b)}; - - blt::span spv{arr}; - - std::cout << silly_op.operator()(spv) << std::endl; - - std::cout << "Hello World!" << std::endl; + //std::cout << "Hello World!" << std::endl; } \ No newline at end of file diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index 68fb72f..4acd265 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -19,6 +19,7 @@ #ifndef BLT_GP_PROGRAM_H #define BLT_GP_PROGRAM_H +#include #include #include #include @@ -96,292 +97,54 @@ namespace blt::gp operation(operation&& move) = default; - template, void>> - explicit operation(const T& functor): func(functor) + explicit operation(const function_t& functor): func(functor) {} - template, void>> - explicit operation(const T& functor) - { - func = [&functor](Args... args) { - return functor(args...); - }; - } - explicit operation(function_t&& functor): func(std::move(functor)) {} - [[nodiscard]] inline Return operator()(Args... args) const - { - return func(args...); - } - - Return operator()(blt::span args) - { - auto pack_sequence = std::make_integer_sequence(); - return function_evaluator(args, pack_sequence); - } - - std::function)> to_functor() - { - return [this](blt::span args) { - return this->operator()(args); - }; - } - - private: - template - static inline T& access_pack_index(blt::span args) - { - return *reinterpret_cast(args[index]); - } - - template - Return function_evaluator(blt::span args, std::integer_sequence) - { - return func(access_pack_index(args)...); - } - - function_t func; - }; - - class stack_allocator - { - constexpr static blt::size_t PAGE_SIZE = 0x1000; - constexpr static blt::size_t MAX_ALIGNMENT = 8; - public: - /** - * Pushes an instance of an object on to the stack - * @tparam T type to push - * @param value universal reference to the object to push - */ - template - void push(T&& value) - { - auto ptr = allocate_bytes(); - head->metadata.offset = static_cast(ptr) + aligned_size(); - new(ptr) T(std::forward(value)); - } - - template - T pop() - { - constexpr static auto TYPE_SIZE = aligned_size(); - if (head == nullptr) - throw std::runtime_error("Silly boi the stack is empty!"); - if (head->used_bytes_in_block() < static_cast(aligned_size())) - throw std::runtime_error((std::string("Mismatched Types! Not enough space left in block! Bytes: ") += std::to_string( - head->used_bytes_in_block()) += " Size: " + std::to_string(sizeof(T))).c_str()); - T t = *reinterpret_cast(head->metadata.offset - TYPE_SIZE); - head->metadata.offset -= TYPE_SIZE; - if (head->used_bytes_in_block() == 0) - { - auto ptr = head; - head = head->metadata.prev; - std::free(ptr); - } - return t; - } - - template - T& from(blt::size_t bytes) - { - constexpr static auto TYPE_SIZE = aligned_size(); - auto remaining_bytes = static_cast(bytes); - blt::i64 bytes_into_block = 0; - block* blk = head; - while (remaining_bytes > 0) - { - if (blk == nullptr) - throw std::runtime_error("Requested size is beyond the scope of this stack!"); - auto bytes_available = blk->used_bytes_in_block() - remaining_bytes; - bytes_into_block = remaining_bytes; - if (bytes_available < 0) - { - remaining_bytes = -bytes_available; - blk = head->metadata.prev; - } else - break; - } - if (blk == nullptr) - throw std::runtime_error("Some nonsense is going on. This function already smells"); - if (blk->used_bytes_in_block() < static_cast(aligned_size())) - throw std::runtime_error((std::string("Mismatched Types! Not enough space left in block! Bytes: ") += std::to_string( - blk->used_bytes_in_block()) += " Size: " + std::to_string(sizeof(T))).c_str()); - return *reinterpret_cast((blk->metadata.offset - bytes_into_block) - TYPE_SIZE); - } - - template - blt::size_t getByteOffset() + template + inline constexpr blt::size_t getByteOffset() const { blt::size_t offset = 0; blt::size_t current_index = 0; - ((offset += (current_index++ > index ? aligned_size() : 0)), ...); + ((offset += (current_index++ > index ? stack_allocator::aligned_size() : 0)), ...); return offset; } - template - CurrentArgument& getArgument() + template + inline CurrentArgument& getArgument(stack_allocator& allocator) const { - auto bytes = getByteOffset(); - return from(bytes); + auto bytes = getByteOffset(); + return allocator.from(bytes); } - template - Return sequence_to_indices(const operation& function, std::integer_sequence) + template + inline Return sequence_to_indices(stack_allocator& allocator, std::integer_sequence) const { - return function(getArgument()...); + return func(getArgument(allocator)...); } - template - Return run(const operation& function) + [[nodiscard]] inline Return operator()(stack_allocator& allocator) const { auto seq = std::make_integer_sequence(); - return sequence_to_indices(function, seq); - } - - [[nodiscard]] bool empty() const - { - if (head == nullptr) - return true; - if (head->metadata.prev != nullptr) - return false; - return head->used_bytes_in_block() == 0; - } - - [[nodiscard]] blt::ptrdiff_t bytes_in_head() const - { - if (head == nullptr) - return 0; - return head->used_bytes_in_block(); - } - - stack_allocator() = default; - - stack_allocator(const stack_allocator& copy) = delete; - - stack_allocator& operator=(const stack_allocator& copy) = delete; - - stack_allocator(stack_allocator&& move) noexcept - { - head = move.head; - move.head = nullptr; - } - - stack_allocator& operator=(stack_allocator&& move) noexcept - { - head = move.head; - move.head = nullptr; - return *this; - } - - ~stack_allocator() - { - block* current = head; - while (current != nullptr) - { - block* ptr = current; - current = current->metadata.prev; - std::free(ptr); - } + return sequence_to_indices(allocator, seq); } private: - struct block - { - struct block_metadata_t - { - blt::size_t size = 0; - block* next = nullptr; - block* prev = nullptr; - blt::u8* offset = nullptr; - } metadata; - blt::u8 buffer[8]{}; - - explicit block(blt::size_t size) - { - metadata.size = size; - metadata.offset = buffer; - } - - [[nodiscard]] blt::ptrdiff_t storage_size() const - { - return static_cast(metadata.size - sizeof(typename block::block_metadata_t)); - } - - [[nodiscard]] blt::ptrdiff_t used_bytes_in_block() const - { - return static_cast(metadata.offset - buffer); - } - - [[nodiscard]] blt::ptrdiff_t remaining_bytes_in_block() const - { - return storage_size() - used_bytes_in_block(); - } - }; +// template +// static inline T& access_pack_index(blt::span args) +// { +// return *reinterpret_cast(args[index]); +// } +// +// template +// Return function_evaluator(blt::span args, std::integer_sequence) +// { +// return func(access_pack_index(args)...); +// } - template - void* allocate_bytes() - { - auto ptr = get_aligned_pointer(sizeof(T)); - if (ptr == nullptr) - push_block_for(); - ptr = get_aligned_pointer(sizeof(T)); - if (ptr == nullptr) - throw std::bad_alloc(); - return ptr; - } - - void* get_aligned_pointer(blt::size_t bytes) - { - if (head == nullptr) - return nullptr; - blt::size_t remaining_bytes = head->remaining_bytes_in_block(); - auto* pointer = static_cast(head->metadata.offset); - return std::align(MAX_ALIGNMENT, bytes, pointer, remaining_bytes); - } - - template - void push_block_for() - { - push_block(std::max(PAGE_SIZE, to_nearest_page_size(sizeof(T)))); - } - - void push_block(blt::size_t size) - { - auto blk = allocate_block(size); - if (head == nullptr) - { - head = blk; - return; - } - head->metadata.next = blk; - blk->metadata.prev = head; - head = blk; - } - - static size_t to_nearest_page_size(blt::size_t bytes) - { - constexpr static blt::size_t MASK = ~(PAGE_SIZE - 1); - return (bytes & MASK) + PAGE_SIZE; - } - - static block* allocate_block(blt::size_t bytes) - { - auto size = to_nearest_page_size(bytes); - auto* data = std::aligned_alloc(PAGE_SIZE, size); - new(data) block{size}; - return reinterpret_cast(data); - } - - template - static inline constexpr blt::size_t aligned_size() noexcept - { - return (sizeof(T) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1); - } - - private: - block* head = nullptr; + function_t func; }; diff --git a/include/blt/gp/stack.h b/include/blt/gp/stack.h new file mode 100644 index 0000000..dfc6d25 --- /dev/null +++ b/include/blt/gp/stack.h @@ -0,0 +1,257 @@ +#pragma once +/* + * Copyright (C) 2024 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BLT_GP_STACK_H +#define BLT_GP_STACK_H + +#include +#include +#include +#include +#include + +namespace blt::gp +{ + class stack_allocator + { + constexpr static blt::size_t PAGE_SIZE = 0x1000; + constexpr static blt::size_t MAX_ALIGNMENT = 8; + public: + /** + * Pushes an instance of an object on to the stack + * @tparam T type to push + * @param value universal reference to the object to push + */ + template + void push(T&& value) + { + auto ptr = allocate_bytes(); + head->metadata.offset = static_cast(ptr) + aligned_size(); + new(ptr) T(std::forward(value)); + } + + template + T pop() + { + constexpr static auto TYPE_SIZE = aligned_size(); + if (head == nullptr) + throw std::runtime_error("Silly boi the stack is empty!"); + if (head->used_bytes_in_block() < static_cast(aligned_size())) + throw std::runtime_error((std::string("Mismatched Types! Not enough space left in block! Bytes: ") += std::to_string( + head->used_bytes_in_block()) += " Size: " + std::to_string(sizeof(T))).c_str()); + T t = *reinterpret_cast(head->metadata.offset - TYPE_SIZE); + head->metadata.offset -= TYPE_SIZE; + if (head->used_bytes_in_block() == 0) + { + auto ptr = head; + head = head->metadata.prev; + std::free(ptr); + } + return t; + } + + template + T& from(blt::size_t bytes) + { + constexpr static auto TYPE_SIZE = aligned_size(); + auto remaining_bytes = static_cast(bytes); + blt::i64 bytes_into_block = 0; + block* blk = head; + while (remaining_bytes > 0) + { + if (blk == nullptr) + throw std::runtime_error("Requested size is beyond the scope of this stack!"); + auto bytes_available = blk->used_bytes_in_block() - remaining_bytes; + bytes_into_block = remaining_bytes; + if (bytes_available < 0) + { + remaining_bytes = -bytes_available; + blk = head->metadata.prev; + } else + break; + } + if (blk == nullptr) + throw std::runtime_error("Some nonsense is going on. This function already smells"); + if (blk->used_bytes_in_block() < static_cast(aligned_size())) + throw std::runtime_error((std::string("Mismatched Types! Not enough space left in block! Bytes: ") += std::to_string( + blk->used_bytes_in_block()) += " Size: " + std::to_string(sizeof(T))).c_str()); + return *reinterpret_cast((blk->metadata.offset - bytes_into_block) - TYPE_SIZE); + } + + void pop_bytes(blt::ptrdiff_t bytes) + { + while (bytes > 0) + { + auto diff = head->used_bytes_in_block() - bytes; + // if there is not enough room left to pop completely off the block, then move to the next previous block + // and pop from it, update the amount of bytes to reflect the amount removed from the current block + if (diff <= 0) + { + bytes -= head->used_bytes_in_block(); + head = head->metadata.prev; + } else // otherwise update the offset pointer + head->metadata.offset -= bytes; + } + } + + [[nodiscard]] bool empty() const + { + if (head == nullptr) + return true; + if (head->metadata.prev != nullptr) + return false; + return head->used_bytes_in_block() == 0; + } + + [[nodiscard]] blt::ptrdiff_t bytes_in_head() const + { + if (head == nullptr) + return 0; + return head->used_bytes_in_block(); + } + + stack_allocator() = default; + + stack_allocator(const stack_allocator& copy) = delete; + + stack_allocator& operator=(const stack_allocator& copy) = delete; + + stack_allocator(stack_allocator&& move) noexcept + { + head = move.head; + move.head = nullptr; + } + + stack_allocator& operator=(stack_allocator&& move) noexcept + { + head = move.head; + move.head = nullptr; + return *this; + } + + ~stack_allocator() + { + block* current = head; + while (current != nullptr) + { + block* ptr = current; + current = current->metadata.prev; + std::free(ptr); + } + } + + template + static inline constexpr blt::size_t aligned_size() noexcept + { + return (sizeof(T) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1); + } + + private: + struct block + { + struct block_metadata_t + { + blt::size_t size = 0; + block* next = nullptr; + block* prev = nullptr; + blt::u8* offset = nullptr; + } metadata; + blt::u8 buffer[8]{}; + + explicit block(blt::size_t size) + { + metadata.size = size; + metadata.offset = buffer; + } + + [[nodiscard]] blt::ptrdiff_t storage_size() const + { + return static_cast(metadata.size - sizeof(typename block::block_metadata_t)); + } + + [[nodiscard]] blt::ptrdiff_t used_bytes_in_block() const + { + return static_cast(metadata.offset - buffer); + } + + [[nodiscard]] blt::ptrdiff_t remaining_bytes_in_block() const + { + return storage_size() - used_bytes_in_block(); + } + }; + + template + void* allocate_bytes() + { + auto ptr = get_aligned_pointer(sizeof(T)); + if (ptr == nullptr) + push_block_for(); + ptr = get_aligned_pointer(sizeof(T)); + if (ptr == nullptr) + throw std::bad_alloc(); + return ptr; + } + + void* get_aligned_pointer(blt::size_t bytes) + { + if (head == nullptr) + return nullptr; + blt::size_t remaining_bytes = head->remaining_bytes_in_block(); + auto* pointer = static_cast(head->metadata.offset); + return std::align(MAX_ALIGNMENT, bytes, pointer, remaining_bytes); + } + + template + void push_block_for() + { + push_block(std::max(PAGE_SIZE, to_nearest_page_size(sizeof(T)))); + } + + void push_block(blt::size_t size) + { + auto blk = allocate_block(size); + if (head == nullptr) + { + head = blk; + return; + } + head->metadata.next = blk; + blk->metadata.prev = head; + head = blk; + } + + static size_t to_nearest_page_size(blt::size_t bytes) + { + constexpr static blt::size_t MASK = ~(PAGE_SIZE - 1); + return (bytes & MASK) + PAGE_SIZE; + } + + static block* allocate_block(blt::size_t bytes) + { + auto size = to_nearest_page_size(bytes); + auto* data = std::aligned_alloc(PAGE_SIZE, size); + new(data) block{size}; + return reinterpret_cast(data); + } + + private: + block* head = nullptr; + }; +} + +#endif //BLT_GP_STACK_H