×

HashTable使用手册篇

96
等哈哈咯
2017.08.07 21:05* 字数 779

书上的开篇是简单介绍链表和数组了,想起自己当年,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);
}

php进阶c扩展
Web note ad 1