转自:http://www.cnblogs.com/kernel_hcy/archive/2009/10/23/1588873.html
typedef enum { TYPE_UNSET, /* 数据的类型未设置, 这几种数据类型使用了面向对象的设计思想, 这个类型相当于父类型,继承关系见后面 */ TYPE_STRING, /* 字符串类型 */ TYPE_COUNT, /* COUNT类型 */ TYPE_ARRAY, /* 数组类型 */ TYPE_INTEGER, /* 整数类型 */ TYPE_FASTCGI, /* FASTCGI类型 */ TYPE_CONFIG /* CONFIG类型 */ } data_type_t;
#define DATA_UNSET \ data_type_t type; \ buffer *key; \ int is_index_key; /* 1 if key is a array index */ \ struct data_unset *(*copy)(const struct data_unset *src); \ void (* free)(struct data_unset *p); \ void (* reset)(struct data_unset *p); \ int (*insert_dup)(struct data_unset *dst, struct data_unset *src); \ void (*print)(const struct data_unset *p, int depth) typedef struct data_unset { DATA_UNSET; } data_unset;其中,UNSET类型数据的定义中,数据的实际定义部分使用宏DATA_UNSET,这样可以方便其他类型在定义中直接引用DATA_UNSET宏来模拟继承。在宏DATA_UNSET中,定义了下面五个函数指针:
struct data_unset *(*copy)(const struct data_unset *src); void (* free)(struct data_unset *p); void (* reset)(struct data_unset *p); int (*insert_dup)(struct data_unset *dst, struct data_unset *src); void (*print)(const struct data_unset *p, int depth)
data_string *data_string_init(void) { data_string *ds; /* 分配内存空间。 这里用的是calloc函数,分配的空间会自动清零。 */ ds = calloc(1, sizeof(*ds)); assert(ds); /* 初始化各个数据成员, 这里调用buffer_init函数,主要就是分配内存空间 */ ds->key = buffer_init(); ds->value = buffer_init(); /*确定成员函数的具体调用函数,对函数指针赋值*/ ds->copy = data_string_copy; ds->free = data_string_free; ds->reset = data_string_reset; ds->insert_dup = data_string_insert_dup; ds->print = data_string_print; ds->type = TYPE_STRING; return ds; }
typedef struct { /* UNSET类型的指针型数组,存放数组中的元素 */ data_unset **data; /* 存放着排好序的各个元素的下标的数组 */ size_t *sorted; size_t used; /* data中使用了的长度,也就是数组中元素个数 */ /* data的大小。data的大小会根据数据的多少变化,会为以后的数据预先分 配空间 */ size_t size; size_t unique_ndx; /* */ /* 比used大的最小的2的倍数。也就是离used最近的且比used大的2的倍 数 ,用于在数组中利用二分法查找元素*/ size_t next_power_of_2; /* data is weakref, don't bother the data */ /* data就是一个指针,不用关系其所指向的内容 */ int is_weakref; } array;
typedef struct { DATA_UNSET; array *value; } data_array;这个定义了一个array类型的数据,也就是说,通用数组中存放的数据可以是通用数组也可以是通用数据类型,这样可以形成多维的通用数组。
static int array_get_index(array *a, const char *key, size_t keylen, int *rndx) { /*参数keylen是key的长度*/ int ndx = -1; int i, pos = 0; if (key == NULL) return -1; /* try to find the string */ /* * sorted数组是个下标数组,存放的是排好序的输入元素的下标, * 相当于一个排好序的数组。 * 利用sorted数组进行二分查找。 * 若找到,返回元素在data数组中的位置,并通过rndx返回 * 其在sorted数组中的位置。 * 若没有找到,通过rndx返回此元素在sorted中的位置,并返回-1 */ /* pos中存放的是元素在数组data中的位置 */ /* 当data的空间不够时,通用数组每次为data增加16个空间,第一次初始化时, data的长度为16。因此,size始终是16的倍数。 used可以为任何数值,当然要大于等于0,小于size。 而next_power_of_2是大于used最小的2的倍数,如used=5,那么 next_power_of_2就等于8。 这样,used始终大于等于next_power_of_2的1/2。 */ /* 在这儿的二分搜索中,next_power_of_2是个很有创意的技巧。 next_power_of_2类似于一个标杆,利用这个标杆进行二分搜索可以减少很多 出错的几率,也使程序更加易懂。效率上当然没有什么损失。下面的程序读者可 自行看看,并不是很难。 */ for (i = pos = a->next_power_of_2 / 2; ; i >>= 1) { int cmp; if (pos < 0) { pos += i; } else if (pos >= (int)a->used) { pos -= i; } else { /* 比较两个元素的key值 */ cmp = buffer_caseless_compare(key, keylen , a->data[a->sorted[pos]]->key->ptr , a->data[a->sorted[pos]]->key->used ); if (cmp == 0) { /* found */ ndx = a->sorted[pos]; break; } else if (cmp < 0) {/* 所找数据在前半部分 */ pos -= i; } else { /* 所找数据在后半部分*/ pos += i; } } if (i == 0) break; } if (rndx) *rndx = pos; return ndx; }在上面列出的函数中,还有一个函数要重点讲解一下,也是最复杂的一个函数:int array_insert_unique(array *a, data_unset *str)。这个函数将数据str插入到数组中,当并不是单纯的插入,如果数组中存在key于str相同的数据,则把str的内容拷贝到这个数据中。
int array_insert_unique(array *a, data_unset *str) { int ndx = -1; int pos = 0; size_t j; /* generate unique index if neccesary */ if (str->key->used == 0 || str->is_index_key) { buffer_copy_long(str->key, a->unique_ndx++); str->is_index_key = 1; } /* 在数组中查找与str具有相同key的数据 */ if (-1 != (ndx = array_get_index(a, str->key->ptr, str->key->used, &pos))) { /* 找到,复制 */ if (a->data[ndx]->type == str->type) { str->insert_dup(a->data[ndx], str); } else { fprintf(stderr, "a\n"); } return 0; } /* 当数组的长度大于最大值时,不进行插入,并返回-1 */ if (a->used+1 > INT_MAX) { /* we can't handle more then INT_MAX entries: see array_get_index() */ return -1; } if (a->size == 0) { /* 数组为空 */ /* 初始data的长度为16 */ a->size = 16; a->data = malloc(sizeof(*a->data) * a->size); a->sorted = malloc(sizeof(*a->sorted) * a->size); assert(a->data); assert(a->sorted); for (j = a->used; j < a->size; j++) a->data[j] = NULL; } else if (a->size == a->used) { /* data已经满了,对data进行扩容,增加16个空间。 */ /* 这就是为什么size一定是16的倍数 */ a->size += 16; a->data = realloc(a->data, sizeof(*a->data) * a->size); a->sorted = realloc(a->sorted, sizeof(*a->sorted) * a->size); assert(a->data); assert(a->sorted); for (j = a->used; j < a->size; j++) a->data[j] = NULL; } ndx = (int) a->used; a->data[a->used++] = str; /* 在上面调用函数array_get_index的时候, 已将str应该在数组sorted中位置存放在了pos中。 */ if (pos != ndx /* 要插入的位置在中部 */&&((pos < 0) /* 在开始位置插入 */ ||buffer_caseless_compare(str->key->ptr , str->key->used , a->data[a->sorted[pos]]->key->ptr , a->data[a->sorted[pos]]->key->used ) > 0)) { /* 判断当前pos所对应的元素是否比str小,若是,这pos后移一位 */ pos++; } /* 移动sorted数组中后面的数据,腾出位置。 */ if (pos != ndx) { memmove(a->sorted + (pos + 1), a->sorted + (pos), (ndx - pos) * sizeof(*a->sorted)); } /* insert */ a->sorted[pos] = ndx; /* 如果used==next_power_of_2时,扩展next_power_of_2 */ if (a->next_power_of_2 == (size_t)ndx) a->next_power_of_2 <<= 1; return 0; }