本文研究eSNACC的C運行時庫動態(tài)內(nèi)存管理。
eSNACC的運行時庫和代碼生成用的內(nèi)存管理函數(shù)用的是統(tǒng)一的宏定義原型,但是支持用戶自己配置內(nèi)存管理方案。eSNACC本身提供兩種內(nèi)存管理方案:
一個是mem.h/c定義的直接分配;另一個是nibble-alloc.h/c定義的Nibble memory系統(tǒng)。
對內(nèi)存管理的配置在asn-config.h中,現(xiàn)在代碼如下:
#ifdef USE_NIBBLE_MEMORY

#include "nibble-alloc.h"

#define Asn1Alloc( size) NibbleAlloc (size)
#define Asn1Free( ptr) /* empty */
#define CheckAsn1Alloc( ptr, env) \
if ((ptr) == NULL)\
longjmp (env, -27)

#else /* !USE_NIBBLE_MEMORY */

#include "mem.h"

#define Asn1Alloc( size) Malloc (size)
#define Asn1Free( ptr) Free (ptr)
#define CheckAsn1Alloc( ptr, env) \
if ((ptr) == NULL)\
longjmp (env, -27)

#endif /* USE_NIBBLE_MEMORY */
其中,eSNACC默認(rèn)使用nibble memory.要記住的是:解碼器始終認(rèn)為Asn1Alloc得到的內(nèi)存一定是用0初始化好的!如果我們實現(xiàn)自己的內(nèi)存方案,一定要記得這一點。
我們先來看看mem.h/c中的實現(xiàn):
#ifdef __cplusplus

extern "C"
{
#endif

void *Malloc PROTO ((int size));
void *Realloc PROTO ((void *ptr, int newsize));
void Free PROTO ((void *ptr));


/**//* malloc type */
#define MT( type) (type *)Malloc (sizeof (type))


#ifdef __cplusplus
}
#endif
實現(xiàn)如下:
#include "mem.h"
#include <memory.h>

void* Malloc PARAMS ((size), int size)


{
void *retVal = malloc (size);

if (retVal == NULL)

{
fprintf (stderr, "out of memory! bye!\n");
fprintf (stderr, "tried to allocate %d byes\n", size);
exit (1);
}

memzero (retVal, size);
return retVal;


} /**//* Malloc */

void *Realloc PARAMS ((ptr, newsize),
void *ptr _AND_
int newsize)


{
void *retval = realloc (ptr, newsize);

if (retval == NULL)

{
fprintf (stderr, "out of memory! bye!\n");
fprintf (stderr, "tried to reallocate %d byes\n", newsize);
exit (1);
}

return retval;
}

void Free PARAMS ((ptr),
void *ptr)


{
free (ptr);
}相當(dāng)簡單,只是別忘了,在malloc之后,要先memzero再返回。
然后我們再來研究一下nibble memory:
#ifdef __cplusplus

extern "C"
{
#endif

typedef struct NibbleBuf

{
char *start;
char *end;
char *curr;
struct NibbleBuf *next;
} NibbleBuf;

typedef struct NibbleMem

{
NibbleBuf *firstNibbleBuf;
NibbleBuf *currNibbleBuf;
unsigned long incrementSize;
} NibbleMem;

void InitNibbleMem PROTO ((unsigned long initialSize, unsigned long incrementSize));

void ShutdownNibbleMem();

void ServiceNibbleFault PROTO ((unsigned long size));

void *NibbleAlloc PROTO ((unsigned long size));

void ResetNibbleMem();


#ifdef __cplusplus
}
#endif
#include <malloc.h>
#include <string.h>
#include "asn-config.h"
#include "nibble-alloc.h"


NibbleMem *nmG = NULL;

void InitNibbleMem PARAMS ((initialSize, incrementSize),
unsigned long initialSize _AND_
unsigned long incrementSize)


{
NibbleMem *nm;

nm = (NibbleMem*) malloc (sizeof (NibbleMem));
nm->incrementSize = incrementSize;

nm->currNibbleBuf = nm->firstNibbleBuf = (NibbleBuf*)malloc (sizeof (NibbleBuf));
nm->firstNibbleBuf->curr = nm->firstNibbleBuf->start = (char*) malloc (initialSize);
nm->firstNibbleBuf->end = nm->firstNibbleBuf->start + initialSize;
nm->firstNibbleBuf->next = NULL;
memzero (nm->currNibbleBuf->start, initialSize);


nmG = nm;/**//* set global */


} /**//* InitNibbleAlloc */



/**//*
* alloc new nibble buf, link in, reset to curr nibble buf
*/
void ServiceNibbleFault PARAMS ((size),
unsigned long size)


