PHP源码分析-HashTable API

一、创建HashTable

[cpp]  view plain copy
  1. int zend_hash_init(  
  2.     HashTable *ht,//指向一个HashTable  
  3.     uint nSize,//nSize是指这个HashTable可以拥有的元素的最大数量。在我们添加新的元素时,这个值会根据情况决定是否自动增长,这个值永远都是2的次方,如果你给它的值不是一个2的次方  
  4.             //的形式,那它将自动调整成大于它的最小的2的次方值。它的计算方法就像这样:nSize = pow(2, ceil(log(nSize, 2)))。(HashTable能够包含任意数量的元素,这个值只是为了提前申请好内存,提高性  
  5.             //能,省的不停的进行rehash操作)  
  6.     hash_func_t pHashFunction,//pHashFunction是早期的参数,为向前兼容所以没有去掉。直接赋值NULL即可。  
  7.     dtor_func_t pDestructor,//一个回调函数,当我们删除或者修改HashTable中其中一个元素时候便会调用,它的函数原型必须是这样的:void method_name(void *pElement),一般使用宏ZVAL_PTR_DTOR即可。  
  8.     zend_bool persistent//值为0或1,如果是1那么这个HashTable将永远存在于内存中,而不会在RSHUTDOWN阶段自动被注销掉。此时第一个参数ht所指向的地址必须是通过pemalloc()函数申请的。  
  9. );  

注:
#define ZVAL_PTR_DTOR (void (*)(void *)) zval_ptr_dtor_wrapper

删除元素,回调析构该元素。


二、添加

[cpp]  view plain copy
  1. int zend_hash_add(  
  2.     HashTable *ht,      //待操作的ht  
  3.     char *arKey,            //索引,如"my_key"  
  4.     uint nKeyLen,       //字符串索引的长度,如6  
  5.     void **pData,       //要插入的数据,注意它是void **类型的。  
  6.     uint nDataSize,     //例如,存放zval类型变量,那么nDataSize=sizeof(zval*)  
  7.     void *pDest         //如果操作成功,则pDest=*pData;  
  8. );  
  9.   
  10. int zend_hash_next_index_insert(  
  11.     HashTable *ht,  //待操作的ht  
  12.     void *pData,    //要插入的数据  
  13.     uint nDataSize,  
  14.     void **pDest  
  15. );  

三、更新

[cpp]  view plain copy
  1. int zend_hash_update(  
  2.     HashTable *ht,  
  3.     char *arKey,    //索引字符串  
  4.     uint nKeyLen,   //索引字符串长度  
  5.     void *pData,  
  6.     uint nDataSize,  
  7.     void **pDest  
  8. );  
  9.   
  10. int zend_hash_index_update(  
  11.     HashTable *ht,  
  12.     ulong h,  
  13.     void *pData,  
  14.     uint nDataSize,  
  15.     void **pDest  
  16. );  

四、查找

[cpp]  view plain copy
  1. int zend_hash_find(  
  2.     HashTable *ht,  
  3.     char *arKey,    //索引字符串  
  4.     uint nKeyLength,    //索引字符串长度  
  5.     void **pData    //找到元素,则将元素指向pData  
  6. );  
  7.   
  8. int zend_hash_index_find(  
  9.     HashTable *ht,  
  10.     ulong h,        //数字索引  
  11.     void **pData    //找到元素,则将元素指向pData  
  12. );  

五、检测

[cpp]  view plain copy
  1. int zend_hash_exists(  
  2.     HashTable *ht,   
  3.     char *arKey,   
  4.     uint nKeyLen  
  5. );  
  6.   
  7. int zend_hash_index_exists(  
  8.     HashTable *ht,   
  9.     ulong h  
  10. );  
这两个函数返回SUCCESS或者FAILURE,分别代表着是否存在


六、加速

ulong zend_get_hash_value(char *arKey, uint nKeyLen);//返回索引的hash值
通过使用zend_get_hash_value函数得到索引hash值,之后对hashTable进行添加、修改等操作使用quick系列函数可以避免重复计算字符串的hash值,达到加速加速的目的。

