⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 vmem.c.svn-base

📁 uMemory 内存管理模块
💻 SVN-BASE
字号:
/*
 * =====================================================================================
 * 
 *       Filename:  vmem.c
 * 
 *    Description:  dynamic size memory allocation. 
 * 
 *        Version:  1.0
 *        Created:  6/19/2007 6:31:09 PM Tokyo Standard Time
 *       Revision:  none
 * 
 *         Author:  Jian Zhang , zhjwp@hotmail.com
 *        Note(s):  none
 * 
 * =====================================================================================
 */

#include "vmem.h"

typedef struct vmem_seg {
    INT16U prev;
    INT16U next;
} VMEM_SEG;

typedef struct vmem_free_seg {
    INT16U prev;
    INT16U next;
    struct vmem_free_seg *next_free;
} VMEM_FREE_SEG;

#define SIZEOF_VMEMSEG      (sizeof(VMEM_SEG))
#define VMEM_PAGE_SIZE      SIZEOF_VMEMSEG
#define ROUND_BUF_SIZE      (((CFG_VMEM_BUF_SIZE + VMEM_PAGE_SIZE - 1) / VMEM_PAGE_SIZE) * VMEM_PAGE_SIZE)
#define SIZEOF_TAIL(seg)    ((INT32U)((INT8U *)vmem_buf + ROUND_BUF_SIZE - (INT8U *)(seg)))

#define GET_PREV(seg)       (((seg)->prev >> 1) << 1)
#define SET_PREV(seg, p)    ((seg)->prev = p | ((seg)->prev & VMEM_USED))
#define GET_NEXT(seg)       ((seg)->next)
#define SET_NEXT(seg, n)    (((seg)->next) = n)

/* 'used' flag is the last bit of 'prev' member of each segment. */
#define VMEM_USED           ((INT16U)(0x0001))
#define ISNOT_USED(seg)     (!((seg)->prev & VMEM_USED))
#define SET_USED(seg)       ((seg)->prev = (seg)->prev | VMEM_USED)
#define CLEAR_USED(seg)     ((seg)->prev = (seg)->prev & ~VMEM_USED)

#define IS_TAIL(seg)        (GET_NEXT(seg) == 0)
#define IS_HEAD(seg)        (GET_PREV(seg) == 0)

#define NSEG(seg)           ((VMEM_FREE_SEG *)((INT8U *)(seg) + GET_NEXT(seg)))
#define PSEG(seg)           ((VMEM_FREE_SEG *)((INT8U *)(seg) - GET_PREV(seg)))


/* vmem_buf should be aligned and the size of vmem_buf should to be devided into VMEM_PAGE_SIZE */
static VMEM_SEG vmem_buf[ROUND_BUF_SIZE / SIZEOF_VMEMSEG];
static HANDLE g_vmem_lock = NULL;
static CB_MallocFailAlert *g_vmem_cbfunc = NULL;

static VMEM_FREE_SEG *vfree_list;

/**********************************************************************************

1. Structure of the segment links

 vmem_buf
    |       |-> head                                             |-> tail    
    |       |                                                    |          
    \---->  --------------------------------------------------------------------
            ||.......||....||            ||....||         ||.....||            |
            --------------------------------------------------------------------
                           |             
                           |-> vfree_list

2. 'head' and 'tail'
  'head' segment: the first segment in vmem_buf. The 'prev' of this segment is 0;
  'tail' segment: the last segment in vmem_buf.  The 'next' of this segment is 0;

3. 'vfree_list'
  'vfree_list' is the head node of the free segment list.

**************************************************************************************/

void VMEM_CBRegister(CB_MallocFailAlert *cbfunc)
{
    g_vmem_cbfunc = cbfunc;
}

void VMEM_Init(void)
{
    memset(vmem_buf, 0, ROUND_BUF_SIZE);
    vfree_list = (VMEM_FREE_SEG *)vmem_buf;
    SET_NEXT(vfree_list, 0);                    /* 0 means it is the tail segment */
    SET_PREV(vfree_list, 0);                    /* 0 means it is the head segment */
    CLEAR_USED(vfree_list);
    vfree_list->next_free = (VMEM_FREE_SEG *)0; /* 0 means it is the tail of the free segment list */
    g_vmem_lock = CRITICAL_CREATE(NULL);
}

void VMEM_Done(void)
{
    CRITICAL_FREE(g_vmem_lock);
}

