开始设计
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
#include <c_Alignment.h>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef INCLUDED_C_ALIGNMENT_H
|
||||
#define INCLUDED_C_ALIGNMENT_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
typedef union {
|
||||
#ifdef C_ALIGN_MAX_SIZE
|
||||
char pad[C_ALIGN_MAX_SIZE];
|
||||
#else
|
||||
int i;
|
||||
long l;
|
||||
long *lp;
|
||||
void *p;
|
||||
void (*fp)(void);
|
||||
float f;
|
||||
double d;
|
||||
long double ld;
|
||||
#endif
|
||||
} c_align_t;
|
||||
|
||||
#define C_ALIGN_SIZE sizeof(c_align_t)
|
||||
|
||||
#endif /*INCLUDED_C_ALIGNMENT_H*/
|
||||
@@ -0,0 +1,102 @@
|
||||
#include <c_Arena.h>
|
||||
#include <assert.h>
|
||||
#include <c_Alignment.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_uintptr_t align_forward(c_uintptr_t ptr, c_size_t align) {
|
||||
c_uintptr_t p;
|
||||
c_uintptr_t a;
|
||||
c_uintptr_t modulo;
|
||||
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
p = ptr;
|
||||
a = (uintptr_t)align;
|
||||
// Same as (p % a) but faster as 'a' is a power of two
|
||||
modulo = p & (a-1);
|
||||
|
||||
if (modulo != 0) {
|
||||
// If 'p' address is not aligned, push the address to the
|
||||
// next value which is aligned
|
||||
p += a - modulo;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void *arena_alloc_align(c_Arena_t *a, c_size_t size, c_size_t align) {
|
||||
// Align 'curr_offset' forward to the specified alignment
|
||||
c_uintptr_t curr_ptr = (c_uintptr_t)a->buf + (c_uintptr_t)a->curr_offset;
|
||||
c_uintptr_t offset = align_forward(curr_ptr, align);
|
||||
offset -= (c_uintptr_t)a->buf; // Change to relative offset
|
||||
|
||||
// Check to see if the backing memory has space left
|
||||
if (offset+size <= a->buf_len) {
|
||||
void *ptr = &a->buf[offset];
|
||||
a->prev_offset = offset;
|
||||
a->curr_offset = offset+size;
|
||||
|
||||
// Zero new memory by default
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
// Return NULL if the arena is out of memory (or handle differently)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void *arena_resize_align(c_Arena_t *a, void *old_memory, c_size_t old_size, c_size_t new_size, c_size_t align) {
|
||||
uint8_t* old_mem = (uint8_t*)old_memory;
|
||||
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
if (old_mem == NULL || old_size == 0) {
|
||||
return arena_alloc_align(a, new_size, align);
|
||||
} else if (a->buf <= old_mem && old_mem < (a->buf+ a->buf_len)) {
|
||||
if (a->buf+a->prev_offset == old_mem) {
|
||||
a->curr_offset = a->prev_offset + new_size;
|
||||
if (new_size > old_size) {
|
||||
// Zero the new memory by default
|
||||
memset(&a->buf[a->curr_offset], 0, new_size-old_size);
|
||||
}
|
||||
return old_memory;
|
||||
} else {
|
||||
void *new_memory = arena_alloc_align(a, new_size, align);
|
||||
c_size_t copy_size = old_size < new_size ? old_size : new_size;
|
||||
// Copy across old memory to the new memory
|
||||
memmove(new_memory, old_memory, copy_size);
|
||||
return new_memory;
|
||||
}
|
||||
|
||||
} else {
|
||||
assert(0 && "Memory is out of bounds of the buffer in this arena");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
void c_Arena_Init(c_Arena_t* a, void* buf, c_size_t buf_len) {
|
||||
a->buf = buf;
|
||||
a->buf_len = buf_len;
|
||||
a->prev_offset = 0;
|
||||
a->curr_offset = 0;
|
||||
}
|
||||
|
||||
void *c_Arena_Alloc(c_Arena_t *a, c_size_t size) {
|
||||
return arena_alloc_align(a, size, C_ALIGN_SIZE);
|
||||
}
|
||||
|
||||
void *c_Arena_Resize(c_Arena_t *a, void *old_memory, c_size_t old_size, c_size_t new_size) {
|
||||
return arena_resize_align(a, old_memory, old_size, new_size, C_ALIGN_SIZE);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef INCLUDED_C_ARENA_H
|
||||
#define INCLUDED_C_ARENA_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
uint8_t* buf;
|
||||
c_size_t buf_len;
|
||||
c_size_t prev_offset;
|
||||
c_size_t curr_offset;
|
||||
}c_Arena_t;
|
||||
|
||||
typedef struct {
|
||||
c_Arena_t* arena;
|
||||
c_size_t prev_offset;
|
||||
c_size_t curr_offset;
|
||||
}c_ArenaTemp_t;
|
||||
|
||||
void c_Arena_Init(c_Arena_t* a, void* buf, c_size_t buf_len);
|
||||
|
||||
void *c_Arena_Alloc(c_Arena_t *a, c_size_t size);
|
||||
|
||||
void *c_Arena_Resize(c_Arena_t *a, void *old_memory, c_size_t old_size, c_size_t new_size);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_Arena_Destroy(c_Arena_t* a) {
|
||||
a->curr_offset = a->prev_offset = 0;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_Arena_Free(c_Arena_t *a, void *ptr) {
|
||||
C_UNUSED(a);
|
||||
C_UNUSED(ptr);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_ArenaTemp_t c_ArenaTemp_Begin(c_Arena_t *a) {
|
||||
c_ArenaTemp_t temp;
|
||||
temp.arena = a;
|
||||
temp.prev_offset = a->prev_offset;
|
||||
temp.curr_offset = a->prev_offset;
|
||||
return temp;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_ArenaTemp_End(c_ArenaTemp_t *a) {
|
||||
a->arena->curr_offset = a->curr_offset;
|
||||
a->arena->prev_offset = a->prev_offset;
|
||||
}
|
||||
|
||||
#endif /*INCLUDED_C_ARENA_H*/
|
||||
@@ -0,0 +1,132 @@
|
||||
#include "c_Arena.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "assert.h"
|
||||
|
||||
static
|
||||
void test_arena_basic_alloc() {
|
||||
printf("[測試] 基礎分配...\n");
|
||||
|
||||
uint8_t backing_buffer[1024];
|
||||
c_Arena_t arena;
|
||||
|
||||
// 初始化
|
||||
c_Arena_Init(&arena, backing_buffer, sizeof(backing_buffer));
|
||||
assert(arena.buf == backing_buffer);
|
||||
assert(arena.buf_len == 1024);
|
||||
assert(arena.curr_offset == 0);
|
||||
|
||||
// 第一次分配
|
||||
void* p1 = c_Arena_Alloc(&arena, 100);
|
||||
assert(p1 != NULL);
|
||||
assert(arena.curr_offset >= 100); // 考慮到對齊,可能大於等於 100
|
||||
|
||||
// 第二次分配
|
||||
void* p2 = c_Arena_Alloc(&arena, 200);
|
||||
assert(p2 != NULL);
|
||||
assert(p2 > p1); // 記憶體地址應是連續向後的
|
||||
|
||||
printf(" => 基礎分配測試成功\n");
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 測試用例 2:記憶體溢出(Out of Memory)
|
||||
// ==========================================
|
||||
static
|
||||
void test_arena_oom() {
|
||||
printf("[測試] 記憶體溢出邊界...\n");
|
||||
|
||||
uint8_t backing_buffer[256];
|
||||
c_Arena_t arena;
|
||||
c_Arena_Init(&arena, backing_buffer, sizeof(backing_buffer));
|
||||
|
||||
// 嘗試分配超出整塊 Arena 大小的記憶體
|
||||
void* p1 = c_Arena_Alloc(&arena, 300);
|
||||
assert(p1 == NULL); // 應返回 NULL
|
||||
|
||||
// 分配剛好極限的記憶體
|
||||
void* p2 = c_Arena_Alloc(&arena, 256);
|
||||
assert(p2 != NULL);
|
||||
|
||||
// 已經滿了,再次分配應失敗
|
||||
void* p3 = c_Arena_Alloc(&arena, 1);
|
||||
assert(p3 == NULL);
|
||||
|
||||
printf(" => OOM 邊界測試成功\n");
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 測試用例 3:Resize 原地擴展(最速路徑)
|
||||
// ==========================================
|
||||
// 說明:若舊記憶體剛好是最後一次分配的區塊(curr_offset 緊鄰它),
|
||||
// Arena 應該直接移動 curr_offset 實現原地擴展,而不需搬移資料。
|
||||
static
|
||||
void test_arena_resize_inplace() {
|
||||
printf("[測試] Resize 原地擴展(最後一個分配物)...\n");
|
||||
|
||||
uint8_t backing_buffer[1024];
|
||||
c_Arena_t arena;
|
||||
c_Arena_Init(&arena, backing_buffer, sizeof(backing_buffer));
|
||||
|
||||
// 分配 p1,並寫入測試資料
|
||||
char* p1 = (char*)c_Arena_Alloc(&arena, 10);
|
||||
strcpy(p1, "Hello");
|
||||
|
||||
c_size_t offset_before_resize = arena.curr_offset;
|
||||
|
||||
// 將 p1 擴大到 50 字節
|
||||
char* p1_new = (char*)c_Arena_Resize(&arena, p1, 10, 50);
|
||||
|
||||
// 驗證:因為 p1 是最後分配的,它的地址不應該改變(原地擴展)
|
||||
assert(p1_new == p1);
|
||||
assert(strcmp(p1_new, "Hello") == 0); // 資料必須完整保留
|
||||
assert(arena.curr_offset > offset_before_resize); // 偏移量正確推移
|
||||
|
||||
printf(" => Resize 原地擴展測試成功\n");
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 測試用例 4:Resize 重新分配(非最後分配物)
|
||||
// ==========================================
|
||||
// 說明:若舊記憶體後面已經有其他分配物(p1 後面有 p2),
|
||||
// 此時對 p1 做 Resize 必須當作全新分配,並拷貝舊資料。
|
||||
static
|
||||
void test_arena_resize_realloc() {
|
||||
printf("[測試] Resize 重新分配(中間的分配物)...\n");
|
||||
|
||||
uint8_t backing_buffer[1024];
|
||||
c_Arena_t arena;
|
||||
c_Arena_Init(&arena, backing_buffer, sizeof(backing_buffer));
|
||||
|
||||
// 連續分配 p1 與 p2
|
||||
char* p1 = (char*)c_Arena_Alloc(&arena, 16);
|
||||
strcpy(p1, "Data1");
|
||||
|
||||
char* p2 = (char*)c_Arena_Alloc(&arena, 16);
|
||||
strcpy(p2, "Data2");
|
||||
|
||||
// 對 p1(此時不是最後一個分配物)進行 Resize
|
||||
char* p1_new = (char*)c_Arena_Resize(&arena, p1, 16, 32);
|
||||
|
||||
// 驗證:
|
||||
assert(p1_new != NULL);
|
||||
assert(p1_new != p1); // 必須分配在 p2 之後的新位置
|
||||
assert(p1_new > p2); // 確保在新地址
|
||||
assert(strcmp(p1_new, "Data1") == 0); // 舊資料必須被 memcpy 過去
|
||||
assert(strcmp(p2, "Data2") == 0); // 隔壁的 p2 資料不能被破壞
|
||||
|
||||
printf(" => Resize 重新分配測試成功\n");
|
||||
}
|
||||
|
||||
int main(int argc, char** argv){
|
||||
printf("--- Test Begin ---\n");
|
||||
|
||||
test_arena_basic_alloc();
|
||||
test_arena_oom();
|
||||
test_arena_resize_inplace();
|
||||
test_arena_resize_realloc();
|
||||
|
||||
printf("--- Test End! ---\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef INCLUDED_C_BUDDY_H
|
||||
#define INCLUDED_C_BUDDY_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
int size;
|
||||
c_bool_t is_free;
|
||||
}c_BuddyBlock_t;
|
||||
|
||||
typedef struct {
|
||||
c_BuddyBlock_t* head;
|
||||
c_BuddyBlock_t* tail;
|
||||
int alignment;
|
||||
}c_Buddy_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
void c_Buddy_Init(c_Buddy_t *b, void *data, int size, int alignment);
|
||||
void *c_Buddy_Alloc(c_Buddy_t *b, int size);
|
||||
void c_Buddy_Free(c_Buddy_t *b, void *data);
|
||||
|
||||
#endif /*INCLUDED_C_BUDDY_H*/
|
||||
@@ -0,0 +1,245 @@
|
||||
#include "c_Buddy.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdalign.h> // C11 標準
|
||||
#include <c_Memory.h>
|
||||
|
||||
// 2. 測試用例函數宣告
|
||||
void test_buddy_basic_alloc();
|
||||
void test_buddy_alignment();
|
||||
void test_buddy_oom();
|
||||
void test_buddy_coalescing();
|
||||
void test_1024(void);
|
||||
|
||||
int main(int argc, char** argv){
|
||||
printf("--- 開始執行 c_Buddy_t 夥伴分配器測試 ---\n");
|
||||
|
||||
test_buddy_basic_alloc();
|
||||
test_buddy_alignment();
|
||||
test_buddy_oom();
|
||||
test_buddy_coalescing();
|
||||
test_1024();
|
||||
printf("--- 所有測試用例通過! ---\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void c_Buddy_PrintStatus(c_Buddy_t *b) {
|
||||
if (!b || !b->head || !b->tail) {
|
||||
printf("[Buddy] 錯誤:分配器尚未初始化或為空指標。\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\n=================== Buddy Allocator Status ===================\n");
|
||||
printf("總記憶體範圍: %p ~ %p\n", (void*)b->head, (void*)b->tail);
|
||||
printf("最小對齊基準 (Alignment): %d 位元組\n", b->alignment);
|
||||
printf("--------------------------------------------------------------\n");
|
||||
printf("%-5s | %-16s | %-12s | %-10s\n", "編號", "區塊記憶體位址", "區塊大小(Size)", "狀態");
|
||||
printf("--------------------------------------------------------------\n");
|
||||
|
||||
c_BuddyBlock_t *curr = b->head;
|
||||
int block_index = 0;
|
||||
int total_free = 0;
|
||||
int total_allocated = 0;
|
||||
|
||||
// 使用與之前相同的單指針步進遍歷
|
||||
while (curr < b->tail) {
|
||||
printf("[%3d] | %p | %-12d(%dKB) | %s\n",
|
||||
block_index++,
|
||||
(void*)curr,
|
||||
curr->size,
|
||||
curr->size/1024,
|
||||
curr->is_free ? "FREE (空閒)" : "ALLOCATED (已佔用)");
|
||||
|
||||
if (curr->is_free) {
|
||||
total_free += curr->size;
|
||||
} else {
|
||||
total_allocated += curr->size;
|
||||
}
|
||||
|
||||
// 精確前進到下一個區塊
|
||||
curr = (c_BuddyBlock_t *)((uint8_t *)curr + curr->size);
|
||||
}
|
||||
|
||||
printf("--------------------------------------------------------------\n");
|
||||
printf("摘要統計:已使用: %d 總位元組 | 剩餘空閒: %d 總位元組\n", total_allocated, total_free);
|
||||
printf("==============================================================\n\n");
|
||||
}
|
||||
|
||||
#define KB 1024
|
||||
|
||||
void test_1024(void) {
|
||||
void* block = c_Memory_AlignedAlloc(1024*KB, 8);
|
||||
|
||||
c_Buddy_t buddy;
|
||||
c_Buddy_Init(&buddy, block, 1024*KB, 8);
|
||||
|
||||
printf("Step 1: Alloc A=70KB\n");
|
||||
void* A = c_Buddy_Alloc(&buddy, 70*KB);
|
||||
c_Buddy_PrintStatus(&buddy);
|
||||
|
||||
printf("Step 2: Alloc B=35KB\n");
|
||||
void* B = c_Buddy_Alloc(&buddy, 35*KB);
|
||||
c_Buddy_PrintStatus(&buddy);
|
||||
|
||||
printf("Step 3: Alloc C=80KB\n");
|
||||
void* C = c_Buddy_Alloc(&buddy, 80*KB);
|
||||
c_Buddy_PrintStatus(&buddy);
|
||||
|
||||
printf("Step 4: Free A\n");
|
||||
c_Buddy_Free(&buddy, A);
|
||||
c_Buddy_PrintStatus(&buddy);
|
||||
|
||||
printf("Step 5: Alloc D=60KB\n");
|
||||
void* D = c_Buddy_Alloc(&buddy, 60*KB);
|
||||
c_Buddy_PrintStatus(&buddy);
|
||||
|
||||
printf("Step 6: Free B\n");
|
||||
c_Buddy_Free(&buddy, B);
|
||||
c_Buddy_PrintStatus(&buddy);
|
||||
|
||||
printf("Step 7: Free D\n");
|
||||
c_Buddy_Free(&buddy, D);
|
||||
c_Buddy_PrintStatus(&buddy);
|
||||
|
||||
printf("Step 8: Free C\n");
|
||||
c_Buddy_Free(&buddy, C);
|
||||
c_Buddy_PrintStatus(&buddy);
|
||||
|
||||
printf("Step 9: Alloc E=600KB\n");
|
||||
void* E = c_Buddy_Alloc(&buddy, 600*KB); // 实际分配 1024KB
|
||||
c_Buddy_PrintStatus(&buddy);
|
||||
|
||||
printf("Step 10: Free E\n");
|
||||
c_Buddy_Free(&buddy, E);
|
||||
c_Buddy_PrintStatus(&buddy);
|
||||
|
||||
c_Memory_AlignedFree(block);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 測試用例 1:基礎初始化與 2 的冪次方裂變分配
|
||||
// ==========================================
|
||||
void test_buddy_basic_alloc() {
|
||||
printf("[測試] 基礎裂變分配...\n");
|
||||
|
||||
// 建立 1024 位元組的底層記憶體,對齊設為 8
|
||||
uint8_t* backing_buffer = (uint8_t*)malloc(1024);
|
||||
c_Buddy_t buddy;
|
||||
|
||||
c_Buddy_Init(&buddy, backing_buffer, 1024, 8);
|
||||
assert(buddy.alignment == 8);
|
||||
|
||||
// 請求分配 120 位元組(夥伴系統通常會向上對齊到 128 或包含 Block Header 的 2 的冪次方)
|
||||
void* p1 = c_Buddy_Alloc(&buddy, 120);
|
||||
assert(p1 != NULL);
|
||||
|
||||
// 再次分配相同大小,地址應當緊鄰第一個區塊(按 2 冪次步進)
|
||||
void* p2 = c_Buddy_Alloc(&buddy, 120);
|
||||
assert(p2 != NULL);
|
||||
assert(p2 > p1);
|
||||
|
||||
// 清理記憶體池內部分配
|
||||
c_Buddy_Free(&buddy, p1);
|
||||
c_Buddy_Free(&buddy, p2);
|
||||
free(backing_buffer);
|
||||
printf(" => 基礎分配測試成功\n");
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 測試用例 2:記憶體對齊(Alignment)邊界驗證
|
||||
// ==========================================
|
||||
void test_buddy_alignment() {
|
||||
printf("[測試] 記憶體對齊要求...\n");
|
||||
|
||||
uint8_t* backing_buffer = (uint8_t*)c_Memory_AlignedAlloc(2048, 64);
|
||||
c_Buddy_t buddy;
|
||||
|
||||
// 強制對齊要求為 64 位元組
|
||||
c_Buddy_Init(&buddy, backing_buffer, 2048, 64);
|
||||
|
||||
void* p1 = c_Buddy_Alloc(&buddy, 100);
|
||||
void* p2 = c_Buddy_Alloc(&buddy, 100);
|
||||
|
||||
assert(p1 != NULL);
|
||||
assert(p2 != NULL);
|
||||
|
||||
// 驗證返回的記憶體地址是否符合 64 位元組對齊
|
||||
assert(((uintptr_t)p1 % 64) == 0);
|
||||
assert(((uintptr_t)p2 % 64) == 0);
|
||||
|
||||
c_Buddy_Free(&buddy, p1);
|
||||
c_Buddy_Free(&buddy, p2);
|
||||
c_Memory_AlignedFree(backing_buffer);
|
||||
printf(" => 對齊邊界測試成功\n");
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 測試用例 3:記憶體溢出(OOM)
|
||||
// ==========================================
|
||||
void test_buddy_oom() {
|
||||
printf("[測試] 記憶體溢出邊界...\n");
|
||||
|
||||
uint8_t* backing_buffer = (uint8_t*)malloc(512);
|
||||
c_Buddy_t buddy;
|
||||
c_Buddy_Init(&buddy, backing_buffer, 512, 8);
|
||||
|
||||
// 嘗試分配超出整塊夥伴系統大小的記憶體
|
||||
void* p1 = c_Buddy_Alloc(&buddy, 600);
|
||||
assert(p1 == NULL);
|
||||
|
||||
// 分配剛好極限的記憶體(需扣除或包含 Header 空間,依實作而定)
|
||||
void* p2 = c_Buddy_Alloc(&buddy, 256);
|
||||
assert(p2 != NULL);
|
||||
|
||||
void* p3 = c_Buddy_Alloc(&buddy, 256);
|
||||
// 由於 512 已被完全填滿,此時應觸發 OOM
|
||||
void* p4 = c_Buddy_Alloc(&buddy, 8);
|
||||
assert(p4 == NULL);
|
||||
|
||||
if (p2) c_Buddy_Free(&buddy, p2);
|
||||
if (p3) c_Buddy_Free(&buddy, p3);
|
||||
free(backing_buffer);
|
||||
printf(" => OOM 邊界測試成功\n");
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 測試用例 4:核心邏輯——夥伴自動合併(Coalescing)
|
||||
// ==========================================
|
||||
// 說明:當 A 和 B 互為夥伴區塊,且 A 分配出去後,若 A 與 B 均被釋放,
|
||||
// 夥伴系統必須將兩者合併回原本更大的 2 的冪次方區塊。
|
||||
void test_buddy_coalescing() {
|
||||
printf("[測試] 夥伴區塊自動合併(Coalescing)...\n");
|
||||
|
||||
uint8_t* backing_buffer = (uint8_t*)malloc(1024);
|
||||
c_Buddy_t buddy;
|
||||
// 初始化一個 1024 總大小的區塊
|
||||
c_Buddy_Init(&buddy, backing_buffer, 1024, 8);
|
||||
|
||||
// 1. 將其切碎:連續分配 4 個 256 的空間(假設總空間剛好能切 4 塊)
|
||||
void* p1 = c_Buddy_Alloc(&buddy, 200); // 裂變為 256
|
||||
void* p2 = c_Buddy_Alloc(&buddy, 200); // 裂變為 256
|
||||
void* p3 = c_Buddy_Alloc(&buddy, 200); // 裂變為 256
|
||||
void* p4 = c_Buddy_Alloc(&buddy, 200); // 裂變為 256
|
||||
|
||||
assert(p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL);
|
||||
|
||||
// 2. 此時剩餘空間為 0,嘗試分配 512 必然失敗
|
||||
void* p_large_fail = c_Buddy_Alloc(&buddy, 500);
|
||||
assert(p_large_fail == NULL);
|
||||
|
||||
// 3. 釋放相鄰的夥伴 p1 與 p2
|
||||
c_Buddy_Free(&buddy, p1);
|
||||
c_Buddy_Free(&buddy, p2);
|
||||
|
||||
// 4. 關鍵驗證:如果實作了自動合併,p1 和 p2 釋放後應融合成一個 512 的大區塊
|
||||
// 此時再次申請 512 位元組(要求大小約 500),應該要能分配成功!
|
||||
void* p_large_success = c_Buddy_Alloc(&buddy, 500);
|
||||
assert(p_large_success != NULL);
|
||||
|
||||
// 清理剩餘記憶體
|
||||
c_Buddy_Free(&buddy, p_large_success);
|
||||
c_Buddy_Free(&buddy, p3);
|
||||
c_Buddy_Free(&buddy, p4);
|
||||
free(backing_buffer);
|
||||
printf(" => 夥伴自動合併測試成功\n");
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#include <c_FixedPool.h>
|
||||
#include <c_Alignment.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_err_t c_FixedPool_Replenish(c_FixedPool_t* self, void* block, int block_size){
|
||||
if (!self || !block || block_size < self->objSize) return C_ERR_PARAM;
|
||||
|
||||
const c_size_t chunk_size = block_size/self->objSize;
|
||||
uint8_t* start = (uint8_t*)block;
|
||||
|
||||
uint8_t* last = &start[(chunk_size - 1) * self->objSize];
|
||||
for (uint8_t* p = start; p<last; p+=self->objSize) {
|
||||
((struct c_FixedPoolLink_t*)p)->next = (struct c_FixedPoolLink_t*)(p + self->objSize);
|
||||
}
|
||||
((struct c_FixedPoolLink_t*)last)->next = self->freelist;
|
||||
self->freelist = (struct c_FixedPoolLink_t*)start;
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_FixedPool_Init(c_FixedPool_t* self, int objSize, void* block, int block_size) {
|
||||
if (!self || objSize==0 || !block || block_size < objSize) return C_ERR_PARAM;
|
||||
|
||||
self->objSize = objSize>=sizeof(struct c_FixedPoolLink_t)?objSize:sizeof(struct c_FixedPoolLink_t);
|
||||
self->objSize = C_ALIGN_UPB(self->objSize, C_ALIGN_SIZE);
|
||||
self->instanceCount = 0;
|
||||
self->freelist = 0;
|
||||
return c_FixedPool_Replenish(self, block, block_size);
|
||||
}
|
||||
|
||||
void c_FixedPool_Destroy(c_FixedPool_t* self) {
|
||||
if (!self) return;
|
||||
|
||||
if (0==self->instanceCount) {
|
||||
self->freelist = 0;
|
||||
}
|
||||
assert(0==self->instanceCount);
|
||||
}
|
||||
|
||||
c_err_t c_FixedPool_AddBlock(c_FixedPool_t* self, void* block, int block_size) {
|
||||
return c_FixedPool_Replenish(self, block, block_size);
|
||||
}
|
||||
|
||||
void* c_FixedPool_Alloc(c_FixedPool_t* self) {
|
||||
if (!self || !self->freelist) {
|
||||
return NULL;
|
||||
}
|
||||
struct c_FixedPoolLink_t* p = self->freelist;
|
||||
self->freelist = p->next;
|
||||
++self->instanceCount;
|
||||
return p;
|
||||
}
|
||||
|
||||
void c_FixedPool_Free(c_FixedPool_t* self, void* ptr) {
|
||||
if (!self || !ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct c_FixedPoolLink_t* p = (struct c_FixedPoolLink_t*)ptr;
|
||||
p->next = self->freelist;
|
||||
self->freelist = p;
|
||||
--self->instanceCount;
|
||||
}
|
||||
|
||||
void c_FixedPool_DryUp(c_FixedPool_t* self) {
|
||||
if (!self) return;
|
||||
self->freelist = 0;
|
||||
self->instanceCount = 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef INCLUDED_C_FIXEDPOOL_H
|
||||
#define INCLUDED_C_FIXEDPOOL_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
int objSize;
|
||||
c_size_t instanceCount;
|
||||
struct c_FixedPoolLink_t{ struct c_FixedPoolLink_t* next;} * freelist;
|
||||
}c_FixedPool_t;
|
||||
|
||||
c_err_t c_FixedPool_Init(c_FixedPool_t* self, int objSize, void* block, int block_size);
|
||||
void c_FixedPool_Destroy(c_FixedPool_t* self);
|
||||
c_err_t c_FixedPool_AddBlock(c_FixedPool_t* self, void* block, int block_size);
|
||||
void* c_FixedPool_Alloc(c_FixedPool_t* self);
|
||||
void c_FixedPool_Free(c_FixedPool_t* self, void* ptr);
|
||||
void c_FixedPool_DryUp(c_FixedPool_t* self);
|
||||
|
||||
#endif /*INCLUDED_C_FIXEDPOOL_H*/
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "c_FixedPool.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
char name[12];
|
||||
} TestObject;
|
||||
|
||||
int main(int argc, char** argv){
|
||||
|
||||
printf("--- 開始執行 c_FixedPool_t 測試 ---\n");
|
||||
|
||||
// 建立兩個獨立的記憶體緩衝區
|
||||
uint8_t block1[32]; // 約可容納 2 個 TestObject (16 bytes * 2 = 32, 考慮對齊足夠)
|
||||
uint8_t block2[32];
|
||||
|
||||
c_FixedPool_t pool;
|
||||
|
||||
// 1. 初始化並掛載第一個區塊
|
||||
c_err_t err = c_FixedPool_Init(&pool, sizeof(TestObject), block1, sizeof(block1));
|
||||
assert(err == C_ERR_SUCCESS);
|
||||
printf("初始化成功,當前可用總數: %zu\n", pool.instanceCount);
|
||||
|
||||
// 2. 持續分配直到第一個區塊耗盡
|
||||
void* p1 = c_FixedPool_Alloc(&pool);
|
||||
void* p2 = c_FixedPool_Alloc(&pool);
|
||||
void* p3 = c_FixedPool_Alloc(&pool); // 超出 block1 的容量,應為 NULL
|
||||
|
||||
assert(p1 != NULL);
|
||||
assert(p2 != NULL);
|
||||
assert(p3 == NULL);
|
||||
printf("第一階段分配完畢,池已成功耗盡 (OOM 觸發)。\n");
|
||||
|
||||
// 3. 動態追加第二個記憶體區塊 (AddBlock)
|
||||
err = c_FixedPool_AddBlock(&pool, block2, sizeof(block2));
|
||||
assert(err == C_ERR_SUCCESS);
|
||||
printf("成功追加新區塊!當前可用總數增加。\n");
|
||||
|
||||
// 4. 再次分配,此時應該能成功從 block2 取得記憶體
|
||||
void* p4 = c_FixedPool_Alloc(&pool);
|
||||
assert(p4 != NULL);
|
||||
assert(p4 != p1 && p4 != p2);
|
||||
|
||||
// 5. 測試回收與重複利用
|
||||
c_FixedPool_Free(&pool, p1);
|
||||
void* p_reuse = c_FixedPool_Alloc(&pool);
|
||||
assert(p_reuse == p1); // 應優先拿回剛釋放的 p1 地址
|
||||
|
||||
// 6. 清理
|
||||
c_FixedPool_DryUp(&pool);
|
||||
c_FixedPool_Destroy(&pool);
|
||||
|
||||
printf("--- c_FixedPool_t 所有測試案例通過! ---\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <c_Memory.h>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#ifndef INCLUDED_C_MEMORY_H
|
||||
#define INCLUDED_C_MEMORY_H
|
||||
|
||||
#ifndef INCLUDED_STDLIB_H
|
||||
#define INCLUDED_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif /*INCLUDED_STDLIB_H*/
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALIGNMENT_H
|
||||
#include <c_Alignment.h>
|
||||
#endif /*INCLUDED_C_ALIGNMENT_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void* c_Memory_Alloc(c_size_t size) {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void* c_Memory_Realloc(void* ptr, c_size_t size) {
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void* c_Memory_Calloc(c_size_t count, c_size_t size) {
|
||||
return calloc(count, size);
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_Memory_Free(void* ptr) {
|
||||
if (ptr) free(ptr);
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void* c_Memory_AlignedAlloc(c_size_t size, c_size_t alignment) {
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
// Windows 環境下使用微軟特有的對齊配置函數
|
||||
return _aligned_malloc(size, alignment);
|
||||
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
// 真正的 C11 環境,且支援 aligned_alloc
|
||||
return aligned_alloc(alignment, size);
|
||||
#else
|
||||
// POSIX 環境 (Linux/macOS) 的備用方案
|
||||
void* ptr = NULL;
|
||||
if (posix_memalign(&ptr, alignment, size) != 0) return NULL;
|
||||
return ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_Memory_AlignedFree(void* ptr) {
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
free(ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
#define C_ALLOC(n) c_Memory_Alloc(n)
|
||||
#define C_REALLOC(p, n) c_Memory_Realloc((p), (n))
|
||||
#define C_CALLOC(x, n) c_Memory_Calloc((x), (n))
|
||||
#define C_FREE(p) do{if(p){c_Memory_Free(p); (p)=NULL;}}while(0)
|
||||
|
||||
#define C_RESIZE(p, n) (p)=C_REALLOC(p, n)
|
||||
|
||||
#define C_NEW(p) (p)=C_ALLOC(sizeof(*(p)))
|
||||
#define C_NEW0(p) (p)=C_CALLOC(1, sizeof(*(p)))
|
||||
|
||||
#endif /*INCLUDED_C_MEMORY_H*/
|
||||
@@ -0,0 +1,139 @@
|
||||
#include <c_Pool.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "c_Alignment.h"
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
#define FREE(x) do{ \
|
||||
if(x){ \
|
||||
free(x); \
|
||||
(x) = NULL; \
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
#define ALLOC(x) malloc(x)
|
||||
#define NEW(p) (p)=ALLOC(sizeof(*(p)))
|
||||
|
||||
#define DEFAULT_CHUNK_SIZE 10
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
typedef struct c_PoolBlockLink_t {
|
||||
void* block;
|
||||
struct c_PoolBlockLink_t* next;
|
||||
}c_PoolBlockLink_t;
|
||||
|
||||
struct c_PoolBlockList_t{
|
||||
c_PoolBlockLink_t* list;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_PoolBlockLink_Init(c_PoolBlockLink_t* self, void* p, c_PoolBlockLink_t* next) {
|
||||
self->block = p;
|
||||
self->next = next;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_PoolBlockList_Init(c_PoolBlockList_t* self) {
|
||||
self->list = NULL;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_PoolBlockList_Destroy(c_PoolBlockList_t* self) {
|
||||
if (!self) return;
|
||||
while (self->list){
|
||||
c_PoolBlockLink_t* q = self->list;
|
||||
self->list = q->next;
|
||||
FREE(q);
|
||||
}
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void* c_PoolBlockList_Alloc(c_PoolBlockList_t* self, c_size_t bytes) {
|
||||
c_size_t block_size = bytes + sizeof(c_PoolBlockLink_t);
|
||||
block_size = C_ALIGN_UPB(block_size, C_ALIGN_SIZE);
|
||||
c_PoolBlockLink_t* p = ALLOC(block_size);
|
||||
if (!p) {
|
||||
return NULL;
|
||||
}
|
||||
c_PoolBlockLink_Init(p, p+1, self->list);
|
||||
self->list = p;
|
||||
return p->block;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_err_t c_Pool_Replenish(c_Pool_t* self) {
|
||||
c_size_t size = self->chunkSize * self->objSize;
|
||||
uint8_t* start = (uint8_t*)c_PoolBlockList_Alloc(self->blockAllocator, size);
|
||||
if (!start) return C_ERR_NOMEM;
|
||||
uint8_t* last = &start[(self->chunkSize - 1) * self->objSize];
|
||||
for (uint8_t* p = start; p<last; p+=self->objSize) {
|
||||
((struct c_PoolLink_t*)p)->next = (struct c_PoolLink_t*)(p + self->objSize);
|
||||
}
|
||||
((struct c_PoolLink_t*)last)->next = NULL;
|
||||
self->freelist = (struct c_PoolLink_t*)start;
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_Pool_Init(c_Pool_t* self, int objSize, int chunkSize) {
|
||||
self->freelist = 0;
|
||||
self->objSize = objSize>=sizeof(struct c_PoolLink_t)?objSize:sizeof(struct c_PoolLink_t);
|
||||
self->chunkSize = (chunkSize > 0)?chunkSize:DEFAULT_CHUNK_SIZE;
|
||||
self->instanceCount = 0;
|
||||
NEW(self->blockAllocator);
|
||||
if (!self->blockAllocator) {
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
c_PoolBlockList_Init(self->blockAllocator);
|
||||
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void c_Pool_Destroy(c_Pool_t* self) {
|
||||
if (0==self->instanceCount) {
|
||||
c_PoolBlockList_Destroy(self->blockAllocator);
|
||||
FREE(self->blockAllocator);
|
||||
self->freelist = NULL;
|
||||
}
|
||||
assert(0==self->instanceCount);
|
||||
}
|
||||
|
||||
void* c_Pool_Alloc(c_Pool_t* self) {
|
||||
if (!self->freelist) {
|
||||
if (c_Pool_Replenish(self)!=C_ERR_SUCCESS) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
struct c_PoolLink_t* p = self->freelist;
|
||||
self->freelist = p->next;
|
||||
++self->instanceCount;
|
||||
return p;
|
||||
}
|
||||
|
||||
void c_Pool_Free(c_Pool_t* self, void* ptr) {
|
||||
if (!self || !ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct c_PoolLink_t* p = (struct c_PoolLink_t*)ptr;
|
||||
p->next = self->freelist;
|
||||
self->freelist = p;
|
||||
--self->instanceCount;
|
||||
}
|
||||
|
||||
void c_Pool_DryUp(c_Pool_t* self) {
|
||||
c_PoolBlockList_Destroy(self->blockAllocator);
|
||||
FREE(self->blockAllocator);
|
||||
self->instanceCount = 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef INCLUDED_C_POOL_H
|
||||
#define INCLUDED_C_POOL_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct c_PoolBlockList_t c_PoolBlockList_t;
|
||||
|
||||
typedef struct {
|
||||
c_PoolBlockList_t* blockAllocator;
|
||||
int objSize;
|
||||
int chunkSize;
|
||||
c_size_t instanceCount;
|
||||
struct c_PoolLink_t{ struct c_PoolLink_t* next;} * freelist;
|
||||
}c_Pool_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_Pool_Init(c_Pool_t* self, int objSize, int chunkSize);
|
||||
|
||||
void c_Pool_Destroy(c_Pool_t* self);
|
||||
|
||||
void* c_Pool_Alloc(c_Pool_t* self);
|
||||
|
||||
void c_Pool_Free(c_Pool_t* self, void* ptr);
|
||||
|
||||
void c_Pool_DryUp(c_Pool_t* self);
|
||||
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_POOL_H*/
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "c_Pool.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// 定義一個測試用的結構體(例如網路連線上下文)
|
||||
typedef struct {
|
||||
int connection_id;
|
||||
char ip[16];
|
||||
} ConnectionContext;
|
||||
|
||||
int main(int argc, char** argv){
|
||||
printf("--- 開始執行 c_Pool_t 記憶體池測試 ---\n");
|
||||
|
||||
// 建立一個足夠容納 3 個 ConnectionContext 的靜態緩衝區
|
||||
// 考慮到 64 位元對齊,ConnectionContext 佔 24 位元組 (4 + 16 補齊到 24)
|
||||
|
||||
c_Pool_t pool;
|
||||
c_Pool_Init(&pool, sizeof(ConnectionContext), 3);
|
||||
|
||||
// 1. 測試分配
|
||||
ConnectionContext* conn1 = (ConnectionContext*)c_Pool_Alloc(&pool);
|
||||
ConnectionContext* conn2 = (ConnectionContext*)c_Pool_Alloc(&pool);
|
||||
ConnectionContext* conn3 = (ConnectionContext*)c_Pool_Alloc(&pool);
|
||||
|
||||
assert(conn1 != NULL);
|
||||
assert(conn2 != NULL);
|
||||
assert(conn3 != NULL);
|
||||
assert(conn1 != conn2); // 確保分配到不同區塊
|
||||
|
||||
// 寫入資料驗證
|
||||
conn1->connection_id = 101;
|
||||
conn2->connection_id = 102;
|
||||
printf("分配成功,conn1 ID: %d, conn2 ID: %d\n", conn1->connection_id, conn2->connection_id);
|
||||
|
||||
// 2. 測試記憶體池全滿 (OOM)
|
||||
ConnectionContext* conn4 = (ConnectionContext*)c_Pool_Alloc(&pool);
|
||||
assert(conn4 != NULL); // 第 4 個分配應失敗返回 NULL
|
||||
printf("記憶體池已滿邊界測試成功!\n");
|
||||
|
||||
// 3. 測試釋放與回收再分配
|
||||
c_Pool_Free(&pool, conn2); // 釋放第 2 個區塊
|
||||
|
||||
// 再次分配,此時應該會優先拿到剛剛釋放的 conn2 區塊
|
||||
ConnectionContext* conn_reuse = (ConnectionContext*)c_Pool_Alloc(&pool);
|
||||
assert(conn_reuse == conn2);
|
||||
printf("記憶體回收與 O(1) 再分配測試成功!\n");
|
||||
|
||||
c_Pool_DryUp(&pool);
|
||||
c_Pool_Destroy(&pool);
|
||||
printf("--- c_Pool_t 所有測試順利通過! ---\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user