简单的内存分配算法

来源:互联网 发布:网络大电影有效转化率 编辑:程序博客网 时间:2024/06/05 05:36

实现一个简单的内存分配模块,有简单的碎片整理功能

/* start of main.c */

#include "memory.h"

#define MEMBODY_SIZE 640

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 */