|
|
@ -24,29 +24,46 @@ namespace gp{ |
|
|
|
*/ |
|
|
|
template<size_t max_msb = 24, size_t align = 8> |
|
|
|
class buddy : public allocator { |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief an allocation tree node, made into an easily packable struct |
|
|
|
*/ |
|
|
|
struct twig { |
|
|
|
bool used : 1; |
|
|
|
bool used_children : 1; |
|
|
|
twig(uint8_t src) { |
|
|
|
twig(uint8_t src) k">noexcept { |
|
|
|
used = 1 & src; |
|
|
|
used_children = 2 & src; |
|
|
|
} |
|
|
|
|
|
|
|
operator uint8_t() { |
|
|
|
operator uint8_t() k">noexcept { |
|
|
|
return 1 * used + 2 * used_children; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief a structure to pack 4 twig within |
|
|
|
*/ |
|
|
|
struct bundle { |
|
|
|
uint8_t a : 2; |
|
|
|
uint8_t b : 2; |
|
|
|
uint8_t c : 2; |
|
|
|
uint8_t d : 2; |
|
|
|
|
|
|
|
bundle() { |
|
|
|
bundle() k">noexcept { |
|
|
|
a = 0; b = 0; c = 0; d = 0; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief a meta allocator to allocate the memory used and deallocate it later |
|
|
|
*/ |
|
|
|
gp::optional<gp::reference_wrapper<allocator>> allocator_v; |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief The actual buffer where data is stored |
|
|
|
* |
|
|
|
*/ |
|
|
|
gp::buffer<char> data; |
|
|
|
const size_t max_depth; |
|
|
|
const size_t twig_explore_length; |
|
|
@ -67,7 +84,7 @@ namespace gp{ |
|
|
|
static constexpr size_t span_size = required_twigs / 4 + (required_twigs % 4 != 0); |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief The array of twigs (in bundles) |
|
|
|
* @brief The array of twigs (in bundles) aka the metadata |
|
|
|
*/ |
|
|
|
gp::array<bundle, span_size> stack; |
|
|
|
|
|
|
@ -79,7 +96,13 @@ namespace gp{ |
|
|
|
#pragma clang diagnostic ignored "-Wreturn-type"
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wreturn-type"
|
|
|
|
twig get_twig(size_t idx) const { |
|
|
|
/**
|
|
|
|
* @brief Get the value of the twig at the specified index |
|
|
|
* |
|
|
|
* @param idx |
|
|
|
* @return twig A copy of the specified twig |
|
|
|
*/ |
|
|
|
twig get_twig(size_t idx) const noexcept { |
|
|
|
auto far = idx / 4; |
|
|
|
auto local = idx % 4; |
|
|
|
switch(local) { |
|
|
@ -96,7 +119,13 @@ namespace gp{ |
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
|
|
|
|
void set_twig(size_t idx, twig v) { |
|
|
|
/**
|
|
|
|
* @brief Set the twig at the specified index to the value of the heredescribed twig |
|
|
|
* |
|
|
|
* @param idx |
|
|
|
* @param v |
|
|
|
*/ |
|
|
|
void set_twig(size_t idx, twig v) noexcept { |
|
|
|
auto far = idx / 4; |
|
|
|
auto local = idx % 4; |
|
|
|
auto& group = stack[far]; |
|
|
@ -117,7 +146,13 @@ namespace gp{ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
constexpr size_t size_to_depth(size_t sz) { |
|
|
|
/**
|
|
|
|
* @brief Deduces the depth to explore to allocate the specified size |
|
|
|
* |
|
|
|
* @param sz |
|
|
|
* @return size_t |
|
|
|
*/ |
|
|
|
constexpr size_t size_to_depth(size_t sz) noexcept { |
|
|
|
size_t pow2 = gp::math::msb(sz) - gp::math::msb(align); |
|
|
|
return gp::clamp( |
|
|
|
(size_t)0 , |
|
|
@ -126,18 +161,42 @@ namespace gp{ |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
constexpr size_t depth_to_size(size_t depth) { |
|
|
|
/**
|
|
|
|
* @brief Deduces the size from the specified depth |
|
|
|
* |
|
|
|
* @param depth |
|
|
|
* @return size_t |
|
|
|
*/ |
|
|
|
constexpr size_t depth_to_size(size_t depth) noexcept { |
|
|
|
return 1 << (max_depth - depth + gp::math::msb(align)); |
|
|
|
} |
|
|
|
|
|
|
|
constexpr size_t get_left(size_t index) const { |
|
|
|
/**
|
|
|
|
* @brief Get the left child index from the specified index |
|
|
|
* |
|
|
|
* @param index |
|
|
|
* @return size_t |
|
|
|
*/ |
|
|
|
constexpr size_t get_left(size_t index) const noexcept { |
|
|
|
return ((index + 1) << 1) - 1; |
|
|
|
} |
|
|
|
|
|
|
|
constexpr size_t get_right(size_t index) const { |
|
|
|
/**
|
|
|
|
* @brief Get the right child index from the specified index |
|
|
|
* |
|
|
|
* @param index |
|
|
|
* @return size_t |
|
|
|
*/ |
|
|
|
constexpr size_t get_right(size_t index) const noexcept{ |
|
|
|
return ((index + 1) << 1); |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Recursively applies a function to the children of a given node |
|
|
|
* |
|
|
|
* @param index the starting node |
|
|
|
* @param func the function to apply downwards |
|
|
|
*/ |
|
|
|
template<typename function> |
|
|
|
void all_under(size_t index, function func) { |
|
|
|
size_t left = get_left(index); |
|
|
@ -148,6 +207,13 @@ namespace gp{ |
|
|
|
func(right); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Recursively applies a function to the chain of parents of the given node |
|
|
|
* |
|
|
|
* @param index the starting node |
|
|
|
* @param func the function to apply upwards |
|
|
|
*/ |
|
|
|
template<typename function> |
|
|
|
void all_over(size_t index, function func) { |
|
|
|
if(index != 0) { |
|
|
@ -158,6 +224,14 @@ namespace gp{ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Recursively checks if any child matches a given predicate |
|
|
|
* |
|
|
|
* @param index The parent |
|
|
|
* @param func The predicate |
|
|
|
* @return true if the predicate matched once |
|
|
|
* @return false if the predicate never matched |
|
|
|
*/ |
|
|
|
template<typename function> |
|
|
|
bool is_any_child(size_t index, function func) const { |
|
|
|
size_t left = get_left(index); |
|
|
@ -171,8 +245,21 @@ namespace gp{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief a constant used to specify that no twig has been found |
|
|
|
*/ |
|
|
|
static constexpr size_t no_twig = -1; |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Finds a twig that represent free memory |
|
|
|
* |
|
|
|
* It's free real estate! |
|
|
|
* |
|
|
|
* @param depth the depth to allocate |
|
|
|
* @param root the root to look from |
|
|
|
* @param explored the current depth we sit at |
|
|
|
* @return size_t |
|
|
|
*/ |
|
|
|
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) { |
|
|
@ -202,6 +289,14 @@ namespace gp{ |
|
|
|
return no_twig; |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Recursively looks for the highest filled nodewith the given offset |
|
|
|
* |
|
|
|
* @param offset The offset to look for |
|
|
|
* @param root the root to look from |
|
|
|
* @param explored the explored depth |
|
|
|
* @return size_t |
|
|
|
*/ |
|
|
|
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) |
|
|
@ -279,6 +374,12 @@ namespace gp{ |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Allocates memory |
|
|
|
* |
|
|
|
* @param sz The size of memory to allocate |
|
|
|
* @return void* the allocated memory OR nullptr |
|
|
|
*/ |
|
|
|
virtual void* allocate(size_t sz) |
|
|
|
{ |
|
|
|
auto depth = size_to_depth(sz); |
|
|
@ -309,10 +410,22 @@ namespace gp{ |
|
|
|
return pot; |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Tries to reallocate (UNIMPLEMENTED) |
|
|
|
* |
|
|
|
* @return false |
|
|
|
*/ |
|
|
|
virtual bool try_reallocate(void*, size_t) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Tries to deallocate the given memory |
|
|
|
* |
|
|
|
* @param ptr the pointer to the memory to deallocate |
|
|
|
* @return true if everything went fine |
|
|
|
* @return false if deallocation failed |
|
|
|
*/ |
|
|
|
virtual bool deallocate(void* ptr) |
|
|
|
{ |
|
|
|
if(data.contains((char*)ptr)) |
|
|
@ -330,7 +443,8 @@ namespace gp{ |
|
|
|
all_over(index, [&](size_t idx){ |
|
|
|
auto l = get_twig(get_left(idx)); |
|
|
|
auto r = get_twig(get_right(idx)); |
|
|
|
|
|
|
|
// And now for the tricky bit:
|
|
|
|
// Sets the "used_children" bit if any of the collected twigs has a bit set
|
|
|
|
set_twig(idx, 2*(l.used | l.used_children | r.used | r.used_children)); |
|
|
|
}); |
|
|
|
|
|
|
@ -345,13 +459,16 @@ namespace gp{ |
|
|
|
* @return true if the allocator is completely empty |
|
|
|
* @return false if anything is still allocated in there |
|
|
|
*/ |
|
|
|
bool empty() const { |
|
|
|
buddy* addr = (buddy*)this; |
|
|
|
auto prepred = not_fn(&buddy::empty_node); |
|
|
|
auto pred = bind_front(prepred, addr); |
|
|
|
bool empty() const k">noexcept { |
|
|
|
k">const buddy* addr = (buddy*)this; |
|
|
|
const auto prepred = not_fn(&buddy::empty_node); |
|
|
|
const auto pred = bind_front(prepred, addr); |
|
|
|
return empty_node(addr, 0) && !is_any_child(0, pred); |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Destroy the buddy object, will wrink out the space if it was created with an allocator |
|
|
|
*/ |
|
|
|
virtual ~buddy() |
|
|
|
{ |
|
|
|
if(allocator_v.has_value()) |
|
|
|