書上的開篇是簡單介紹鏈表和數(shù)組了,想起自己當(dāng)年鸠项,c語言數(shù)據(jù)結(jié)構(gòu)敲起來手到拈來,各種隨便姿勢(shì)敲子姜,php弄久了祟绊,相當(dāng)生澀楼入。找時(shí)間且切題,數(shù)據(jù)結(jié)構(gòu)敲一遍牧抽,應(yīng)該很快回暖吧嘉熊。(沒人教,自己探索了這么久扬舒,才慢慢理解這東西的進(jìn)階路線阐肤,如果有會(huì)的人帶,不至于想現(xiàn)在這樣讲坎,再重拾c語言孕惜,這晃,就1年光景)
zend內(nèi)核的核心存儲(chǔ)結(jié)構(gòu)晨炕,就是HashTable了衫画。
初始化并創(chuàng)建一個(gè)hashtable: zend_hash_init(
HashTable *ht,
uint nSize,//大小會(huì)自動(dòng)被更改成最接近的,并且大于nSize的2的冪的值
hash_func_t pHashFunction,//NULL
dtor_func_t pDestructor,
/*回調(diào)函數(shù),當(dāng)刪除hashtable中的一個(gè)元素時(shí)候瓮栗,就會(huì)調(diào)用削罩,函數(shù)原型void method_name(voidpElement);pElment指向你要?jiǎng)h除的元素
zend_bool persistent //這是標(biāo)記是否持久化內(nèi)存,引擎會(huì)傳遞給pemalloc。有一個(gè)使用的例子:在php在請(qǐng)求最開始,初始化全局變量時(shí)候:
zend_hash_init(&EG(symbol_table),50,NULL,ZVAL_PTR_DTOR,0),50不是2的冪费奸,會(huì)被更改成64(zend/zend_execute_API.c)
#define ZVAL_PTR_DTOR (void (*)(void *)) zval_ptr_dtor_wrapper (zend/zend_variables.h)
由此可見弥激,如果刪除hashtable的元素,比如unset(),時(shí)候愿阐,就會(huì)調(diào)用這個(gè)ZVAL_PTR_DTOR
*/
)
添加||修改
常用的有4個(gè)函數(shù)
int zend_hash_add(
HashTable *ht, //待操作的ht
char *arKey, //索引秆撮,如"my_key"
uint nKeyLen, //字符串索引的長度,如6
void **pData, //要插入的數(shù)據(jù)换况,注意它是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
);
前兩個(gè)是關(guān)聯(lián)數(shù)組的舒裤,后兩個(gè)是普通數(shù)組(就是字符串索引或者數(shù)字的順序索引)
前兩個(gè) :update和add的區(qū)別呢,就是如果add發(fā)現(xiàn)已經(jīng)存在的數(shù)據(jù)觉吭,直接return腾供,但是update會(huì)修改
使用例子: $foo['bar'] = "xxx";
zend_hash_add(ht,"bar",sizeof("bar"),"xxx",sizeof("xxx"),NULL);
后兩個(gè): zend_hash_next_index_insert會(huì)自己計(jì)算出下一個(gè)index的索引值,因此不需要索引參數(shù)鲜滩。如果需要下一個(gè)的索引值伴鳖,可以用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內(nèi)添加一個(gè)新值,并且提取出來
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; //理論不發(fā)生
}
if(zend_hash_index_find(ht,targetID,(void **)&data2) == FAILURE){
return; //理論不可能
}
此處的data1 != data2,但*data1 == *data2徙硅。即他們的值相同榜聂,但是地址不同,因?yàn)閔ashtable的更新值嗓蘑,是把要插入的數(shù)據(jù)(data1)copy一份须肆。所以hash桶存的指針與data1穿來的指針分別是兩個(gè)獨(dú)立的空間
}
檢查數(shù)據(jù)是否存在:
int zend_hash_exists(HashTable *ht,char *arKey,uint nKeyLen);
int zend_hash_index_exists(HashTable *ht,ulong h);
這兩個(gè)函數(shù)都不會(huì)返回SUCCESS/FAILURE 匿乃,只有0和1
if(zend_hash_exists(EG(active_symbol_table,"foo",sizeof("foo")))){
}
else{
}
這段代碼等價(jià)與 isset($foo) 所以這里是當(dāng)前符號(hào)表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是成員占的字節(jié)數(shù)豌汇,對(duì)于用戶空間的hash變量幢炸,則為sizeof(zval *)。b中的每個(gè)元素會(huì)拷貝到a中去拒贱,并且由pCopyConstructor函數(shù)進(jìn)行處理宛徊。對(duì)于數(shù)組變量這種類型的數(shù)據(jù),是用引用計(jì)數(shù)的方式逻澳,不是直接銷毀闸天。
zend_hash_merge和zend_hash_copy的唯一區(qū)別是,多一個(gè)int overwrite參數(shù)赡盘,表示是否覆蓋
void *tmp, uint size, int overwrite);
還有一種方式号枕,是選擇性拷貝,自定義一個(gè)函數(shù)進(jìn)行選擇性拷貝: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);//這是函數(shù)原型
zend_bool choice(HashTable *ht,void *pData,zend_hash_key *hash_key,void *pPrama){ //這里進(jìn)行選擇
return (hash_key->arKey && hash_key->nKeyLength); //通過一個(gè)key和長度確定一個(gè)元素(可以看看hash_key的結(jié)構(gòu))
}
void merge_func(HashTable *a,HashTable *b){
zend_hash_merge_ex(HashTable *a,HashTable *b,zval_add_ref,sizeof(zval *),choice,NULL);
}