本文研究eSNACC的C运行时库动态内存管理。
eSNACC的运行时库和代码生成用的内存管理函数用的是统一的宏定义原型,但是支持用户自己配置内存管理方案。eSNACC本身提供两种内存管理方案:
一个是mem.h/c定义的直接分配;另一个是nibble-alloc.h/c定义的Nibble memory系统。
对内存管理的配置在asn-config.h中,现在代码如下:
#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默认使用nibble memory.要记住的是:解码器始终认为Asn1Alloc得到的内存一定是用0初始化好的!如果我们实现自己的内存方案,一定要记得这一点。
我们先来看看mem.h/c中的实现:
#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
实现如下:
#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);
} 相当简单,只是别忘了,在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,那么在做任何编码解码操作之前,需要调用InitNibbleMem函数以初始化全局变量。
2、在程序最后,调用ShutdownNibbleMem销毁nibble内存。
3、nibble memory在申请了内存块之后无法释放指定块,也就是一直占用着,哪怕那块数据也就不用了,除非Reset或者Shutdown,是不是考虑Asn.1解码时分配临时内存不多?
4、正如3中所说,其实nibble memory的效率主要来自于两方面:一是一次分配一块大内存来减少malloc;另外就是免除了free操作。不过对后者,个人感觉限制太死,会不会导致占用内存太多?
好了,eSNACC的内存方案就是这些了。如果你对这些都不满意,可以很方便的修改asn-config.h来配置自己的方案。