|
|
- #pragma once
- #include "gp_config.hpp"
- #include "gp/buffer.hpp"
- #include "gp/array.hpp"
- #include "gp/math/integer_math.hpp"
- #include <type_traits>
- #include <gp/algorithm/tmp_manip.hpp>
- #include <gp/algorithm/modifiers.hpp>
- #include <gp/allocator/dummy.hpp>
-
-
- namespace gp{
- template<typename page_allocator = gp::dummy_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 1 * used + 2 * used_children;
- }
- };
- struct bundle {
- uint8_t a : 2;
- uint8_t b : 2;
- uint8_t c : 2;
- uint8_t d : 2;
-
- bundle() {
- a = 0; b = 0; c = 0; d = 0;
- }
- };
- page_allocator allocator;
- gp::buffer<char> data;
- 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
- **/
- static constexpr size_t span_size = required_twigs / 4 + (required_twigs % 4 != 0);
- gp::array<bundle, span_size> stack;
-
- /**
- * This code has been manually hecked and will always return.
- * If you find a case where it doesn't, please file an issue.
- **/
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wreturn-type"
- twig get_twig(size_t idx) const {
- auto far = idx / 4;
- auto local = idx % 4;
- switch(local) {
- case 0:
- return stack[far].a;
- case 1:
- return stack[far].b;
- case 2:
- return stack[far].c;
- case 3:
- return stack[far].d;
- }
- }
- #pragma clang diagnostic pop
-
- 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) {
- 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) {
- return 1 << (max_depth - depth + gp::math::msb(align));
- }
-
- constexpr size_t get_left(size_t index) const {
- return ((index + 1) << 1) - 1;
- }
-
- constexpr size_t get_right(size_t index) const {
- 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) {
- if(index != 0) {
- 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) const {
- size_t left = get_left(index);
- size_t right = get_right(index);
- 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;
- }
-
- static constexpr size_t no_twig = -1;
-
- 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(v.used || v.used_children)
- {
- return no_twig;
- } else {
- return root;
- }
- } else {
- if(v.used)
- {
- return no_twig;
- }
- ++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;
- }
-
- 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:
- buddy()
- : data(gp::buffer<char>(nullptr,nullptr))
- , max_depth(0)
- , twig_explore_length(1 << max_depth)
- {}
-
- buddy(size_t sz)
- : 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)
- {
- auto v=allocator.allocate(sz);
- if(v!=nullptr)
- {
- 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)
- : data(pos,pos+sz)
- , max_depth(gp::math::msb(sz)-gp::math::msb(align))
- , twig_explore_length(1 << max_depth)
- {
- }
-
- void* allocate(size_t sz)
- {
- auto depth = size_to_depth(sz);
- auto index = find_free_twig(depth);
- if(index == no_twig)
- {
- 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){
- 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 pot;
- }
-
- constexpr bool try_reallocate(void*, size_t) {
- return false;
- }
-
- bool deallocate(void* 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 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()
- {
- allocator.deallocate(data.begin().data);
- }
- };
- }
|