void *VMEM_Malloc(INT16U size)
{
    VMEM_FREE_SEG *rseg = NULL; 
    VMEM_FREE_SEG *nseg = NULL;
    VMEM_FREE_SEG *pseg = NULL;

    /* Align the size of the allocated memory to the page size */
    size = SIZEOF_VMEMSEG + ((size + VMEM_PAGE_SIZE - 1) / VMEM_PAGE_SIZE) * VMEM_PAGE_SIZE;

    CRITICAL_ENTER(g_vmem_lock);

    /* Allocate memory from vfree_list pointer. */ 
    if (vfree_list == NULL) {
        goto MALLOC_RET;
    }
    nseg = vfree_list;
    /* find a big enough segment in the free segment list pointed by 'vfree_list'. */
    do {
        INT32U seg_len;
        if (!IS_TAIL(nseg)) {
            seg_len = GET_NEXT(nseg);
            if (seg_len >=size) {
                if (seg_len > size + SIZEOF_VMEMSEG) {
                    /* There are enough space to split it into two segments */
                    SET_USED(nseg);
                    SET_PREV(NSEG(nseg), seg_len - size);
                    SET_NEXT(PSEG(NSEG(nseg)), seg_len - size);
                    SET_NEXT(nseg, size);
                    SET_PREV(NSEG(nseg), size);
                    CLEAR_USED(NSEG(nseg));
                    rseg = nseg;
                    /* adjust the free segment list. */
                    if (nseg == vfree_list) {
                        vfree_list = NSEG(nseg);
                        vfree_list->next_free = nseg->next_free;
                    }
                    else {
                        NSEG(nseg)->next_free = nseg->next_free;
                        pseg->next_free = NSEG(nseg);
                    }
                    break;
                }
                else {  
                    /* nseg->next is size or (size + SIZEOF_VMEMSEG).
                     * It can't be split into two segments, this whole segment
                     * will be allocated.
                     */
                    SET_USED(nseg);
                    rseg = nseg;
                    /* adjust the free segment list. */                
                    if (nseg == vfree_list) {
                        vfree_list = nseg->next_free;
                    }
                    else {
                        pseg->next_free = nseg->next_free;
                    }
                    break;
                }
            }
        }
        else {
            /* nseg is the tail segment */
            seg_len = SIZEOF_TAIL(nseg);
            if (seg_len >= size) {
                if (seg_len > size + SIZEOF_VMEMSEG) {
                    SET_USED(nseg);
                    SET_NEXT(nseg, size);
                    SET_PREV(NSEG(nseg), size);
                    CLEAR_USED(NSEG(nseg));
                    SET_NEXT(NSEG(nseg), 0);
                    rseg = nseg;
                    /* adjust the free segment list. */                
                    if (nseg == vfree_list) {
                        vfree_list = NSEG(nseg);
                        vfree_list->next_free = nseg->next_free;
                    }
                    else {
                        NSEG(nseg)->next_free = nseg->next_free;
                        pseg->next_free = NSEG(nseg);
                    }
                    break;
                } 
                else {
                    SET_USED(nseg);
                    rseg = nseg;
                    /* adjust the free segment list. */                
                    if (nseg == vfree_list) {
                        vfree_list = nseg->next_free;
                    }
                    else {
                        pseg->next_free = nseg->next_free;
                    }
                    break;
                }
            }
        }
        
        pseg = nseg;
        nseg = nseg->next_free;
    } while (nseg);

MALLOC_RET:
    CRITICAL_LEAVE(g_vmem_lock);
    
    if (rseg == NULL) {
        if (g_vmem_cbfunc) {
            (*g_vmem_cbfunc)(size);
        }
        return NULL;
    }
    else {
        return (void *)((INT8U *)rseg + SIZEOF_VMEMSEG);
    }
}

