实现一个简单的内存分配模块,有简单的碎片整理功能
/* start of main.c */
#include "memory.h"
char membody[MEMBODY_SIZE] = {0,};
int main()
{
void *mem1, *mem2, *mem3;
int i;
printf("main!!!\n");
simple_mem_init(membody, MEMBODY_SIZE);
mem1 = simple_new(200);
mem2 = simple_new(200);
mem3 = simple_new(200);
simple_delete(mem1);
simple_delete(mem2);
simple_delete(mem3);
mem1 = simple_new(300);
simple_delete(mem1);
for (i = 0; ;i++) {
if (!simple_new(10))
break;
}
return 0;
}
/* end of main.c */
/* start of memory.h */
#ifndef memory_h
#define memory_h
/* not very useful, but only some defines */
#include "common.h"
void simple_delete(char* data);
void simple_delete_array(char* data);
void simple_memset(char* data, char val, int32 size);
char* simple_new(int32 size);
bool simple_mem_init(char* mem, int32 size);
#endif
/* end of memory.h */
/* start of memory.c */
#include "memory.h"
//sizeof(MEM_ALLOC_INFO) must be mem_slice_size aligned
typedef struct {
int32 size;
}MEM_ALLOC_INFO;
#define mem_slice_size 4 //must be greater than 4 and shoule be multile of 4
#define mem_slice_size_is_2_index //define this if mem_slice_size = 2^n
#ifdef mem_slice_size_is_2_index
//mem_slice_size = 2^mem_slice_size_shift
#define mem_slice_size_shift 2
#endif
#define bitsof(type) ((int32)(sizeof(type) << 3))
//the last on is greater than mem_block_free_list_size * mem_slice_size
#define mem_block_free_list_size (0x80)
#ifdef mem_slice_size_is_2_index
#define roundup_2_mem_slice_size(size) (((size) + mem_slice_size-1)&(-mem_slice_size))
#else
#define roundup_2_mem_slice_size(size) (((size) + mem_slice_size-1) - ((((size) + mem_slice_size-1)) % mem_slice_size))
#endif
//{4, 8, 12, 16...}
//the last is a list that with various size but greater than
//mem_slice_size*mem_block_free_list_size
static char* mem_block_free_list[mem_block_free_list_size] = {0,};
static unsigned char mem_valible_idx_flag[mem_block_free_list_size/(bitsof(char))] = {0,};
//stats
static int32 mem_block_free_cnt_list[mem_block_free_list_size] = {0,};
static int32 mem_block_alloc_cnt_list[mem_block_free_list_size] = {0,};
static int32 mem_block_alloc_fail_cnt_list[mem_block_free_list_size] = {0,};
static void add_new_block(char* mem, int32 size);
static char* simple_new_internal(int32 alloc_size);
static char* malloc_by_idx(int32 idx, int32 alloc_size);
static int32 collect_mem(char* m);
static void mem_block_set(int32 idx, char* mem);
static void process_partion_mem(char* mem, int32 size);
static char* membody;
static int32 membody_size;
#define mem_used_flag 0x1
#define mem_flag_mask 0x3
#define mem_size(mem) \
((*(int32*)(mem)) & (-4))
#define mem_is_used(mem) \
((*(int32*)(mem)) & mem_used_flag)
#define mem_set_freed(mem)\
(*(int32*)(mem)) = ((*(int32*)(mem)) & (-2));
#define mem_set_used(mem) \
(*(int32*)(mem)) = ((*(int32*)(mem)) | mem_used_flag)
#define mem_size_idx(idx) \
((idx)*(mem_slice_size))
#define mem_block(idx) \
mem_block_free_list[idx]
#define mem_next(mem) \
(*(int32*)(mem+sizeof(int32)))
#define mem_next_get(mem)\
(char*)mem_next(mem)
#define mem_next_set(mem, next)\
mem_next(mem) = next
//(mem_slice_size=4)
static int32 mem_idx(int32 size)
{
#ifdef mem_slice_size_is_2_index
int32 idx = ((size) >> mem_slice_size_shift);
#else
int32 idx = size / mem_slice_size;
#endif
if (idx >= mem_block_free_list_size)
idx = mem_block_free_list_size - 1;
return idx;
}
#define mem_size_idx_min \
mem_idx(sizeof(MEM_ALLOC_INFO)+mem_slice_size-1)
static void mem_size_set(char* mem, int32 size)
{
size = size | ((*(int32*)(mem)) & mem_flag_mask);
(*(int32*)(mem)) = size;
}
static void mem_block_set(int32 idx, char* mem)
{
int32 x = idx >> 3;
int32 y = idx & 7;
mem_block_free_list[idx] = mem;
if (mem != NULL) {
mem_valible_idx_flag[x] =
mem_valible_idx_flag[x] | (1<<y);
} else {
mem_valible_idx_flag[x] =
mem_valible_idx_flag[x] & (~(1<<y));
}
}
static void alloc_stats(int32 size)
{
mem_block_alloc_cnt_list[mem_idx(size)]++;
}
static void alloc_fail_stats(int32 size)
{
mem_block_alloc_fail_cnt_list[mem_idx(size)]++;
}
static void free_stats(int32 size)
{
mem_block_free_cnt_list[mem_idx(size)]++;
}
//the first funtion must be called when memmory alloc works
bool simple_mem_init(char* mem, int32 size)
{
if (!mem || !size)
return false;
membody = mem;
membody_size = size;
add_new_block(mem, size);
return true;
}
static void mem_set_alloc_inf(char* mem, int32 size)
{
mem_size_set(mem, size);
mem_set_used(mem);
}
static bool mem_in(char* addr)
{
if (addr < membody || addr >= membody+membody_size)
return false;
return true;
}
//mem is in free list
static void remove_free_mem(char* mem)
{
char* pre = 0, *cur = NULL;
int32 idx = mem_idx(mem_size(mem));
cur= mem_block(idx);
while (cur != mem) {
pre = cur;
cur = mem_next_get(cur);
}
if (cur != mem) {
crashMe("invalid data!!!");
return;
}
if (pre != NULL)
mem_next_set(pre, mem_next_get(cur));
else if (cur == mem_block(idx)) {
char* next = mem_next_get(cur);
mem_block_set(idx, mem_next_get(cur));
}
mem_next_set(mem, NULL);
}
static void merge_mem(char* mem)
{
remove_free_mem(mem+mem_size(mem));
mem_size_set(mem, mem_size(mem) + mem_size(mem+mem_size(mem)));
}
static int32 collect_mem(char* m)
{
int32 size = mem_size(m);
if (!mem_is_used(m))
remove_free_mem(m);
while (mem_in(m+size)
&& !mem_is_used(m+size)) {
merge_mem(m);
size = mem_size(m);
}
add_new_block(m, size);
return size;
}
void simple_delete(char* data)
{
char* m = (char*)data - sizeof(MEM_ALLOC_INFO);
if (!mem_in(m)
|| !mem_is_used(m))
return;
free_stats(mem_size(m));
collect_mem(m);
}
void simple_delete_array(char* data)
{
simple_delete(data);
}
void simple_memset(char* data,
char val, int32 size)
{
int32 i;
for(i = 0; i < size; i++)
data[i] = val;
}
static void add_new_block(char* mem, int32 size)
{
int32 idx = mem_idx(size);
mem_next_set(mem, mem_block(idx));
mem_block_set(idx, mem);
mem_set_freed(mem);
mem_size_set(mem, size);
}
static char* post_alloc(int32 idx,
int32 alloc_size)
{
#ifdef mem_slice_size_is_2_index
int32 remain = (idx << mem_slice_size_shift) - alloc_size;
#else
int32 remain = idx * mem_slice_size - alloc_size;
#endif
char* memnext = mem_next_get(mem_block(idx));
add_new_block(mem_block(idx) + alloc_size,
remain);
return memnext;
}
static char* find_best_match(int32 alloc_size)
{
char* nextmem;
int32 diff = 0x7fffffff;
char* bestmem = NULL;
char* pre1 = NULL, *pre2 = NULL;
for (nextmem = mem_block(mem_block_free_list_size-1);
nextmem != NULL;
pre1 = nextmem, nextmem = mem_next(nextmem)) {
if (mem_size(nextmem) >= alloc_size
&& (mem_size(nextmem) - alloc_size) < diff) {
pre2 = pre1;
diff = mem_size(nextmem) - alloc_size;
bestmem = nextmem;
if (diff < 0x10)
break;
}
}
if (bestmem != NULL) {
if (pre2 != NULL)
mem_next_set(pre2, mem_next_get(bestmem));
else
mem_block_set(mem_block_free_list_size-1, mem_next_get(bestmem));
}
return bestmem;
}
static int32 collect(int32 idx, int32 alloc_size)
{
char* m = mem_block(idx);
int32 size;
while(m != NULL) {
size = collect_mem(m);
if (size >= alloc_size)
return mem_idx(size);
m = mem_next_get(m);
}
return -1;
}
//when compact and find a memory bigger than alloc_size
//just return idx
static int32 mem_compact(int32 alloc_size)
{
int32 idx;
int32 validi = 0;
for (validi = (mem_block_free_list_size >> 3) - 1;
validi >= 0;
validi--) {
if (mem_valible_idx_flag[validi] == 0)
continue;
for (idx = ((validi+1) << 3) - 1;
idx >= (validi << 3);
idx--) {
if (mem_block(idx) != NULL) {
int32 result = collect(idx, alloc_size);
if (result >= 0)
return result;
}
}
}
return -1;
}
static char* big_mem_alloc(int32 alloc_size)
{
char* bestmem = find_best_match(alloc_size);
if (!bestmem) {
int32 idx = mem_compact(alloc_size);
if (idx >= 0)
return malloc_by_idx(idx, alloc_size);
alloc_fail_stats(alloc_size);
crashMe("out of memory");
return NULL;
}
process_partion_mem(bestmem + alloc_size, mem_size(bestmem) - alloc_size);
mem_set_alloc_inf(bestmem, alloc_size);
return bestmem + sizeof(MEM_ALLOC_INFO);
}
static int32 next_valible_idx(int32 idx)
{
int32 i,cnt;
for (i = idx/bitsof(char); i < (mem_block_free_list_size >> 3); i++) {
if (mem_valible_idx_flag[i] != 0
&& mem_valible_idx_flag[i] >= (1 << (idx&7))) {
cnt = (i << 3) + (idx&7);
return cnt;
}
idx = 0;
}
return mem_block_free_list_size-1;//always try last item
}
static void process_partion_mem(char* mem, int32 size)
{
if (size <= 0)
return;
mem_size_set(mem, size);
mem_set_used(mem);
collect_mem(mem);
}
static char* malloc_by_idx(int32 idx, int32 alloc_size)
{
char* result = 0;
if (idx == mem_block_free_list_size-1)
return big_mem_alloc(alloc_size);
result = mem_block(idx);
mem_block_set(idx, mem_next_get(mem_block(idx)));
#ifdef mem_slice_size_is_2_index
process_partion_mem(result + alloc_size,
(idx <<mem_slice_size_shift) - alloc_size);
#else
process_partion_mem(result + alloc_size,
idx * mem_slice_size - alloc_size);
#endif
mem_set_alloc_inf(result,
alloc_size);
return result + sizeof(MEM_ALLOC_INFO);
}
static char* simple_new_internal(int32 alloc_size)
{
int32 idx = mem_idx(alloc_size);
int32 tmp = next_valible_idx(idx);
idx = tmp > idx ? tmp : idx;
alloc_stats(alloc_size);
for (idx; idx < mem_block_free_list_size - 1; idx++) {
if (mem_block(idx) != NULL) {
break;
}
}
return malloc_by_idx(idx, alloc_size);
}
char* simple_new(int32 size)
{
char* result = 0;
int32 alloc_size = size + sizeof(MEM_ALLOC_INFO);
if (!size)
return NULL;
alloc_size = roundup_2_mem_slice_size(alloc_size);
return simple_new_internal(alloc_size);
}
/* end of memory.c */