#pragma once
|
|
#include "gp_config.hpp"
|
|
#include "gp/buffer.hpp"
|
|
#include "gp/array.hpp"
|
|
#include "gp/integer_math.hpp"
|
|
#include <type_traits>
|
|
#include <gp/algorithm/tmp_manip.hpp>
|
|
|
|
|
|
|
|
namespace gp{
|
|
template<typename page_allocator, size_t max_msb = 24, size_t align = 8>
|
|
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<char> 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<bundle, span_size> 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<typename function>
|
|
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<typename function>
|
|
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<typename function>
|
|
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<char>(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<intptr_t>(v) % align) ==0)
|
|
{
|
|
data=gp::buffer<char>(reinterpret_cast<char*>(v),reinterpret_cast<char*>(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<void*>(index*depth_to_size(depth)
|
|
+ reinterpret_cast<intptr_t>(&*data.begin()));
|
|
}
|
|
|
|
bool deallocate(void* ptr)
|
|
{
|
|
if(data.contains((char*)ptr))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
~buddy()
|
|
{
|
|
if constexpr(gp::has_allocator_interface<page_allocator>::value)
|
|
{
|
|
allocator.deallocate(&data[0]);
|
|
}
|
|
}
|
|
};
|
|
}
|