这个文件内有五个类型:TBuffer、TString、TList、TTable和TPool。这些应该是为库构件的基础数据结构。
typedef struct { void *data; xmlrpc_uint32_t size; xmlrpc_uint32_t staticid; } TBuffer;BufferAlloc函数不复杂。函数的作用就是申请memsize个字节的空间,如果申请成功,则让TBuffer的data指针指向这个内存区域,并且TBuffer的size保存这个内存区域的大小。
bool BufferAlloc(TBuffer * const buf, xmlrpc_uint32_t const memsize) { /* ************** Implement the static buffers ***/ buf->staticid=0; buf->data=(void *)malloc(memsize); if (buf->data) { buf->size=memsize; return TRUE; } else { buf->size=0; return FALSE; }; }BufferFree函数就是将在BufferAlloc函数内申请的空间释放掉。
bool BufferRealloc(TBuffer * const buf, xmlrpc_uint32_t const memsize) { if (buf->staticid) { TBuffer b; if (memsize<=buf->size) return TRUE; if (BufferAlloc(&b,memsize)) { memcpy(b.data,buf->data,buf->size); BufferFree(buf); *buf=b; return TRUE; } } else { void *d; d=realloc(buf->data,memsize); if (d) { buf->data=d; buf->size=memsize; return TRUE; } } return FALSE; }BufferRealloc里的if语句后的第一个分支不会被使用,因为BufferAlloc函数内TBuffer的staticid始终都是0,因此BufferRealloc内的else后的分支总是会被执行。可以看出TBuffer结构体只是简单地封装了c的malloc等内存分配函数。
typedef struct { TBuffer buffer; xmlrpc_uint32_t size; } TString;从定义来看,只是简单地包裹了TBuffer类型。既然TBuffer有个size属性,为何TString又有个size属性呢?
bool StringAlloc(TString * const stringP) { bool succeeded; stringP->size = 0; succeeded = BufferAlloc(&stringP->buffer, 256); if (succeeded) { *(char *)(stringP->buffer.data) = '\0'; return TRUE; } else return FALSE; }TString的创建函数表明每个TString在创建时都只会申请256个字节的空间。除了将第一个字节置为‘\0’外,并无其他处理。
bool StringConcat(TString * const stringP, const char * const string2) { uint32_t const len = strlen(string2); if (len + stringP->size + 1 > stringP->buffer.size) { bool succeeded; succeeded = BufferRealloc( &stringP->buffer, ((len + stringP->size + 1 + 256) / 256) * 256); if (!succeeded) return FALSE; } strcat((char *)(stringP->buffer.data), string2); stringP->size += len; return TRUE; }StringBlockConcat函数与StringConcat函数的不同之处在于新加入字串的拷贝方式。StringConcat函数采用的方式是使用strcat库函数,而StringBlockConcat函数采用的方式是使用memcpy库函数。strcat会自动在最后一个字符的后部再多拷贝一个'\0'字符。memcpy库函数从原字串(函数的第二个参数)地址的起始位置拷贝len(函数的第三个参数)个字串到目标地址(函数的第一个参数)。StringBlockConcat函数内计算新加入的字串长度时多加了1。即,memcpy库函数会多拷贝一个字符,这个字符就是'\0'。然后将TString的size赋上len。
bool StringBlockConcat(TString * const stringP, const char * const string2, char ** const ref) { uint32_t const len = strlen(string2) + 1; if (len + stringP->size > stringP->buffer.size) { bool succeeded; succeeded = BufferRealloc( &stringP->buffer, ((len + stringP->size + 1 + 256) / 256) * 256); if (!succeeded) return FALSE; } *ref = (char *)(stringP->buffer.data) + stringP->size; memcpy(*ref, string2, len); stringP->size += len; return TRUE; }这个TString的size有点奇怪,因为在StringAlloc函数内size值无论如何都是0。那么在StringConcat和StringBlockConcat函数内在拷贝完毕后在原有size值基础上再加上len,其实就是将这次拷贝的字符个数赋给size。我觉得这个size值没什么用。也许当初设想好的size的用途现在没有实现吧。StringFree和StringData都很简单就不具体分析了。
typedef struct { void **item; uint16_t size; uint16_t maxsize; bool autofree; } TList;这个结构体想实现C++标准库里的list功能。item是指向无类型指针的指针,目的是想存放任何类型的变量。
void ListInit(TList * const sl) { sl->item=NULL; sl->size=sl->maxsize=0; sl->autofree=FALSE; } void ListInitAutoFree(TList * const sl) { sl->item=NULL; sl->size=sl->maxsize=0; sl->autofree=TRUE; }
ListAdd的作用是将str加入到列表的尾部。每次添加新的元素时,都会再计算一次内部数组空间是否有空闲位置保存新的元素。如果空间不足,每次申请新的空间时都会申请16个可以保存指向元素的指针的空间。内部数组空间处理妥当后,将此次新增的元素指针放入数组内。
bool ListAdd(TList * const sl, void * const str) { /*---------------------------------------------------------------------------- Add an item to the end of the list. -----------------------------------------------------------------------------*/ bool success; if (sl->size >= sl->maxsize) { uint16_t newSize = sl->maxsize + 16; void **newitem; newitem = realloc(sl->item, newSize * sizeof(void *)); if (newitem) { sl->item = newitem; sl->maxsize = newSize; } } if (sl->size >= sl->maxsize) success = FALSE; else { success = TRUE; sl->item[sl->size++] = str; } return success; }ListFree的作用是释放之前已申请的保存指向元素指针的内存空间。释放时还会再判断autofree标志,但之前分析ListAdd时,申请空间的处理并没有任何有关autofree的处理。看样子这个库的代码不是很完美,有较多的瑕疵。
void ListFree(TList * const sl) { if (sl->item) { if (sl->autofree) { unsigned int i; for (i = sl->size; i > 0; --i) free(sl->item[i-1]); } free(sl->item); } sl->item = NULL; sl->size = 0; sl->maxsize = 0; }刚说到这个库的实现有瑕疵,分析ListFindString函数时又发现了一个有趣的事。这个函数的目的是想在TList中查找是否存在str这个字串,如果存在返回它在列表中的索引值。
bool ListFindString(TList * const sl, const char * const str, uint16_t * const indexP) { uint16_t i; if (sl->item && str) for (i=0;i<sl->size;i++) if (strcmp(str,(char *)(sl->item[i]))==0) { *indexP=i; return TRUE; }; return FALSE; }这个函数的内部实现没什么问题,也容易理解。但之前分析TList结构体类型时感觉这个列表可以存储任何类型,因为使用了void **,指向无类型的指针的指针。可这个函数明确地说明列表内存储的就是字串。
ListAddFromString函数内最重要的部分是for循环。buffer是输入参数stringArg的一个拷贝。NextToken函数的定义在token.c文件内,这个函数的作用是找到字串中第一个非制表符和空格字符并指向这个字符所在的地址。然后再继续处理,这个字符不能是逗号。GetToken函数也是在token.c文件内定义,这个函数的作用是找到第一个单词,并将这个单词后的一个位置赋上'\0'。如果第一个字符就是制表符、空格、回车、换行或者是'\0',那么GetToken将返回空,即第一个单词是空。也就是说,t现在指向这个找到的第一个单词字串的起始地址。接着又是一个for循环,目的是在原始字串中再找到逗号字符所在的位置。t所指向的第一个单词地址,是指在c这个原始串中。但之前在找这个第一个单词前,又已经处理过了逗号,不是很清楚这里的处理的目的是什么。最后将这个找到的单词存入TList内。
bool ListAddFromString(TList * const list, const char * const stringArg) { bool retval; if (!stringArg) retval = TRUE; else { char * buffer; buffer = strdup(stringArg); if (!buffer) retval = FALSE; else { bool endOfString; bool error; char * c; for (c = &buffer[0], endOfString = FALSE, error = FALSE; !endOfString && !error; ) { const char * t; NextToken((const char **)&c); while (*c == ',') ++c; t = GetToken(&c); if (!t) endOfString = TRUE; else { char * p; for (p = c - 2; *p == ','; --p) *p = '\0'; if (t[0] != '\0') { bool added; added = ListAdd(list, (void*)t); if (!added) error = TRUE; } } } retval = !error; xmlrpc_strfree(buffer); } } return retval; }
typedef struct { char *name,*value; uint16_t hash; } TTableItem; typedef struct { TTableItem *item; uint16_t size,maxsize; } TTable;TTable内部是一个存放TTableItem类型指针的数组。TTableItem可以存放一对键值对字串。TTableItem内还有一个无符号16位整型值。TableInit和TableFree不是很复杂,正如所预想的那样初始化一个TTable类型变量,逐个释放TTableItem内部的两个字符数组、以及TTable的指针数组。
bool TableAdd(TTable * const t, const char * const name, const char * const value) { if (t->size>=t->maxsize) { TTableItem *newitem; t->maxsize+=16; newitem=(TTableItem *)realloc(t->item,(t->maxsize)*sizeof(TTableItem)); if (newitem) t->item=newitem; else { t->maxsize-=16; return FALSE; } } t->item[t->size].name=strdup(name); t->item[t->size].value=strdup(value); t->item[t->size].hash=Hash16(name); ++t->size; return TRUE; }TTableItem有一个无符号16位整型值属性hash,这个值是依据name字串值计算得到的。Hash16就是计算这个值的函数,计算过程也不复杂一目了然。
typedef struct _TPoolZone { char * pos; char * maxpos; struct _TPoolZone * next; struct _TPoolZone * prev; /* char data[0]; Some compilers don't accept this */ char data[1]; } TPoolZone; typedef struct { TPoolZone * firstzone; TPoolZone * currentzone; uint32_t zonesize; struct abyss_mutex * mutexP; } TPool;TPool是TPoolZone的管理器。TPoolZone有next和prev两个属性,均为指向TPoolZone的指针。TPoolZone可以维护一个双向链表。
bool PoolCreate(TPool * const poolP, uint32_t const zonesize) { bool success; bool mutexCreated; poolP->zonesize = zonesize; mutexCreated = MutexCreate(&poolP->mutexP); if (mutexCreated) { TPoolZone * const firstZoneP = PoolZoneAlloc(zonesize); if (firstZoneP != NULL) { poolP->firstzone = firstZoneP; poolP->currentzone = firstZoneP; success = TRUE; } else success = FALSE; if (!success) MutexDestroy(poolP->mutexP); } else success = FALSE; return success; }PoolAlloc函数的作用是依据size值在TPool内找到一个满足此大小的空闲内存区域,如果已有的所有TPoolZone内存空间都没有这么大的空闲内存区域,就再创建一个TPoolZone变量。这个新创建的TPoolZone变量内部的内存区域大小由TPool的zonesize值决定,这个值是在初始化TPool变量时确定的。
void * PoolAlloc(TPool * const poolP, uint32_t const size) { /*---------------------------------------------------------------------------- Allocate a block of size 'size' from pool 'poolP'. -----------------------------------------------------------------------------*/ void * retval; if (size == 0) retval = NULL; else { bool gotMutexLock; gotMutexLock = MutexLock(poolP->mutexP); if (!gotMutexLock) retval = NULL; else { TPoolZone * const curPoolZoneP = poolP->currentzone; if (curPoolZoneP->pos + size < curPoolZoneP->maxpos) { retval = curPoolZoneP->pos; curPoolZoneP->pos += size; } else { uint32_t const zonesize = MAX(size, poolP->zonesize); TPoolZone * const newPoolZoneP = PoolZoneAlloc(zonesize); if (newPoolZoneP) { newPoolZoneP->prev = curPoolZoneP; newPoolZoneP->next = curPoolZoneP->next; curPoolZoneP->next = newPoolZoneP; poolP->currentzone = newPoolZoneP; retval= newPoolZoneP->data; newPoolZoneP->pos = newPoolZoneP->data + size; } else retval = NULL; } MutexUnlock(poolP->mutexP); } } return retval; }