书上的开篇是简单介绍链表和数组了,想起自己当年,c语言数据结构敲起来手到拈来,各种随便姿势敲,php弄久了,相当生涩。找时间且切题,数据结构敲一遍,应该很快回暖吧。(没人教,自己探索了这么久,才慢慢理解这东西的进阶路线,如果有会的人带,不至于想现在这样,再重拾c语言,这晃,就1年光景)
zend内核的核心存储结构,就是HashTable了。
初始化并创建一个hashtable: zend_hash_init(
HashTable *ht,
uint nSize,//大小会自动被更改成最接近的,并且大于nSize的2的幂的值
hash_func_t pHashFunction,//NULL
dtor_func_t pDestructor,
/*回调函数,当删除hashtable中的一个元素时候,就会调用,函数原型void method_name(voidpElement);pElment指向你要删除的元素
zend_bool persistent //这是标记是否持久化内存,引擎会传递给pemalloc。有一个使用的例子:在php在请求最开始,初始化全局变量时候:
zend_hash_init(&EG(symbol_table),50,NULL,ZVAL_PTR_DTOR,0),50不是2的幂,会被更改成64(zend/zend_execute_API.c)
#define ZVAL_PTR_DTOR (void (*)(void *)) zval_ptr_dtor_wrapper (zend/zend_variables.h)
由此可见,如果删除hashtable的元素,比如unset(),时候,就会调用这个ZVAL_PTR_DTOR
*/
)
添加||修改
常用的有4个函数
int zend_hash_add(
HashTable *ht, //待操作的ht
char *arKey, //索引,如"my_key"
uint nKeyLen, //字符串索引的长度,如6
void **pData, //要插入的数据,注意它是void **类型的。int *p,i=1;p=&i,pData=&p;。
uint nDataSize,
void pDest //如果操作成功,则pDest=pData;
);
int zend_hash_update(
HashTable *ht,
char *arKey,
uint nKeyLen,
void *pData,
uint nDataSize,
void **pDest
);
int zend_hash_index_update(
HashTable *ht,
ulong h,
void *pData,
uint nDataSize,
void **pDest
);
int zend_hash_next_index_insert(
HashTable *ht,
void *pData,
uint nDataSize,
void **pDest
);
前两个是关联数组的,后两个是普通数组(就是字符串索引或者数字的顺序索引)
前两个 :update和add的区别呢,就是如果add发现已经存在的数据,直接return,但是update会修改
使用例子: $foo['bar'] = "xxx";
zend_hash_add(ht,"bar",sizeof("bar"),"xxx",sizeof("xxx"),NULL);
后两个: zend_hash_next_index_insert会自己计算出下一个index的索引值,因此不需要索引参数。如果需要下一个的索引值,可以用ulong nextid = zend_hash_next_free_element(ht); 配合zend_hash_index_update(ht,nextid,&data,sizeof(data),NULL);
查找索引值:
int zend_hash_find(HashTable *ht,char *arKey,unit nKeyLen,void **pData);
void hash_sample(HashTable *ht, sample_data *data1){//往hashtable内添加一个新值,并且提取出来
sample_data *data2;
ulong targetID = zend_hash_next_free_element(ht);
if(zend_hash_index_update(ht,targetID,data1,sizeof(sample_data),NULL) == FAILURE){
return; //理论不发生
}
if(zend_hash_index_find(ht,targetID,(void **)&data2) == FAILURE){
return; //理论不可能
}
此处的data1 != data2,但*data1 == *data2。即他们的值相同,但是地址不同,因为hashtable的更新值,是把要插入的数据(data1)copy一份。所以hash桶存的指针与data1穿来的指针分别是两个独立的空间
}
检查数据是否存在:
int zend_hash_exists(HashTable *ht,char *arKey,uint nKeyLen);
int zend_hash_index_exists(HashTable *ht,ulong h);
这两个函数都不会返回SUCCESS/FAILURE ,只有0和1
if(zend_hash_exists(EG(active_symbol_table,"foo",sizeof("foo")))){
}
else{
}
这段代码等价与 isset($foo) 所以这里是当前符号表active_symbol_table
拷贝和合并
void zend_hash_copy(HashTable *a,HashTabel *b,copy_ctor_func_t pCopyConstructor,void *tmp,unit size);
tmp 在4.3以后为NULL,size是成员占的字节数,对于用户空间的hash变量,则为sizeof(zval *)。b中的每个元素会拷贝到a中去,并且由pCopyConstructor函数进行处理。对于数组变量这种类型的数据,是用引用计数的方式,不是直接销毁。
zend_hash_merge和zend_hash_copy的唯一区别是,多一个int overwrite参数,表示是否覆盖
void *tmp, uint size, int overwrite);
还有一种方式,是选择性拷贝,自定义一个函数进行选择性拷贝:zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, uint size, merge_checker_func_t pMergeSource, void *pParam);
typedef zend_bool (*merge_checker_func_t)(HashTable *target_ht, void *source_data, zend_hash_key *hash_key, void *pParam);//这是函数原型
zend_bool choice(HashTable *ht,void *pData,zend_hash_key *hash_key,void *pPrama){ //这里进行选择
return (hash_key->arKey && hash_key->nKeyLength); //通过一个key和长度确定一个元素(可以看看hash_key的结构)
}
void merge_func(HashTable *a,HashTable *b){
zend_hash_merge_ex(HashTable *a,HashTable *b,zval_add_ref,sizeof(zval *),choice,NULL);
}