mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
[kutil] Rename 'size' to 'order' when meaning 2^N
Heap allocator is a buddy allocator that deals with power-of-two block sizes. Previously it referred to both a number of bytes and an order of magnitude as a 'size'. Rename functions and variables referring to orders of magnitude to 'order'. Tags: pedantry
This commit is contained in:
@@ -7,15 +7,15 @@ namespace kutil {
|
||||
|
||||
struct heap_allocator::mem_header
|
||||
{
|
||||
mem_header(mem_header *prev, mem_header *next, uint8_t size) :
|
||||
mem_header(mem_header *prev, mem_header *next, uint8_t order) :
|
||||
m_prev(prev), m_next(next)
|
||||
{
|
||||
set_size(size);
|
||||
set_order(order);
|
||||
}
|
||||
|
||||
inline void set_size(uint8_t size) {
|
||||
inline void set_order(uint8_t order) {
|
||||
m_prev = reinterpret_cast<mem_header *>(
|
||||
reinterpret_cast<uintptr_t>(prev()) | (size & 0x3f));
|
||||
reinterpret_cast<uintptr_t>(prev()) | (order & 0x3f));
|
||||
}
|
||||
|
||||
inline void set_used(bool used) {
|
||||
@@ -30,9 +30,9 @@ struct heap_allocator::mem_header
|
||||
}
|
||||
|
||||
inline void set_prev(mem_header *prev) {
|
||||
uint8_t s = size();
|
||||
uint8_t s = order();
|
||||
m_prev = prev;
|
||||
set_size(s);
|
||||
set_order(s);
|
||||
}
|
||||
|
||||
void remove() {
|
||||
@@ -47,12 +47,12 @@ struct heap_allocator::mem_header
|
||||
|
||||
inline mem_header * buddy() const {
|
||||
return reinterpret_cast<mem_header *>(
|
||||
reinterpret_cast<uintptr_t>(this) ^ (1 << size()));
|
||||
reinterpret_cast<uintptr_t>(this) ^ (1 << order()));
|
||||
}
|
||||
|
||||
inline bool eldest() const { return this < buddy(); }
|
||||
|
||||
inline uint8_t size() const { return reinterpret_cast<uintptr_t>(m_prev) & 0x3f; }
|
||||
inline uint8_t order() const { return reinterpret_cast<uintptr_t>(m_prev) & 0x3f; }
|
||||
inline bool used() const { return reinterpret_cast<uintptr_t>(m_next) & 0x1; }
|
||||
|
||||
private:
|
||||
@@ -73,16 +73,17 @@ void *
|
||||
heap_allocator::allocate(size_t length)
|
||||
{
|
||||
size_t total = length + sizeof(mem_header);
|
||||
unsigned size = min_size;
|
||||
while (total > (1 << size)) size++;
|
||||
|
||||
kassert(size <= max_size, "Tried to allocate a block bigger than max_size");
|
||||
if (size > max_size)
|
||||
unsigned order = min_order;
|
||||
while (total > (1 << order)) order++;
|
||||
|
||||
kassert(order <= max_order, "Tried to allocate a block bigger than max_order");
|
||||
if (order > max_order)
|
||||
return nullptr;
|
||||
|
||||
mem_header *header = pop_free(size);
|
||||
mem_header *header = pop_free(order);
|
||||
header->set_used(true);
|
||||
m_allocated_size += (1 << size);
|
||||
m_allocated_size += (1 << order);
|
||||
return header + 1;
|
||||
}
|
||||
|
||||
@@ -94,66 +95,66 @@ heap_allocator::free(void *p)
|
||||
mem_header *header = reinterpret_cast<mem_header *>(p);
|
||||
header -= 1; // p points after the header
|
||||
header->set_used(false);
|
||||
m_allocated_size -= (1 << header->size());
|
||||
m_allocated_size -= (1 << header->order());
|
||||
|
||||
while (header->size() != max_size) {
|
||||
auto size = header->size();
|
||||
while (header->order() != max_order) {
|
||||
auto order = header->order();
|
||||
|
||||
mem_header *buddy = header->buddy();
|
||||
if (buddy->used() || buddy->size() != size)
|
||||
if (buddy->used() || buddy->order() != order)
|
||||
break;
|
||||
|
||||
if (get_free(size) == buddy)
|
||||
get_free(size) = buddy->next();
|
||||
if (get_free(order) == buddy)
|
||||
get_free(order) = buddy->next();
|
||||
|
||||
buddy->remove();
|
||||
|
||||
header = header->eldest() ? header : buddy;
|
||||
header->set_size(size + 1);
|
||||
header->set_order(order + 1);
|
||||
}
|
||||
|
||||
uint8_t size = header->size();
|
||||
header->set_next(get_free(size));
|
||||
get_free(size) = header;
|
||||
uint8_t order = header->order();
|
||||
header->set_next(get_free(order));
|
||||
get_free(order) = header;
|
||||
if (header->next())
|
||||
header->next()->set_prev(header);
|
||||
}
|
||||
|
||||
void
|
||||
heap_allocator::ensure_block(unsigned size)
|
||||
heap_allocator::ensure_block(unsigned order)
|
||||
{
|
||||
if (get_free(size) != nullptr)
|
||||
if (get_free(order) != nullptr)
|
||||
return;
|
||||
|
||||
if (size == max_size) {
|
||||
size_t bytes = (1 << max_size);
|
||||
if (order == max_order) {
|
||||
size_t bytes = (1 << max_order);
|
||||
if (bytes <= m_size) {
|
||||
mem_header *next = reinterpret_cast<mem_header *>(m_next);
|
||||
new (next) mem_header(nullptr, nullptr, size);
|
||||
get_free(size) = next;
|
||||
new (next) mem_header(nullptr, nullptr, order);
|
||||
get_free(order) = next;
|
||||
m_next += bytes;
|
||||
m_size -= bytes;
|
||||
}
|
||||
} else {
|
||||
mem_header *orig = pop_free(size + 1);
|
||||
mem_header *orig = pop_free(order + 1);
|
||||
if (orig) {
|
||||
mem_header *next = kutil::offset_pointer(orig, 1 << size);
|
||||
new (next) mem_header(orig, nullptr, size);
|
||||
mem_header *next = kutil::offset_pointer(orig, 1 << order);
|
||||
new (next) mem_header(orig, nullptr, order);
|
||||
|
||||
orig->set_next(next);
|
||||
orig->set_size(size);
|
||||
get_free(size) = orig;
|
||||
orig->set_order(order);
|
||||
get_free(order) = orig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
heap_allocator::mem_header *
|
||||
heap_allocator::pop_free(unsigned size)
|
||||
heap_allocator::pop_free(unsigned order)
|
||||
{
|
||||
ensure_block(size);
|
||||
mem_header *block = get_free(size);
|
||||
ensure_block(order);
|
||||
mem_header *block = get_free(order);
|
||||
if (block) {
|
||||
get_free(size) = block->next();
|
||||
get_free(order) = block->next();
|
||||
block->remove();
|
||||
}
|
||||
return block;
|
||||
|
||||
@@ -29,32 +29,32 @@ public:
|
||||
/// \arg p A pointer previously retuned by allocate()
|
||||
virtual void free(void *p);
|
||||
|
||||
/// Minimum block size is (2^min_size). Must be at least 6.
|
||||
static const unsigned min_size = 6;
|
||||
/// Minimum block size is (2^min_order). Must be at least 6.
|
||||
static const unsigned min_order = 6;
|
||||
|
||||
/// Maximum block size is (2^max_size). Must be less than 64.
|
||||
static const unsigned max_size = 22;
|
||||
/// Maximum block size is (2^max_order). Must be less than 64.
|
||||
static const unsigned max_order = 22;
|
||||
|
||||
protected:
|
||||
class mem_header;
|
||||
|
||||
/// Ensure there is a block of a given size, recursively splitting
|
||||
/// \arg size Size category of the block we want
|
||||
void ensure_block(unsigned size);
|
||||
/// Ensure there is a block of a given order, recursively splitting
|
||||
/// \arg order Order (2^N) of the block we want
|
||||
void ensure_block(unsigned order);
|
||||
|
||||
/// Helper accessor for the list of blocks of a given size
|
||||
/// \arg size Size category of the block we want
|
||||
/// Helper accessor for the list of blocks of a given order
|
||||
/// \arg order Order (2^N) of the block we want
|
||||
/// \returns A mutable reference to the head of the list
|
||||
mem_header *& get_free(unsigned size) { return m_free[size - min_size]; }
|
||||
mem_header *& get_free(unsigned order) { return m_free[order - min_order]; }
|
||||
|
||||
/// Helper to get a block of the given size, growing if necessary
|
||||
/// \arg size Size category of the block we want
|
||||
/// \returns A detached block of the given size
|
||||
mem_header * pop_free(unsigned size);
|
||||
/// Helper to get a block of the given order, growing if necessary
|
||||
/// \arg order Order (2^N) of the block we want
|
||||
/// \returns A detached block of the given order
|
||||
mem_header * pop_free(unsigned order);
|
||||
|
||||
uintptr_t m_next;
|
||||
size_t m_size;
|
||||
mem_header *m_free[max_size - min_size + 1];
|
||||
mem_header *m_free[max_order - min_order + 1];
|
||||
size_t m_allocated_size;
|
||||
|
||||
heap_allocator(const heap_allocator &) = delete;
|
||||
|
||||
Reference in New Issue
Block a user