Files
cAI/cKit/Memory/c_Buddy.c
T

170 lines
5.6 KiB
C
Raw Normal View History

2026-08-10 01:21:15 +08:00
#include <c_Buddy.h>
#include <c_Alignment.h>
#include <c_Memory.h>
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
C_STATIC_FORCE_INLINE
c_bool_t is_power_of_two(c_size_t x) {
return (x & (x-1)) == 0;
}
C_STATIC_FORCE_INLINE
c_BuddyBlock_t* buddy_block_next(c_BuddyBlock_t* block) {
return (c_BuddyBlock_t*)((uint8_t*)block + block->size);
}
static c_BuddyBlock_t *buddy_block_split(c_BuddyBlock_t *block, const int size) {
if (block != NULL && size != 0) {
// 當目標大小小於當前區塊時,進行對半裂變
while (size < block->size) {
int sz = block->size >> 1;
block->size = sz;
// 裂變出的右半邊(Right Buddy)設為空閒
c_BuddyBlock_t* right_buddy = buddy_block_next(block);
right_buddy->size = sz;
right_buddy->is_free = C_TRUE;
// 經典夥伴系統優先分配左半邊(Left Buddy),因此 block 指針保持在左側繼續循環
}
if (size <= block->size) {
return block;
}
}
return NULL;
}
static
c_BuddyBlock_t *buddy_block_find_best(c_BuddyBlock_t *head, c_BuddyBlock_t *tail, int size) {
c_BuddyBlock_t *best_block = NULL;
c_BuddyBlock_t *curr = head;
while (curr < tail) {
if (curr->is_free && curr->size >= size) {
// 尋找符合條件且最小的區塊(Best Fit),減少外部碎片
if (best_block == NULL || curr->size < best_block->size) {
best_block = curr;
}
}
// 精確前進當前區塊的實際大小,不論其內部被切得多碎,都能地毯式掃描
curr = buddy_block_next(curr);
}
if (best_block != NULL) {
return buddy_block_split(best_block, size);
}
return NULL;
}
C_STATIC_FORCE_INLINE
int buddy_block_size_required(c_Buddy_t *b, int size) {
int actual_size = b->alignment;
const int total_needed = size + b->alignment; // 預留足夠空間確保 padding 後依然夠用
while (actual_size < total_needed) {
actual_size <<= 1;
}
return actual_size;
}
static
void buddy_block_coalescence(c_BuddyBlock_t *head, c_BuddyBlock_t *tail) {
for (;;) {
c_BuddyBlock_t *curr = head;
c_bool_t consolidated_any = C_FALSE;
while (curr < tail) {
c_BuddyBlock_t *next = buddy_block_next(curr);
// 檢查安全邊界
if (next >= tail) {
break;
}
// 凝聚條件:兩相鄰區塊皆空閒,且大小相等
// 注意:在夥伴系統中,左夥伴的偏移量必須是其兩倍大小的整數倍(對齊檢查)
if (curr->is_free && next->is_free && curr->size == next->size) {
const c_uintptr_t offset = (c_uintptr_t)curr - (c_uintptr_t)head;
if ((offset % (curr->size << 1)) == 0) {
curr->size <<= 1; // 融合!容量翻倍
consolidated_any = C_TRUE;
// 融合後,下一次推進會自動從融合後的大區塊末端繼續,不用前進 next
continue;
}
}
// 若無法融合,正常步進到下一個區塊
curr = next;
}
// 如果整輪掃描下來沒有任何區塊可以再融合,代表凝聚完成,退出循環
if (!consolidated_any) {
break;
}
}
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
void c_Buddy_Init(c_Buddy_t *b, void *data, int size, int alignment) {
assert(b != NULL);
assert(data != NULL);
assert(is_power_of_two(size) && "size is not a power-of-two");
assert(is_power_of_two(alignment) && "alignment is not a power-of-two");
// 確保對齊基準至少能放下一個標頭結構體
if (alignment < (int)sizeof(c_BuddyBlock_t)) {
alignment = C_ALIGN_UPB(sizeof(c_BuddyBlock_t), 2); // 向上取 2 的冪次
while (!is_power_of_two(alignment)) {
alignment++; // 穩健保險
}
}
assert((c_uintptr_t)data % alignment == 0 && "data is not aligned to minimum alignment");
b->alignment = alignment;
b->head = (c_BuddyBlock_t *)data;
b->head->size = size;
b->head->is_free = C_TRUE;
// 哨兵結尾
b->tail = buddy_block_next(b->head);
}
void *c_Buddy_Alloc(c_Buddy_t *b, int size) {
if (!b || size <= 0) return NULL;
int actual_size = buddy_block_size_required(b, size);
// 第一階段:尋找現有最合適的空閒塊
c_BuddyBlock_t *found = buddy_block_find_best(b->head, b->tail, actual_size);
if (found == NULL) {
// 第二階段:若找不到,強制進行全面碎片凝聚(Coalesce),再搜尋一次
buddy_block_coalescence(b->head, b->tail);
found = buddy_block_find_best(b->head, b->tail, actual_size);
}
if (found != NULL) {
found->is_free = C_FALSE;
return (void *)((uint8_t *)found + b->alignment);
}
return NULL; // OOM
}
void c_Buddy_Free(c_Buddy_t *b, void *data) {
if (data != NULL) {
assert((c_uintptr_t)b->head <= (c_uintptr_t)data);
assert((c_uintptr_t)data < (c_uintptr_t)b->tail);
c_BuddyBlock_t *block = (c_BuddyBlock_t *) ((uint8_t *) data - b->alignment);
block->is_free = C_TRUE;
// NOTE: Coalescence could be done now but it is optional
// buddy_block_coalescence(b->head, b->tail);
}
}