有一个非常有效的关联。 php源代码中使用的数组C语言实现。
/*
* HashTable Data Layout
* =====================
*
* +=============================+
* | HT_HASH(ht, ht->nTableMask) |
* | ... |
* | HT_HASH(ht, -1) |
* +-----------------------------+
* ht->arData ---> | Bucket[0] |
* | ... |
* | Bucket[ht->nTableSize-1] |
* +=============================+
*/
结构:
typedef struct _Bucket {
zval val;
zend_ulong h; /* hash value (or numeric index) */
zend_string *key; /* string key or NULL for numerics */
} Bucket;
typedef struct _zend_array HashTable;
struct _zend_array {
zend_refcounted_h gc;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar flags,
zend_uchar _unused,
zend_uchar nIteratorsCount,
zend_uchar _unused2)
} v;
uint32_t flags;
} u;
uint32_t nTableMask;
Bucket *arData;
uint32_t nNumUsed;
uint32_t nNumOfElements;
uint32_t nTableSize;
uint32_t nInternalPointer;
zend_long nNextFreeElement;
dtor_func_t pDestructor;
};
示例功能:
static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key)
{
zend_ulong h;
uint32_t nIndex;
uint32_t idx;
Bucket *p, *arData;
h = zend_string_hash_val(key);
arData = ht->arData;
nIndex = h | ht->nTableMask; //index calculation
idx = HT_HASH_EX(arData, nIndex);
while (EXPECTED(idx != HT_INVALID_IDX)) {
p = HT_HASH_TO_BUCKET_EX(arData, idx);
if (EXPECTED(p->key == key)) { /* check for the same interned string */
return p;
} else if (EXPECTED(p->h == h) &&
EXPECTED(p->key) &&
EXPECTED(ZSTR_LEN(p->key) == ZSTR_LEN(key)) &&
EXPECTED(memcmp(ZSTR_VAL(p->key), ZSTR_VAL(key), ZSTR_LEN(key)) == 0)) {
return p;
}
idx = Z_NEXT(p->val);
}
return NULL;
}
h是由哈希函数返回的大整数。
问题是: 为什么用这种方式进行索引计算?
nIndex = h | ht->nTableMask; //index calculation
为什么哈希表大小上除以h整数的余数不是简单的?
nIndex = h & (ht->nTableSize - 1); //analog: nIndex = h % ht->nTableSize
答案 0 :(得分:3)
这是为了使数字为负。哈希表的布局确实让人脑筋急转(Zend/zend_types.h):
/*
* HashTable Data Layout
* =====================
*
* +=============================+
* | HT_HASH(ht, ht->nTableMask) |
* | ... |
* | HT_HASH(ht, -1) |
* +-----------------------------+
* ht->arData ---> | Bucket[0] |
* | ... |
* | Bucket[ht->nTableSize-1] |
* +=============================+
*/
ht->nTableMask
是一个整数,解释为2的补码为负数,其目的是通过与之进行或运算并转换为int32_t
,您将从ht->arData
获得负偏移。然后将类型为Bucket的ht->arData
强制转换为指向uint32_t
的指针,并使用负索引对该指针进行索引。即所有这些可疑的技巧都是存在的,每个哈希表不需要有2个指针,而是使用1个指向数据结构中间的指针。
使用AND并从ht->arData
中减去的模数就足够了,并导致相同的操作-似乎已经手动优化了它,可以在某些错误的编译器上快速实现。
答案 1 :(得分:1)
NikiC写道:
这基本上与display:flex
相同,但是要用负数代替正数
不是将高位设置为零,而是将它们设置为一 这样您得到的负数介于-1和-size之间
掩码为-(大小<< 1)。与〜(size << 1)+ 1或〜((size << 1)-1)
相同因此,您获得通常面膜的方式基本上相同,但是 a)倒置,因为您要设置高位而不是底部和 b)偏移一,以便正确处理边界