void VMEM_Free(void *addr)
{
    VMEM_FREE_SEG *fseg;
    INT8U          seg_flag = 0;
    
    if (addr == NULL) {
        return;
    }
    fseg = (VMEM_FREE_SEG *)((INT8U *)addr - SIZEOF_VMEMSEG);
    CRITICAL_ENTER(g_vmem_lock);
    CLEAR_USED(fseg);

    /* Converge with the next free segment */
    if (!IS_TAIL(fseg)) {
        if (ISNOT_USED(NSEG(fseg))) {
            if (IS_TAIL(NSEG(fseg))) {
                /* fseg is the previous segment of the tail segment. */
                SET_NEXT(fseg, 0);
            }
            else {
                SET_PREV(NSEG(NSEG(fseg)), GET_NEXT(fseg) + GET_NEXT(NSEG(fseg)));
                SET_NEXT(fseg, GET_NEXT(fseg) + GET_NEXT(NSEG(fseg)));
            }
            seg_flag += 1;
        }
    }

    /* Converge with the previous free segment */
    if (!IS_HEAD(fseg)) {
        if (ISNOT_USED(PSEG(fseg))) {
            if (!IS_TAIL(fseg)) {
                SET_PREV(NSEG(fseg), GET_NEXT(fseg) + GET_NEXT(PSEG(fseg)));
                SET_NEXT(PSEG(fseg), GET_NEXT(fseg) + GET_NEXT(PSEG(fseg)));
            }
            else {
                /* fseg is the tail segment. */
                SET_NEXT(PSEG(fseg), 0);
            }
            seg_flag += 2;
        }
    }

    /*  value of free_flag: 
     *      0: no segment converging.
     *      1: fseg is only converged with its next segment.
     *      2: fseg is only converged with its previous segment.
     *      3: fseg is converged with both its previous and its next segments.
     */
    if (seg_flag == 3) {
        fseg = PSEG(fseg);
        fseg->next_free = (fseg->next_free)->next_free;        
    }
    else if (seg_flag != 2) {
        VMEM_FREE_SEG *nseg = NULL;
        VMEM_FREE_SEG *pseg = NULL;

        if (vfree_list == NULL) {
            vfree_list = fseg;
            vfree_list->next_free = 0;
        }
        else if (vfree_list > fseg) {
            if (IS_TAIL(fseg)) {
                /* in the case that fseg is converged with its next segment, 
                 * tail segment, and the vfree_list is pointed to that tail segment.
                 */
                vfree_list = fseg;
                vfree_list->next_free = 0;
            }
            else {
                /* check if the segment pointed by vfree_list is converged with fseg. */
                fseg->next_free = (vfree_list < NSEG(fseg)) ? vfree_list->next_free : vfree_list;
                vfree_list = fseg;
            }
        }
        else {
            /* insert 'fseg' into the free segment list. */
            nseg = vfree_list;

            if (seg_flag == 0) {
                do {
                    if (nseg > fseg) {
                        fseg->next_free = nseg;
                        pseg->next_free = fseg;            
                        break;
                    }
                    pseg = nseg;
                    nseg = nseg->next_free;
                } while (nseg);
                if (!nseg) {
                    /* 'fseg' is the tail node of the free segment list. */
                    pseg->next_free = fseg;
                    fseg->next_free = 0;
                }
            }
            else {
                do {
                    if (nseg > fseg) {
                        fseg->next_free = nseg->next_free;
                        pseg->next_free = fseg;            
                        break;
                    }
                    pseg = nseg;
                    nseg = nseg->next_free;
                } while (nseg);
            }
        }
    }    
    CRITICAL_LEAVE(g_vmem_lock);
}

void *VMEM_Realloc(void *addr, INT16U size) 
{
    VMEM_SEG *aseg;
	void *addr_new;
	INT16U size_old;
	
    if (addr == NULL) {
        return NULL;
    }

    aseg = (VMEM_SEG *)((INT8U *)addr - SIZEOF_VMEMSEG);
    size_old = (INT16U)((IS_TAIL(aseg) ? SIZEOF_TAIL(aseg) : GET_NEXT(aseg)) - SIZEOF_VMEMSEG);    

    addr_new = VMEM_Malloc(size);
    if (addr_new) {
        memcpy(addr_new, addr, size_old);
        VMEM_Free(addr);		
    }
    return addr_new;
}

void VMEM_Query(VMEM_INFO *info)
{
    VMEM_FREE_SEG *nseg = vfree_list;
    INT32U list_num = 0;
    INT32U largest_unused = 0;
    INT32U total_unused = 0;
    
    info->total_size = ROUND_BUF_SIZE;
    CRITICAL_ENTER(g_vmem_lock);
    while (nseg) {
        INT32U seg_size = IS_TAIL(nseg) ? SIZEOF_TAIL(nseg) : GET_NEXT(nseg);
        
        total_unused += seg_size;
        list_num++;
        if (seg_size > largest_unused) {
            largest_unused = seg_size;
        }    
        nseg = nseg->next_free;
    };
    info->total_unused = total_unused;
    info->largest_unused = largest_unused;
    info->list_num = list_num;
    
    CRITICAL_LEAVE(g_vmem_lock);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -