PHP的HashTable(一)

数据定义 :

/*zend_hash.h */

typedef struct bucket {
    ulong h;                        /* Used for numeric indexing */
    uint nKeyLength;  
    void *pData; /* 这里是array里面item对对应的数据,有点特殊的是,若存的是指针,这是指向下一个成员pDataPtr的指针,pDataPtr才是真正存放指针value的地方,有点不明白 */
    void *pDataPtr;
    struct bucket *pListNext;
    struct bucket *pListLast;
    struct bucket *pNext;
    struct bucket *pLast;
    char arKey[1]; /* Must be last element 这是柔性数组成员或者叫伸缩性数组成员 */
} Bucket;

typedef struct _hashtable {
    uint nTableSize; 
    uint nTableMask; /* nTableSize-1,用来给key的hash值取模决定key最终落在哪个槽内 */
    uint nNumOfElements;
    ulong nNextFreeElement;
    Bucket *pInternalPointer;    /* Used for element traversal */
    Bucket *pListHead;
    Bucket *pListTail;
    Bucket **arBuckets;
    dtor_func_t pDestructor;
    zend_bool persistent;
    unsigned char nApplyCount;
    zend_bool bApplyProtection;
#if ZEND_DEBUG
    int inconsistent;
#endif

} HashTable;  

 #define UPDATE_DATA(ht, p, pData, nDataSize)                                            \

    if (nDataSize == sizeof(void*)) { /* 若value所占字节数 == sizeof(void*) */              \
        if ((p)->pData != &(p)->pDataPtr) {                                               \
            pefree_rel((p)->pData, (ht)->persistent);                                     \
        }                                                                                 \
        memcpy(&(p)->pDataPtr, pData, sizeof(void *));   /* 把pData存入pDataPtr */          \
        (p)->pData = &(p)->pDataPtr;                     /* pData指向 pDataPtr */           \
    } else {                                                                            \
        if ((p)->pData == &(p)->pDataPtr) {                                                \
            (p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);            \
            (p)->pDataPtr=NULL;                                                            \
        } else {                                                                        \
            (p)->pData = (void *) perealloc_rel((p)->pData, nDataSize, (ht)->persistent);    \
            /* (p)->pDataPtr is already NULL so no need to initialize it */                \
        }                                                                                \
        memcpy((p)->pData, pData, nDataSize);          /* pData 存入 Bucket->pData */     \
    }

#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;                                                \
    }
/* zend_hash_add_or_update */
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; /* key的hash值 */
    uint nIndex; /*  hash 值 对应的槽的 索引 */
    Bucket *p;

    IS_CONSISTENT(ht);

    if (nKeyLength <= 0) {
#if ZEND_DEBUG
        ZEND_PUTS("zend_hash_update: Can't put in empty key\n");
#endif
        return FAILURE;
    }

    h = zend_inline_hash_func(arKey, nKeyLength);  /*  计算key的hash值 */
    nIndex = h & ht->nTableMask;  /* 通过取模计算key的hash值所对应的槽的索引 */

    p = ht->arBuckets[nIndex];
    while (p != NULL) {
        if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
            if (!memcmp(p->arKey, arKey, nKeyLength)) {
                if (flag & HASH_ADD) {
                    return FAILURE;
                }
                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;
    }
    
    p = (Bucket *) pemalloc(sizeof(Bucket) - 1 + nKeyLength, ht->persistent);
    if (!p) {
        return FAILURE;
    }
    memcpy(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);  /*  */
    ht->arBuckets[nIndex] = p; /* 槽头指向这个新的Bucket */
    HANDLE_UNBLOCK_INTERRUPTIONS();

    ht->nNumOfElements++;
    ZEND_HASH_IF_FULL_DO_RESIZE(ht);        /* If the Hash table is full, resize it */
    return SUCCESS;
}

/* end */ 

原文地址:https://www.cnblogs.com/bqrm/p/2709841.html