/* 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 ctx BuddyCtx *AllocBuddyCtx(u64 reserve) { /* TODO: Determine meta reserve dynamically */ Arena *meta_arena = AllocArena(Gibi(64)); BuddyCtx *ctx = PushStruct(meta_arena, BuddyCtx); ctx->meta_arena = meta_arena; ctx->data_arena = AllocArena(reserve); /* TODO: Minimum block size */ ctx->levels = PushArray(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); } //////////////////////////////// //~ Block alloc / release BuddyBlock *AllocBuddyBlock(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; } } //////////////////////////////// //~ Block push / pop //- Push 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); } MEMZERO_STRUCT(block); return block; } //- Pop 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; } //////////////////////////////// //~ Get unused 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) { PushArrayNoZero(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 = ArenaBase(arena); /* 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; }