#pragma once #include "gp_config.hpp" #include "gp/buffer.hpp" #include "gp/array.hpp" #include "gp/integer_math.hpp" #include #include namespace gp{ template class buddy{ struct twig { bool used : 1; bool used_children : 1; twig(uint8_t src) { used = 1 & src; used_children = 2 & src; } operator uint8_t() { return used + 2 * used_children; } }; struct bundle { uint8_t a : 2; uint8_t b : 2; uint8_t c : 2; uint8_t d : 2; }; page_allocator allocator; gp::buffer data; static constexpr size_t max_depth = max_msb - gp::math::msb(align); static constexpr size_t required_twigs = (1 << (max_depth + 1)) - 1; /** * ((max allocatable size - min allocatable size) ** 2 - 1) / 4 twigs in a bundle **/ static constexpr size_t span_size = required_twigs / 4 + (required_twigs % 4 != 0); gp::array stack; twig get_twig(size_t idx) { auto far = idx / 4; auto local = idx % 4; auto& group = stack[far]; switch(local) { case 0: return group.a; case 1: return group.b; case 2: return group.c; case 3: return group.d; } } void set_twig(size_t idx, twig v) { auto far = idx / 4; auto local = idx % 4; auto& group = stack[far]; switch(local) { case 0: group.a = v; return; case 1: group.b = v; return; case 2: group.c = v; return; case 3: group.d = v; return; } } constexpr size_t size_to_depth(size_t sz) { auto pow2 = gp::math::msb(sz); return gp::max(max_depth ,max_msb - pow2 - (0 != sz % (1 << pow2))); } constexpr size_t depth_to_size(size_t depth) { return 1 << (max_depth - depth + gp::math::msb(align)); } size_t get_left(size_t index) { return ((index + 1) << 1) - 1; } size_t get_right(size_t index) { return ((index + 1) << 1); } template void all_under(size_t index, function func) { size_t left = get_left(index); size_t right = get_right(index); all_under(left, func); all_under(right, func); func(left); func(right); } template void all_over(size_t index, function func) { size_t parent = ((index + 1) >> 1) - 1; func(parent); if(parent != 0) all_over(parent, func); } template bool is_any_child(size_t index, function func) { size_t left = get_left(index); size_t right = get_right(index); if(func(left)) return true; if(func(right)) return true; if(any_child(left, func)) return true; if(any_child(right, func)) return true; return false; } static constexpr size_t no_twig = -1; size_t find_free_twig(size_t depth, size_t root = 0, size_t explored = 0) { if(depth == explored) { auto v = get_twig(root); if(v.used || v.used_children) { return no_twig; } else { return root; } } else { ++explored; auto ret = find_free_twig(depth, get_right(root), explored); if(ret != no_twig) { return ret; } ret = find_free_twig(depth, get_left(root), explored); if(ret != no_twig) { return ret; } } return no_twig; } public: buddy() : data(gp::buffer(nullptr,nullptr)) {} buddy(size_t sz) : data(nullptr,nullptr) { if(sz!=0 && (sz & (sz - 1)) == 0) { auto v=allocator.allocate(sz); if(v!=nullptr && (static_cast(v) % align) ==0) { data=gp::buffer(reinterpret_cast(v),reinterpret_cast(v)+sz); } } } buddy(char* pos,size_t sz) : data(pos,pos+sz) { } void* allocate(size_t sz) { auto depth = size_to_depth(sz); auto index = find_free_twig(depth); if(index == no_twig) { return nullptr; } all_over(index, [&](size_t idx){ auto t = get_twig(idx); t.used_children = true; set_twig(idx, t); }); auto t = get_twig(index); t.used = true; set_twig(index, t); return reinterpret_cast(index*depth_to_size(depth) + reinterpret_cast(&*data.begin())); } bool deallocate(void* ptr) { if(data.contains((char*)ptr)) { return true; } return false; } ~buddy() { if constexpr(gp::has_allocator_interface::value) { allocator.deallocate(&data[0]); } } }; }