[cpp]  view plain copy
  1. int zend_hash_quick_add(  
  2.     HashTable *ht,  
  3.     char *arKey,  
  4.     uint nKeyLen,  
  5.     ulong hashval,  //zend_get_hash_value函数得到的hash值  
  6.     void *pData,  
  7.     uint nDataSize,  
  8.     void **pDest  
  9. );  
  10.   
  11. int zend_hash_quick_update(  
  12.     HashTable *ht,  
  13.     char *arKey,  
  14.     uint nKeyLen,  
  15.     ulong hashval,  
  16.     void *pData,  
  17.     uint nDataSize,  
  18.     void **pDest  
  19. );  
  20.   
  21. int zend_hash_quick_find(  
  22.     HashTable *ht,  
  23.     char *arKey,  
  24.     uint nKeyLen,  
  25.     ulong hashval,  
  26.     void **pData  
  27. );  
  28.   
  29. int zend_hash_quick_exists(  
  30.     HashTable *ht,  
  31.     char *arKey,  
  32.     uint nKeyLen,  
  33.     ulong hashval  
  34. );  

七、数组之间的复制与合并

[cpp]  view plain copy
  1. void zend_hash_copy(  
  2.     HashTable *target,  //*source中的所有元素都会通过pCopyConstructor函数Copy到*target中去  
  3.     HashTable *source,  //target中原有的与source中索引位置的数据会被替换掉,而其它的元素则会被保留,原封不动。  
  4.     copy_ctor_func_t pCopyConstructor,    
  5.     void *tmp,//tmp参数是为了兼容PHP4.0.3以前版本的,现在赋值为NULL即可。  
  6.     uint size   //size参数代表每个元素的大小,对于PHP语言中的数组来说,这里的便是sizeof(zval*)了。  
  7. );  
  8.   
  9. void zend_hash_merge(  
  10.     HashTable *target,  
  11.     HashTable *source,  
  12.     copy_ctor_func_t pCopyConstructor,  
  13.     void *tmp,  
  14.     uint size,  
  15.     int overwrite  
  16. );  
zend_hash_merge()与zend_hash_copy唯一的不同便是多了个int类型的overwrite参数,当其值非0的时候,两个函数的工作是完全一样的;如果overwrite参数为0,则zend_hash_merge函数就不会对target中已有索引的值进行替换了。

[cpp]  view plain copy
  1. void zend_hash_merge_ex(  
  2.     HashTable *target,  
  3.     HashTable *source,  
  4.     copy_ctor_func_t pCopyConstructor,   
  5.     uint size,  
  6.     merge_checker_func_t pMergeSource,  
  7.     void *pParam  
  8. );  
zend_hash_merge_ex函数又繁琐了些,与zend_hash_copy相比,其多了两个参数,多出来的pMergeSoure回调函数允许我们选择性的进行merge,而不是全都merge。


八、遍历

PHP中数组的本质是HashTable,可以使用foreach来遍历PHP中的数组。在内核中则是使用zend_hash_apply函数。
zend_hash_apply接收一个回调函数,并将HashTable的每一个元素都传递给回调函数。这里的回调函数相当于PHP中的foreach循环主体。

[html]  view plain copy
  1. 回调函数的返回值有一个共同的约定:  
  2. ZEND_HASH_APPLY_KEEP        结束当前请求,进入下一个循环。与PHP语言forech语句中的一次循环执行完毕或者遇到   
  3.                             continue关键字的作用一样。  
  4. ZEND_HASH_APPLY_STOP        跳出,与PHP语言forech语句中的break关键字的作用一样。  
  5. ZEND_HASH_APPLY_REMOVE      删除当前的元素,然后继续处理下一个。相当于在PHP语言中:unset($foo  
  6.                             [$key]);continue;  

1、无索引遍历
typedef int (*apply_func_t)(void *pDest TSRMLS_DC);
void zend_hash_apply(HashTable *ht,apply_func_t apply_func TSRMLS_DC);


看看PHP语言中的forech循环。

[php]  view plain copy
  1. <?php  
  2. function foreach_test($arr){  
  3.     foreach($arr as $val) {  
  4.         echo "The value is: $val\n";  
  5.     }  
  6. }  
  7. ?>  

