HashTable添加和更新的函数:

有4个主要的函数用于插入和更新HashTable的数据:

int zend_hash_add(HashTable *ht, char *arKey, uint nKeyLen,void **pData, uint nDataSize, void *pDest);  

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);

这里的前两个函数用于新增关联索引数据, 比如$foo['bar'] = 'baz';对应的C语言代码如下:

zend_hash_add(fooHashTbl, "bar", sizeof("bar"), &barZval, sizeof(zval*), NULL);

zend_hash_add()和zend_hash_update()唯一的区别是如果key存在, zend_hash_add()将会失败.

接下来的两个函数以类似的方式处理数值索引的HashTable. 这两行之间的区别在于是否指定索引 或者说是否自动赋值为下一个可用索引.

如果需要存储使用zend_hash_next_index_insert()插入的元素的索引值, 可以调用zend_hash_next_free_element()函数获得:

ulong nextid = zend_hash_next_free_element(ht);
zend_hash_index_update(ht, nextid, &data, sizeof(data), NULL);

HashTable添加更新元素:

在初始化了HashTable之后,可以用zend_hash_add来向HashTable添加元素 ,zend_hash_add是一个宏:

#define zend_hash_add(ht, arKey, nKeyLength, pData, nDataSize, pDest) \
_zend_hash_add_or_update(ht, arKey, nKeyLength, pData, nDataSize, pDest, HASH_ADD ZEND_FILE_LINE_CC)

我们来看看_zend_hash_add_or_update的定义,同样在Zend/zend_hash.c下

ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, void *pData,
uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
{
ulong h; /*存贮arKey在hash之后的值*/
uint nIndex; /*存贮h & nTableMask之后的值*/
Bucket *p;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH(); //这个还不知道是什么意思
#endif IS_CONSISTENT(ht); //调试信息输出 if (nKeyLength <= ) { //添加的是字符串索引的,所以nKeyLength不可能<=0
#if ZEND_DEBUG
ZEND_PUTS("zend_hash_update: Can't put in empty key\n");
#endif
return FAILURE;
} /**
* 检查是否初始化buckets空间,若没有初始化则初始化buckets的内存空间
* 为arBuckets申请内存,为nTableSize赋值,因为在zend_hash_init里边nTableSize设置为0
*/
CHECK_INIT(ht); h = zend_inline_hash_func(arKey, nKeyLength); /* 计算key的hash值 */
nIndex = h & ht->nTableMask; /* 利用掩码得到key的实际存储位置 */ p = ht->arBuckets[nIndex]; /* 取到指定位置的bucket指针 */
while (p != NULL) { /* 若指针不为空,则表示当前位置已有bucket了 */
if (p->arKey == arKey || ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
/* 若当前bucket的key和要存入的key相同,那么需要更新 */
if (flag & HASH_ADD) { /* 如果当前指定是add操作,此时就返回失败了 */
return FAILURE;
} /**
* interruptions,打断,中断的意思
*/
HANDLE_BLOCK_INTERRUPTIONS();
#if ZEND_DEBUG
if (p->pData == pData) {
ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n");
HANDLE_UNBLOCK_INTERRUPTIONS();
return FAILURE;
}
#endif
if (ht->pDestructor) { /* 调用析构函数析构掉原先的值 */
ht->pDestructor(p->pData);
}
UPDATE_DATA(ht, p, pData, nDataSize); /* 替换为新的值 */
if (pDest) {
*pDest = p->pData;
}
HANDLE_UNBLOCK_INTERRUPTIONS();
return SUCCESS;
} p = p->pNext; /* 若当前key和要存入的key不同,那么查找Hash拉链的下一个bucket
} /* 运行到这里,表示没有找到任何已存在的key和要存入的key相同的,那么申请一个sizeof(bucket)+nKeyLength大小的新空间给key */
//interned用google搜了一下,发现是'字符串驻留'的概念,也没搞太清楚
//大概就是维护了一个驻留池,会把在编译期间相同的字符串只保留一份拷贝。
if (IS_INTERNED(arKey)) {
p = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
if (!p) {
return FAILURE;
}
p->arKey = arKey;
} else {
p = (Bucket *) pemalloc(sizeof(Bucket) + nKeyLength, ht->persistent); //柔性数组的概念
if (!p) {
return FAILURE;
}
p->arKey = (const char*)(p + ); //p+1就是arKey的起始地址
memcpy((char*)p->arKey, arKey, nKeyLength);
}
p->nKeyLength = nKeyLength;
INIT_DATA(ht, p, pData, nDataSize); /* 执行赋值 */
p->h = h;
CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]); /* 设置乱七八槽的指针 */
if (pDest) {
*pDest = p->pData;
} HANDLE_BLOCK_INTERRUPTIONS();
CONNECT_TO_GLOBAL_DLLIST(p, ht); /* 将Bucket 加入到HashTable的双向链表中 */
ht->arBuckets[nIndex] = p;
HANDLE_UNBLOCK_INTERRUPTIONS(); ht->nNumOfElements++;
// 如果HashTable已满,重新调整HashTable的大小。
ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */
return SUCCESS;

上边的函数中涉及到宏CHECK_INIT,在Zend_hash.c中定义如下,

#define CHECK_INIT(ht) do {                                                \
if (UNEXPECTED((ht)->nTableMask == )) { \
(ht)->arBuckets = (Bucket **) pecalloc((ht)->nTableSize, sizeof(Bucket *), (ht)->persistent); \
(ht)->nTableMask = (ht)->nTableSize - ; \
} \
} while ()