{
NibbleMem *nm;
unsigned long newBufSize;

nm = nmG;

if (size > nm->incrementSize)
newBufSize = size;
else
newBufSize = nm->incrementSize;

nm->currNibbleBuf->next = (NibbleBuf*) malloc (sizeof (NibbleBuf));
nm->currNibbleBuf = nm->currNibbleBuf->next;
nm->currNibbleBuf->curr = nm->currNibbleBuf->start = (char*) malloc (newBufSize);
nm->currNibbleBuf->end = nm->currNibbleBuf->start + newBufSize;
nm->currNibbleBuf->next = NULL;
memzero (nm->currNibbleBuf->start, newBufSize);

} /**//* serviceNibbleFault */



/**//*
* returns requested space filled with zeros
*/
void* NibbleAlloc PARAMS ((size),
unsigned long size)


{
NibbleMem *nm;
char *retVal;
unsigned long ndiff;

nm = nmG;

/**//* DAD - although error checking on the mallocs during
* ServiceNibbleFault could be more robust, for now i'm
* just going to avoid allocating really huge amounts
* of memory (which can occur if the ASN.1 data has
* become corrupted, and you were trying to decode).
*/

if(size > 1024*1024) /**//* say roughly a meg for now */
return(0);

if ((nm->currNibbleBuf->end - nm->currNibbleBuf->curr) < (int)size)
ServiceNibbleFault (size);

retVal = nm->currNibbleBuf->curr;


/**//*
* maintain word alignment
*/
ndiff = size % sizeof (long);
if (ndiff != 0)

{
nm->currNibbleBuf->curr += size + sizeof (long) - ndiff;


/**//*
* this is a fix from Terry Sullivan <FCLTPS@nervm.nerdc.ufl.edu>
*
* makes sure curr does not go past the end ptr
*/
if (nm->currNibbleBuf->curr > nm->currNibbleBuf->end)
nm->currNibbleBuf->curr = nm->currNibbleBuf->end;
}
else
nm->currNibbleBuf->curr += size;

return retVal;

} /**//* NibbleAlloc */




/**//*
* frees all nibble buffers except the first,
* resets the first to empty and zero's it
*/
void ResetNibbleMem()


{
NibbleMem *nm;
NibbleBuf *tmp;
NibbleBuf *nextTmp;

nm = nmG;


/**//*
* reset first nibble buf
*/
memzero (nm->firstNibbleBuf->start, nm->firstNibbleBuf->curr - nm->firstNibbleBuf->start);

nm->firstNibbleBuf->curr = nm->firstNibbleBuf->start;


/**//*
* free incrementally added nibble bufs
*/
for (tmp = nm->firstNibbleBuf->next; tmp != NULL; )

{
free (tmp->start);
nextTmp = tmp->next;
free (tmp);
tmp = nextTmp;
}


/**//* From ftp://ftp.cs.ubc.ca/pub/local/src/snacc/bugs-in-1.1 */
nm->firstNibbleBuf->next = NULL;
nm->currNibbleBuf = nm->firstNibbleBuf;


} /**//* ResetNibbleMem */


/**//*
* frees all nibble buffers, closing this
* NibbleMem completely
*/
void ShutdownNibbleMem()


{
NibbleMem *nm;
NibbleBuf *tmp;
NibbleBuf *nextTmp;

nm = nmG;
nmG = NULL;

/**//*
* free nibble bufs
*/
if (nm == NULL)
return;
for (tmp = nm->firstNibbleBuf; tmp != NULL; )

{
free (tmp->start);
nextTmp = tmp->next;
free (tmp);
tmp = nextTmp;
}

free (nm);

} /**//* ShutdownNibbleMem */
這些代碼都比較簡單,而且都基本有注釋,所以就不多具體說了。只是對nibble memory方案的幾個注意點說一下:
1、如果使用nibble memory,那么在做任何編碼解碼操作之前,需要調(diào)用InitNibbleMem函數(shù)以初始化全局變量。
2、在程序最后,調(diào)用ShutdownNibbleMem銷毀nibble內(nèi)存。
3、nibble memory在申請了內(nèi)存塊之后無法釋放指定塊,也就是一直占用著,哪怕那塊數(shù)據(jù)也就不用了,除非Reset或者Shutdown,是不是考慮Asn.1解碼時分配臨時內(nèi)存不多?
4、正如3中所說,其實nibble memory的效率主要來自于兩方面:一是一次分配一塊大內(nèi)存來減少malloc;另外就是免除了free操作。不過對后者,個人感覺限制太死,會不會導(dǎo)致占用內(nèi)存太多?
好了,eSNACC的內(nèi)存方案就是這些了。如果你對這些都不滿意,可以很方便的修改asn-config.h來配置自己的方案。