在内核中实现函数foreach_test。

[php]  view plain copy
  1. //zend_hash_apply回调函数,相当于PHP中的foreach主体  
  2. int php_foreach_body(zval **zval TSRMLS_DC){  
  3.     zval tmpcopy = **zval;      //重新copy一个zval,防止破坏原数据  
  4.     zval_copy_ctor(&tmpcopy);  
  5.   
  6.     INIT_PZVAL(&tmpcopy);       //初始化refcount__gc=1, is_ref__gc=0  
  7.     convert_to_string(&tmpcopy);     //转换为字符串  
  8.   
  9.     php_printf("The value is:");  
  10.     PHPWRITE(Z_STRVAL(tmpcopy), Z_STRLEN(tmpcopy));  
  11.     php_printf("\n");  
  12.   
  13.     zval_dtor(&tmpcopy);  
  14.     return ZEND_HASH_APPLY_KEEP;  
  15. }  
  16.   
  17. PHP_FUNCTION(foreach_test){  
  18.     zval *arr;  //相当于php中function的参数$arr  
  19.     if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) ){  
  20.         RETURN_NULL();  
  21.     }  
  22.       
  23.     zend_hash_apply(Z_ARRVAL_P(arr), php_foreach_body TSRML_CC);    //开始遍历  
  24. }  

2、有索引遍历

为了能在遍历时同时接收索引的值,可以使用void zend_hash_apply_with_arguments:

[cpp]  view plain copy
  1. typedef int (*apply_func_args_t)(void *pDest,int num_args, va_list args, zend_hash_key *hash_key);  
  2. void zend_hash_apply_with_arguments(  
  3.     HashTable *ht,  
  4.     apply_func_args_t apply_func,   
  5.     int numargs,    //传递参数的个数,通过指定参数个数,决定va_end()  
  6.     ...  
  7. );//这个函数通过C语言中的可变参数特性来接收参数。  

PHP中带索引的遍历

[php]  view plain copy
  1. <?php  
  2. foreach($arr as $key => $val)  
  3. {  
  4.     echo "The value of $key is: $val\n";  
  5. }  
  6. ?>  

内核中实现:

[php]  view plain copy
  1. int php_foreach_body_and_key(zval **val, int num_args, va_list args, zend_hash_key *hash_key){  
  2.     zval tmpcopy = **val;   //重新copy一个zval,防止破坏原数据  
  3.       
  4.     php_printf("附带参数个数%d<br>", num_args);  
  5.   
  6.     char **a, **b;  
  7.     a = va_arg(args, char**);   //args是可变参数的起始地址  
  8.     b = va_arg(args, char**);  
  9.     php_printf("参数1:%s, 参数2:%s<br>", *a, *b);  
  10.   
  11.     INIT_PZVAL(&tmpcopy);  
  12.     zval_copy_ctor(&tmpcopy);  
  13.     convert_to_string(&tmpcopy);  
  14.   
  15.     php_printf("The value of ");  
  16.     if( hash_key->nKeyLength ){  
  17.         PHPWRITE(hash_key->arKey, hash_key->nKeyLength);  
  18.     }else{  
  19.         php_printf("%ld", hash_key->h);  
  20.     }  
  21.     php_printf(" is: ");  
  22.     PHPWRITE(Z_STRVAL(tmpcopy), Z_STRLEN(tmpcopy));  
  23.     php_printf("\n");  
  24.   
  25.     zval_dtor(&tmpcopy);  
  26.       
  27.     return ZEND_HASH_APPLY_KEEP;  
  28. }  
  29.   
  30. PHP_FUNCTION(foreach_test){  
  31.     zval *arr;  //相当于php中function的参数$arr  
  32.     if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) ){  
  33.         RETURN_NULL();  
  34.     }  
  35.       
  36.     char *a="var1", *b="var2"//可变参数a,b  
  37.   
  38.     zend_hash_apply_with_arguments(arrht, php_foreach_body_and_key, 2, &a, &b); //开始遍历,并传递2个可参数  
  39. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值