开始设计

This commit is contained in:
2026-08-10 01:21:15 +08:00
commit e45398991f
228 changed files with 20827 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
#include <c_Alignment.h>
+29
View File
@@ -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*/
+102
View File
@@ -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);
}
+62
View File
@@ -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*/
+132
View File
@@ -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;
}
+169
View File
@@ -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);
}
}
+29
View File
@@ -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*/
+245
View File
@@ -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");
}
+76
View File
@@ -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;
}
+25
View File
@@ -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*/
+56
View File
@@ -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;
}
+7
View File
@@ -0,0 +1,7 @@
#include <c_Memory.h>
+78
View File
@@ -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*/
+139
View File
@@ -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;
}
+36
View File
@@ -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*/
+52
View File
@@ -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;
}