|
@ -5,11 +5,12 @@ |
|
|
#include "gp/integer_math.hpp"
|
|
|
#include "gp/integer_math.hpp"
|
|
|
#include <type_traits>
|
|
|
#include <type_traits>
|
|
|
#include <gp/algorithm/tmp_manip.hpp>
|
|
|
#include <gp/algorithm/tmp_manip.hpp>
|
|
|
|
|
|
|
|
|
|
|
|
#include <gp/algorithm/modifiers.hpp>
|
|
|
|
|
|
#include <gp/allocator/dummy.hpp>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace gp{ |
|
|
namespace gp{ |
|
|
template<typename page_allocator, size_t max_msb = 24, size_t align = 8> |
|
|
|
|
|
|
|
|
template<typename page_allocator = gp::dummy_allocator, size_t max_msb = 24, size_t align = 8> |
|
|
class buddy{ |
|
|
class buddy{ |
|
|
struct twig { |
|
|
struct twig { |
|
|
bool used : 1; |
|
|
bool used : 1; |
|
@ -20,7 +21,7 @@ namespace gp{ |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
operator uint8_t() { |
|
|
operator uint8_t() { |
|
|
return used + 2 * used_children; |
|
|
|
|
|
|
|
|
return mi">1 * used + 2 * used_children; |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
struct bundle { |
|
|
struct bundle { |
|
@ -28,30 +29,35 @@ namespace gp{ |
|
|
uint8_t b : 2; |
|
|
uint8_t b : 2; |
|
|
uint8_t c : 2; |
|
|
uint8_t c : 2; |
|
|
uint8_t d : 2; |
|
|
uint8_t d : 2; |
|
|
|
|
|
|
|
|
|
|
|
bundle() { |
|
|
|
|
|
a = 0; b = 0; c = 0; d = 0; |
|
|
|
|
|
} |
|
|
}; |
|
|
}; |
|
|
page_allocator allocator; |
|
|
page_allocator allocator; |
|
|
gp::buffer<char> data; |
|
|
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; |
|
|
|
|
|
|
|
|
const size_t max_depth; |
|
|
|
|
|
const size_t twig_explore_length; |
|
|
|
|
|
static constexpr size_t max_theoric_depth = max_msb - gp::math::msb(align); |
|
|
|
|
|
static constexpr size_t required_twigs = (1 << (max_theoric_depth + 1)) - 1; |
|
|
/**
|
|
|
/**
|
|
|
* ((max allocatable size - min allocatable size) ** 2 - 1) / 4 twigs in a bundle |
|
|
* ((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); |
|
|
static constexpr size_t span_size = required_twigs / 4 + (required_twigs % 4 != 0); |
|
|
gp::array<bundle, span_size> stack; |
|
|
gp::array<bundle, span_size> stack; |
|
|
|
|
|
|
|
|
twig get_twig(size_t idx) { |
|
|
|
|
|
|
|
|
twig get_twig(size_t idx) k">const { |
|
|
auto far = idx / 4; |
|
|
auto far = idx / 4; |
|
|
auto local = idx % 4; |
|
|
auto local = idx % 4; |
|
|
auto& group = stack[far]; |
|
|
|
|
|
switch(local) { |
|
|
switch(local) { |
|
|
case 0: |
|
|
case 0: |
|
|
return group.a; |
|
|
|
|
|
|
|
|
return stack[far].a; |
|
|
case 1: |
|
|
case 1: |
|
|
return group.b; |
|
|
|
|
|
|
|
|
return stack[far].b; |
|
|
case 2: |
|
|
case 2: |
|
|
return group.c; |
|
|
|
|
|
|
|
|
return stack[far].c; |
|
|
case 3: |
|
|
case 3: |
|
|
return group.d; |
|
|
|
|
|
|
|
|
return stack[far].d; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -76,19 +82,23 @@ namespace gp{ |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
constexpr size_t size_to_depth(size_t sz) { |
|
|
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))); |
|
|
|
|
|
|
|
|
size_t pow2 = gp::math::msb(sz) - gp::math::msb(align); |
|
|
|
|
|
return gp::clamp( |
|
|
|
|
|
(size_t)0 , |
|
|
|
|
|
max_depth - pow2, |
|
|
|
|
|
max_depth |
|
|
|
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
constexpr size_t depth_to_size(size_t depth) { |
|
|
constexpr size_t depth_to_size(size_t depth) { |
|
|
return 1 << (max_depth - depth + gp::math::msb(align)); |
|
|
return 1 << (max_depth - depth + gp::math::msb(align)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
size_t get_left(size_t index) { |
|
|
|
|
|
|
|
|
k">constexpr size_t get_left(size_t index) const { |
|
|
return ((index + 1) << 1) - 1; |
|
|
return ((index + 1) << 1) - 1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
size_t get_right(size_t index) { |
|
|
|
|
|
|
|
|
k">constexpr size_t get_right(size_t index) const { |
|
|
return ((index + 1) << 1); |
|
|
return ((index + 1) << 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -104,28 +114,32 @@ namespace gp{ |
|
|
|
|
|
|
|
|
template<typename function> |
|
|
template<typename function> |
|
|
void all_over(size_t index, function func) { |
|
|
void all_over(size_t index, function func) { |
|
|
size_t parent = ((index + 1) >> 1) - 1; |
|
|
|
|
|
func(parent); |
|
|
|
|
|
if(parent != 0) |
|
|
|
|
|
all_over(parent, func); |
|
|
|
|
|
|
|
|
if(index != 0) { |
|
|
|
|
|
size_t parent = ((index + 1) >> 1) - 1; |
|
|
|
|
|
func(parent); |
|
|
|
|
|
if(parent != 0) |
|
|
|
|
|
all_over(parent, func); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
template<typename function> |
|
|
template<typename function> |
|
|
bool is_any_child(size_t index, function func) { |
|
|
|
|
|
|
|
|
bool is_any_child(size_t index, function func) k">const { |
|
|
size_t left = get_left(index); |
|
|
size_t left = get_left(index); |
|
|
size_t right = get_right(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; |
|
|
|
|
|
|
|
|
if(left < twig_explore_length && right < twig_explore_length) { |
|
|
|
|
|
if(func(left)) return true; |
|
|
|
|
|
if(func(right)) return true; |
|
|
|
|
|
if(is_any_child(left, func)) return true; |
|
|
|
|
|
if(is_any_child(right, func)) return true; |
|
|
|
|
|
} |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static constexpr size_t no_twig = -1; |
|
|
static constexpr size_t no_twig = -1; |
|
|
|
|
|
|
|
|
size_t find_free_twig(size_t depth, size_t root = 0, size_t explored = 0) { |
|
|
|
|
|
|
|
|
size_t find_free_twig(size_t depth, size_t root = 0, size_t explored = 0) const { |
|
|
|
|
|
auto v = get_twig(root); |
|
|
if(depth == explored) { |
|
|
if(depth == explored) { |
|
|
auto v = get_twig(root); |
|
|
|
|
|
if(v.used || v.used_children) |
|
|
if(v.used || v.used_children) |
|
|
{ |
|
|
{ |
|
|
return no_twig; |
|
|
return no_twig; |
|
@ -133,6 +147,10 @@ namespace gp{ |
|
|
return root; |
|
|
return root; |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
|
|
|
if(v.used) |
|
|
|
|
|
{ |
|
|
|
|
|
return no_twig; |
|
|
|
|
|
} |
|
|
++explored; |
|
|
++explored; |
|
|
auto ret = find_free_twig(depth, get_right(root), explored); |
|
|
auto ret = find_free_twig(depth, get_right(root), explored); |
|
|
if(ret != no_twig) |
|
|
if(ret != no_twig) |
|
@ -147,26 +165,65 @@ namespace gp{ |
|
|
} |
|
|
} |
|
|
return no_twig; |
|
|
return no_twig; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
size_t find_used_twig(size_t offset, size_t root = 0, size_t explored = 0) { |
|
|
|
|
|
auto v = get_twig(root); |
|
|
|
|
|
if(v.used && offset == 0) |
|
|
|
|
|
{ |
|
|
|
|
|
return root; |
|
|
|
|
|
} |
|
|
|
|
|
++explored; |
|
|
|
|
|
if(explored > max_depth) return no_twig; |
|
|
|
|
|
size_t cut = (1 << (max_depth + gp::math::log2(align))) >> explored; |
|
|
|
|
|
if(offset >= cut) |
|
|
|
|
|
{ |
|
|
|
|
|
return find_used_twig(offset-cut, get_right(root), explored); |
|
|
|
|
|
} else { |
|
|
|
|
|
return find_used_twig(offset, get_left(root), explored); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool empty_node(const buddy* me, size_t node) { |
|
|
|
|
|
gp_config::assertion(node < me->twig_explore_length, "bad emptyness test"); |
|
|
|
|
|
auto p = me->get_twig(node); |
|
|
|
|
|
return !( |
|
|
|
|
|
p.used | p.used_children |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
public: |
|
|
public: |
|
|
buddy() |
|
|
buddy() |
|
|
: data(gp::buffer<char>(nullptr,nullptr)) |
|
|
: data(gp::buffer<char>(nullptr,nullptr)) |
|
|
|
|
|
, max_depth(0) |
|
|
|
|
|
, twig_explore_length(1 << max_depth) |
|
|
{} |
|
|
{} |
|
|
|
|
|
|
|
|
buddy(size_t sz) |
|
|
buddy(size_t sz) |
|
|
: data(nullptr,nullptr) |
|
|
: data(nullptr,nullptr) |
|
|
|
|
|
, max_depth(gp::math::msb(sz)-gp::math::msb(align)) |
|
|
|
|
|
, twig_explore_length(1 << max_depth) |
|
|
{ |
|
|
{ |
|
|
if(sz!=0 && (sz & (sz - 1)) == 0) |
|
|
if(sz!=0 && (sz & (sz - 1)) == 0) |
|
|
{ |
|
|
{ |
|
|
auto v=allocator.allocate(sz); |
|
|
auto v=allocator.allocate(sz); |
|
|
if(v!=nullptr && (static_cast<intptr_t>(v) % align) ==0) |
|
|
|
|
|
|
|
|
if(v!=nullptr) |
|
|
{ |
|
|
{ |
|
|
data=gp::buffer<char>(reinterpret_cast<char*>(v),reinterpret_cast<char*>(v)+sz); |
|
|
|
|
|
|
|
|
if((reinterpret_cast<intptr_t>(v) % align) ==0) |
|
|
|
|
|
{ |
|
|
|
|
|
data=gp::buffer<char>(reinterpret_cast<char*>(v),reinterpret_cast<char*>(v)+sz); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
allocator.deallocate(v); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
buddy(char* pos,size_t sz) |
|
|
buddy(char* pos,size_t sz) |
|
|
: data(pos,pos+sz) |
|
|
: data(pos,pos+sz) |
|
|
|
|
|
, max_depth(gp::math::msb(sz)-gp::math::msb(align)) |
|
|
|
|
|
, twig_explore_length(1 << max_depth) |
|
|
{ |
|
|
{ |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -179,33 +236,68 @@ namespace gp{ |
|
|
return nullptr; |
|
|
return nullptr; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
auto pot = reinterpret_cast<char*>( |
|
|
|
|
|
(index - (1 << depth) + 1)*depth_to_size(depth) |
|
|
|
|
|
+ reinterpret_cast<intptr_t>(&*data.begin()) |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
if(!data.contains(pot)) { |
|
|
|
|
|
return nullptr; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
all_over(index, [&](size_t idx){ |
|
|
all_over(index, [&](size_t idx){ |
|
|
auto t = get_twig(idx); |
|
|
auto t = get_twig(idx); |
|
|
t.used_children = true; |
|
|
t.used_children = true; |
|
|
set_twig(idx, t); |
|
|
set_twig(idx, t); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
auto t = get_twig(index); |
|
|
auto t = get_twig(index); |
|
|
t.used = true; |
|
|
t.used = true; |
|
|
set_twig(index, t); |
|
|
set_twig(index, t); |
|
|
return reinterpret_cast<void*>(index*depth_to_size(depth) |
|
|
|
|
|
+ reinterpret_cast<intptr_t>(&*data.begin())); |
|
|
|
|
|
|
|
|
return pot; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
constexpr bool try_reallocate(void*, size_t) { |
|
|
|
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
bool deallocate(void* ptr) |
|
|
bool deallocate(void* ptr) |
|
|
{ |
|
|
{ |
|
|
if(data.contains((char*)ptr)) |
|
|
if(data.contains((char*)ptr)) |
|
|
{ |
|
|
{ |
|
|
|
|
|
size_t integral_offset = reinterpret_cast<intptr_t>(ptr) - reinterpret_cast<intptr_t>(&*data.begin()); |
|
|
|
|
|
auto index = find_used_twig(integral_offset); |
|
|
|
|
|
if(index == no_twig) |
|
|
|
|
|
{ |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
twig v = get_twig(index); |
|
|
|
|
|
v.used = false; |
|
|
|
|
|
v.used_children = false; |
|
|
|
|
|
set_twig(index, v); |
|
|
|
|
|
all_over(index, [&](size_t idx){ |
|
|
|
|
|
auto l = get_twig(get_left(idx)); |
|
|
|
|
|
auto r = get_twig(get_right(idx)); |
|
|
|
|
|
|
|
|
|
|
|
set_twig(idx, 2*(l.used | l.used_children | r.used | r.used_children)); |
|
|
|
|
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
return true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool empty() const { |
|
|
|
|
|
buddy* addr = (buddy*)this; |
|
|
|
|
|
auto prepred = not_fn(&buddy::empty_node); |
|
|
|
|
|
auto pred = bind_front(prepred, addr); |
|
|
|
|
|
return empty_node(addr, 0) && !is_any_child(0, pred); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
~buddy() |
|
|
~buddy() |
|
|
{ |
|
|
{ |
|
|
if constexpr(gp::has_allocator_interface<page_allocator>::value) |
|
|
|
|
|
{ |
|
|
|
|
|
allocator.deallocate(&data[0]); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
allocator.deallocate(data.begin().data); |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |