HashTable初始化,在使用HashTable之前要先执行初始化,下边就看看初始化时都做了什么,
Zend/zend_hash.c
static const Bucket *uninitialized_bucket = NULL; //声明一个全局Bucket * ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC) { uint i = 3; /* 初始化HashTable大小时,是按照2的整数次幂来初始化,最小为2的3次幂 */ SET_INCONSISTENT(HT_OK); /* 调试用,初始化当前HashTable的状态 */ if (nSize >= 0x80000000) { /* HashTable的最大值,防止溢出 */ ht->nTableSize = 0x80000000; } else { while ((1U << i) < nSize) { i++; } ht->nTableSize = 1 << i; } ht->nTableMask = 0; /* 0 表示 ht->arBuckets 还没有初始化 */ ht->pDestructor = pDestructor; /* 设置析构函数为传入的函数指针 */ ht->arBuckets = (Bucket**)&uninitialized_bucket; ht->pListHead = NULL; ht->pListTail = NULL; ht->nNumOfElements = 0; ht->nNextFreeElement = 0; ht->pInternalPointer = NULL; ht->persistent = persistent; ht->nApplyCount = 0; ht->bApplyProtection = 1; return SUCCESS; }
ht 是一个指向HashTable变量的指针, 它可以定义为直接值形式, 也可以通过emalloc()/pemalloc()动态分配, 或者更常见的是使用ALLOC_HASHTABLE(ht). ALLOC_HASHTABLE()宏使用了一个特定内存池的预分配块来降低内存分配所需的时间, 相比于ht = emalloc(sizeof(HashTable));它通常是首选.
nSize 应该被设置为HashTable期望存储的最大元素个数. 如果向这个HashTable中尝试增加多于这个数的元素, 它将会自动增长, 不过有一点需要注意的是, 这里Zend重建整个新扩展的HashTable的索引的过程需要耗费不少的处理时间. 如果nSize不是2的幂, 它将被按照下面公式扩展为下一个2的幂:
nSize = pow(2, ceil(log(nSize, 2)));
pHashFunction 是旧版本Zend引擎的遗留参数, 它不在使用, 因此这个值应该被设置为NULL. 在早期的Zend引擎中, 这个值指向一个用以替换标准的DJBX33A(一种常见的抗碰撞哈希算法, 用来将任意字符串key转换到可重演的整型值)的可选哈希算法.
pDestructor 指向当从HashTable删除元素时应该被调用的函数, 比如当使用zend_hash_del()删除或使用zend_hash_update()替换. 析构器函数的原型如下:
void method_name(void *pElement);
pElement指向指向要从HashTable中删除的元素.
persistent 它只是一个简单的标记, 引擎会直接传递给pemalloc()函数. 所有需要保持跨请求可用的HashTable都必须设置这个标记, 并且必须调用pemalloc()分配.
可见,zend_hash_init并没有为存放buckets申请内存空间,只是设置了初始化的size,并且设置了nTableMask为0,表示 ht->arBuckets还未初始化。整个HashTable的size是按照2的整数次幂申请的,最小为2的3次幂,若空间不够则尝试2的4次 幂、2的5次幂……直到大于传入的size。
那么buckets的空间是什么时候申请的呢?答案就是在操作HashTable的时候,看下一篇。~..~