INIT_DATA宏的定义,

#define INIT_DATA(ht, p, pData, nDataSize);                             \
if (nDataSize == sizeof(void*)) { \
memcpy(&(p)->pDataPtr, pData, sizeof(void *)); \
(p)->pData = &(p)->pDataPtr; \
} else { \
(p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);\
if (!(p)->pData) { \
pefree_rel(p, (ht)->persistent); \
return FAILURE; \
} \
memcpy((p)->pData, pData, nDataSize); \
(p)->pDataPtr=NULL; \
}

这里有一个tricks,PHP判断数据的大小和一个void指针相同时,就不为其申请额外的空间,而是将数据copy到pDataPtr字段中,也就是 说,如果你add到HashTable的是一个指针,那么他直接被保存在pDataPtr字段中,同时pData字段也会保存一份。如果你add到 HashTable的是一个更大的结构,那么PHP会为这个结构单独申请内存空间,将数据copy到这片新申请的内存空间中,然后将pDataPtr设置 为NULL。

php Hash Table(四) Hash Table添加和更新元素的更多相关文章

  1. <hash命令:显示、添加或清除哈希表>

    linux系统下的hash指令: 说明:linux系统下会有一个hash表,当你刚开机时这个hash表为空,每当你执行过一条命令时,hash表会记录下这条命令的路径,就相当于缓存一样.第一次执行命令s ...

  2. OAF TABLE中第一列添加事件不生效

    我遇到一个比较诡异的现象 在TABLE中,我在TABLE的第一列添加了一个MessageCheckBox,并为其设置全局刷新的FireAction事件selection, 但是点击勾选框按钮之后,事件 ...

  3. 摘:通过ICursor对Table进行操作(添加、修改、删除)

    通过ICursor对Table进行操作(添加.修改.删除) 连接上数据表的目的就是对其进行包括浏览.添加.修改.删除等基本操作. 浏览功能,之前文章中一提到,就是将Itable转换为DataTable ...

  4. 通过ICursor对Table进行操作(添加、修改、删除)

    通过ICursor对Table进行操作(添加.修改.删除) 2010-03-16 16:07:37|  分类: 工作|举报|字号 订阅 来自:http://blog.163.com/liuyang12 ...

  5. Lua 学习之基础篇四<Lua table(表)>

    table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组.字典等. Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil. Lua ta ...

  6. Hash表及hash算法的分析

    Hash表中的一些原理/概念,及根据这些原理/概念: 一.       Hash表概念 二.       Hash构造函数的方法,及适用范围 三.       Hash处理冲突方法,各自特征 四.   ...

  7. OpenFlow Switch学习笔记(五)——Group Table、Meter Table及Counters

    本文主要详述OpenFlow Switch的另外两个主要组件——Group Table和Meter Table,它们在整个OpenFlow Swtich Processing中也起到了重要作用. 1. ...

  8. Hash::make与Hash::check

    调用方法之前要先去引用: use Illuminate\Support\Facades\Hash; 可以调用 Hash 门面上的 make 方法对存储密码进行哈希: $pwd = Hash::make ...

  9. MySQL删除大表时潜在的问题(drop table,truncate table)

    来源于:https://www.cnblogs.com/CtripDBA/p/11465315.html,侵删,纯截图,避免吸引流量之嫌 case1,删除大表时,因为清理自适应hash索引占用的内容导 ...

随机推荐

  1. MyEclipse10安装SVN插件

    一.下载SVN插件subclipse 下载地址:http://subclipse.tigris.org/servlets/ProjectDocumentList?folderID=2240 在打开的网 ...

  2. hdu4549 矩阵快速幂 + 欧拉降幂

    R - M斐波那契数列 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit  ...

  3. EasyIcon:免费图标搜索和下载平台

    EasyIcon是一个为设计师提供免费图标搜索和下载服务的网站. 步骤如下: 第一步,打开EasyIcon网站主页: http://www.easyicon.net/ 第二步,在EasyIcon网站的 ...

  4. js-this的用法,来自阮一峰老师的文章

    1. 随着函数使用场合的不同,this的值会发生变化.但是有一个总的原则,那就是this指的是,调用函数的那个对象. 或者说,this指向当前执行的函数的所有者. 2.情况一:纯粹的函数调用 这是函数 ...

  5. css-a与a:link的一些认识

    1.a元素有四种状态: a:link {color:blue;}//未点击的链接状态 a:visited {color:blue;}//已点击的连接状态 a:hover {color:red;}//鼠 ...

  6. macOS 安装 pip

    安装好wget之后: wget https://bootstrap.pypa.io/get-pip.py sudo python get-pip.py 终于可以愉快的pip了~

  7. Java反射机制的作用

    假如我们有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类.那么第一个程序员的代码能否通过编译呢?这是不能通过编译的.利用Java反射的机制,就可以 ...

  8. 【uoj58】 WC2013—糖果公园

    http://uoj.ac/problem/58 (题目链接) 题意 给定一棵树,每个点有一个颜色,提供两种操作: 1.询问两点间路径上的${\sum{v[a[i]]*w[k]}}$,其中${a[i] ...

  9. Bzoj2648 SJY摆棋子

    Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 3128  Solved: 1067 Description 这天,SJY显得无聊.在家自己玩.在一个 ...

  10. 重新认识unicode和utf8编码

    重新认识unicode和utf8编码 直到今天,准确的说是刚才,我才知道UTF-8编码和Unicode编码是不一样的,是有区别的囧 他们之间是有一定的联系的,看看他们的区别: UTF-8的长度是不一定 ...