zoukankan      html  css  js  c++  java
  • php Hash Table(四) Hash Table添加和更新元素

    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 <= 0) { //添加的是字符串索引的,所以nKeyLength不可能<=0
    #if ZEND_DEBUG
            ZEND_PUTS("zend_hash_update: Can't put in empty key
    ");
    #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
    ");
                    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 + 1); //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 == 0)) {                                
            (ht)->arBuckets = (Bucket **) pecalloc((ht)->nTableSize, sizeof(Bucket *), (ht)->persistent);    
            (ht)->nTableMask = (ht)->nTableSize - 1;                        
        }                                                                    
    } while (0)

    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。

  • 相关阅读:
    A1023 Have Fun with Numbers [大整数乘法]
    大整数的四则运算
    A1096 Consecutive Factors [因子分解]
    A1078 Hashing [质数和散列结合]
    A1015 Reversible Primes [质数问题]
    又谈进制转换
    A1088 Rational Arithmetic [分数四则运算]
    A1081 Rational Sum [分数计算]
    linux主流系统配置静态ip
    主机ping虚拟机请求超时,虚拟机ping主机正常ping通导致ssh连接问题
  • 原文地址:https://www.cnblogs.com/leezhxing/p/4838927.html
Copyright © 2011-2022 走看看