207 lines
4.8 KiB
C
207 lines
4.8 KiB
C
// TODO: Elminiate meta arena. Just store levels in first 4096 bytes of buddy arena, and then zone header data at the beginning of each allocation.
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Buddy context
|
|
|
|
BuddyCtx *AcquireBuddyCtx(u64 reserve)
|
|
{
|
|
// TODO: Determine meta reserve dynamically
|
|
Arena *meta_arena = AcquireArena(Gibi(64));
|
|
BuddyCtx *ctx = PushStruct(meta_arena, BuddyCtx);
|
|
ctx->meta_arena = meta_arena;
|
|
ctx->data_arena = AcquireArena(reserve);
|
|
|
|
// TODO: Minimum block size
|
|
ctx->levels = PushStructs(ctx->meta_arena, BuddyLevel, 64);
|
|
for (u64 i = 0; i < 64; ++i)
|
|
{
|
|
BuddyLevel *level = &ctx->levels[i];
|
|
level->ctx = ctx;
|
|
level->tier = i;
|
|
level->size = (u64)1 << i;
|
|
}
|
|
|
|
return ctx;
|
|
}
|
|
|
|
void ReleaseBuddyCtx(BuddyCtx *ctx)
|
|
{
|
|
ReleaseArena(ctx->data_arena);
|
|
ReleaseArena(ctx->meta_arena);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Buddy block
|
|
|
|
BuddyBlock *GetUnusedBuddyBlock(BuddyCtx *ctx, BuddyLevel *level)
|
|
{
|
|
BuddyBlock *block = 0;
|
|
|
|
// TODO: Tier oob check
|
|
if (level->first_unused_block)
|
|
{
|
|
block = level->first_unused_block;
|
|
level->first_unused_block = block->next;
|
|
if (level->first_unused_block)
|
|
{
|
|
level->first_unused_block->prev = 0;
|
|
}
|
|
block->is_used = 1;
|
|
block->next = 0;
|
|
}
|
|
else
|
|
{
|
|
if (level->backed)
|
|
{
|
|
BuddyLevel *parent_level = &ctx->levels[level->tier + 1];
|
|
BuddyBlock *parent_block = GetUnusedBuddyBlock(ctx, parent_level);
|
|
|
|
// Create left (used) block from parent block
|
|
BuddyBlock *left = PushBuddyBlock(ctx);
|
|
left->is_used = 1;
|
|
left->level = level;
|
|
left->parent = parent_block;
|
|
left->memory = parent_block->memory;
|
|
|
|
// Create right (unused) block from parent block
|
|
BuddyBlock *right = PushBuddyBlock(ctx);
|
|
right->is_used = 0;
|
|
right->level = level;
|
|
right->parent = parent_block;
|
|
right->memory = left->memory + level->size;
|
|
if (level->first_unused_block)
|
|
{
|
|
right->next = level->first_unused_block;
|
|
level->first_unused_block->prev = right;
|
|
}
|
|
level->first_unused_block = right;
|
|
|
|
left->sibling = right;
|
|
right->sibling = left;
|
|
block = left;
|
|
}
|
|
else
|
|
{
|
|
Arena *arena = ctx->data_arena;
|
|
|
|
// Grow arena
|
|
i64 level_commit_diff = (level->size * 2) - arena->pos;
|
|
if (level_commit_diff > 0)
|
|
{
|
|
PushStructsNoZero(arena, u8, level_commit_diff);
|
|
Assert(arena->pos == (level->size * 2));
|
|
}
|
|
|
|
// Create left (used) block from existing child block memory
|
|
BuddyBlock *left = PushBuddyBlock(ctx);
|
|
left->is_used = 1;
|
|
left->level = level;
|
|
left->memory = ArenaFirst(arena, u8);
|
|
|
|
// Create right (unused) block from new arena memory
|
|
BuddyBlock *right = PushBuddyBlock(ctx);
|
|
right->is_used = 0;
|
|
right->level = level;
|
|
right->memory = left->memory + level->size;
|
|
|
|
left->sibling = right;
|
|
right->sibling = left;
|
|
block = left;
|
|
|
|
level->backed = 1;
|
|
}
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
BuddyBlock *PushBuddyBlock(BuddyCtx *ctx)
|
|
{
|
|
BuddyBlock *block;
|
|
if (ctx->first_free_block)
|
|
{
|
|
block = ctx->first_free_block;
|
|
ctx->first_free_block = block->next;
|
|
}
|
|
else
|
|
{
|
|
block = PushStructNoZero(ctx->meta_arena, BuddyBlock);
|
|
}
|
|
ZeroStruct(block);
|
|
return block;
|
|
}
|
|
|
|
void PopBuddyBlock(BuddyCtx *ctx, BuddyLevel *level, BuddyBlock *block)
|
|
{
|
|
// Remove from unused list
|
|
{
|
|
BuddyBlock *prev = block->prev;
|
|
BuddyBlock *next = block->next;
|
|
if (prev)
|
|
{
|
|
prev->next = next;
|
|
}
|
|
else
|
|
{
|
|
level->first_unused_block = next;
|
|
}
|
|
if (next)
|
|
{
|
|
next->prev = prev;
|
|
}
|
|
}
|
|
block->next = ctx->first_free_block;
|
|
ctx->first_free_block = block;
|
|
}
|
|
|
|
BuddyBlock *AcquireBuddyBlock(BuddyCtx *ctx, u64 size)
|
|
{
|
|
if (size > 0x00FFFFFFFFFFFFFFULL)
|
|
{
|
|
// TODO: Error
|
|
Assert(0);
|
|
}
|
|
|
|
// TODO: Minimum block size
|
|
|
|
// TODO: Faster MSB calculation
|
|
|
|
u64 desired_block_size = 1;
|
|
u64 desired_level_tier = 0;
|
|
while (desired_block_size < size && desired_level_tier < 64)
|
|
{
|
|
desired_block_size <<= 1;
|
|
++desired_level_tier;
|
|
}
|
|
|
|
BuddyBlock *block = GetUnusedBuddyBlock(ctx, &ctx->levels[desired_level_tier]);
|
|
|
|
return block;
|
|
}
|
|
|
|
void ReleaseBuddyBlock(BuddyBlock *block)
|
|
{
|
|
block->is_used = 0;
|
|
BuddyLevel *level = block->level;
|
|
BuddyBlock *parent = block->parent;
|
|
BuddyBlock *sibling = block->sibling;
|
|
if (!sibling->is_used && parent != 0)
|
|
{
|
|
// Merge siblings
|
|
BuddyCtx *ctx = level->ctx;
|
|
PopBuddyBlock(ctx, level, block);
|
|
PopBuddyBlock(ctx, level, sibling);
|
|
// Release parent
|
|
ReleaseBuddyBlock(parent);
|
|
}
|
|
else
|
|
{
|
|
if (level->first_unused_block)
|
|
{
|
|
block->next = level->first_unused_block;
|
|
level->first_unused_block->prev = block;
|
|
}
|
|
level->first_unused_block = block;
|
|
}
|
|
}
|