redis6.0.5之dict阅读笔记4-dict之元素查找以及相关的迭代器
******************************************************************
一般操作都是增删改查,我们已经见识了新增元素和空间扩展,接下来我们来看看元素查找以及相关的迭代器
******************************************************************
函数dictFind,通过key查找对应的元素
dictEntry *dictFind(dict *d, const void *key)
{
dictEntry *he;
uint64_t h, idx, table;
if (dictSize(d) == 0) return NULL; /* dict is empty */
//如果字典是空的,就无需查找了
if (dictIsRehashing(d)) _dictRehashStep(d);
//如果正在做迁移,就帮忙干点活,套用成语就是 见缝插针
h = dictHashKey(d, key); //获取hash值
for (table = 0; table <= 1; table++) { //新老两张hash表都准备查找
idx = h & d->ht[table].sizemask;
//找到对应的桶,d->ht[table].sizemask 为2的指数减1,每个位置上都是1,适合与操作
he = d->ht[table].table[idx]; //获取这个桶的第一个位置元素
while(he) {
if (key==he->key || dictCompareKeys(d, key, he->key)) //如果找到元素,就返回
return he;
he = he->next; //没有找到继续下一个
}
if (!dictIsRehashing(d)) return NULL;
//若果没有做迁移,找完table0就可以返回了,否则还要继续找table1
}
return NULL; //两个都找不到,就返回空
}
******************************************************************
函数dictFetchValue获取key对应的值
void *dictFetchValue(dict *d, const void *key) {
dictEntry *he;
he = dictFind(d,key); //通过key查找元素
return he ? dictGetVal(he) : NULL; //找到就返回key对应的值,找不到就返回空
}
******************************************************************
/* A fingerprint is a 64 bit number that represents the state of the dictionary
* at a given time, it's just a few dict properties xored together.
* When an unsafe iterator is initialized, we get the dict fingerprint, and check
* the fingerprint again when the iterator is released.
* If the two fingerprints are different it means that the user of the iterator
* performed forbidden operations against the dictionary while iterating. */
指纹是一个64位的数字,代表了字典在特定时间的状态,它只是字典部分属性异或的结果。
当一个非安全的迭代器开始使用时,我们获取字典的指纹,当迭代器将被释放时,我们再次检查字典的指纹.
如果前后两个指纹不一致,意味着迭代器在使用过程中进行了禁止(修改)的操作。
long long dictFingerprint(dict *d) {
long long integers[6], hash = 0;
int j;
integers[0] = (long) d->ht[0].table;
integers[1] = d->ht[0].size;
integers[2] = d->ht[0].used;
integers[3] = (long) d->ht[1].table;
integers[4] = d->ht[1].size;
integers[5] = d->ht[1].used;
上面6行就是将字典的属性hash表0和表1的地址,桶数个数,元素存储个数进行赋值,用来做hash
/* We hash N integers by summing every successive integer with the integer
* hashing of the previous sum. Basically:
我们对N个整数进行连续求和,即将前一个整数hash值和后一个整数求和再hash,再和下一个整数求和,
示例如下
* Result = hash(hash(hash(int1)+int2)+int3) ...
*
* This way the same set of integers in a different order will (likely) hash
* to a different number. */
这种方式会导致同样集合的整数如果顺序不同会映射到不同的结果数值
for (j = 0; j < 6; j++) {
hash += integers[j];
// 前一个hash值和后一个整数相加, 第一次的hash值为0,整数为integers[0]
/* For the hashing step we use Tomas Wang's 64 bit integer hash. */
这里采用了 Tomas Wang 的64位hash算法
hash = (~hash) + (hash << 21); // hash = (hash << 21) - hash - 1;
因为进位溢出原因,~hash + hash + 1 = 0,所以 ~hash = - hash - 1
hash = hash ^ (hash >> 24);
hash = (hash + (hash << 3)) + (hash << 8); // hash * 265
hash一个hash,hash << 3 8个hash,hash << 8 256个hash, 所以总的 1 + 8 + 256 = 265
hash = hash ^ (hash >> 14);
hash = (hash + (hash << 2)) + (hash << 4); // hash * 21
hash = hash ^ (hash >> 28);
hash = hash + (hash << 31);
}
return hash; //返回最总的hash值
}
******************************************************************
dictGetIterator获取字典的不安全迭代器
dictIterator *dictGetIterator(dict *d)
{
dictIterator *iter = zmalloc(sizeof(*iter)); //给迭代器分配空间, 本环境为48个字节
//初始化迭代器
iter->d = d; //指向字典
iter->table = 0; //默认table0
iter->index = -1; //桶的迭代计数
iter->safe = 0; //默认不安全
iter->entry = NULL;
iter->nextEntry = NULL;
return iter;
}
******************************************************************
dictGetIterator获取字典的安全迭代器
dictIterator *dictGetSafeIterator(dict *d) {
dictIterator *i = dictGetIterator(d);
i->safe = 1; //安全
return i;
}
******************************************************************
dictEntry *dictNext(dictIterator *iter)
{
while (1) {
if (iter->entry == NULL) { //如果当前的元素为空
dictht *ht = &iter->d->ht[iter->table]; //获取hash表
if (iter->index == -1 && iter->table == 0) { //如果计数刚开始并且查找的是表0
if (iter->safe) //安全模式下,迭代器计数+1
iter->d->iterators++;
else //非安全模式下,获取字典指纹
iter->fingerprint = dictFingerprint(iter->d);
}
iter->index++; //迭代器计数+1
if (iter->index >= (long) ht->size) { //如果迭代器计数 大于等于 总的桶数
if (dictIsRehashing(iter->d) && iter->table == 0) { //如果正在做Rehashing并且当前是表0
iter->table++; //就需要查找表1(因为表0已经查找完毕了)
iter->index = 0; //换了一张表,需要从新计数
ht = &iter->d->ht[1];
} else {
//其它情况(没有做Rehashing,或者在做Rehashing,但是已经是表1了),就已经查找完毕,直接退出
break;
}
}
iter->entry = ht->table[iter->index];//将一个新桶的第一个元素赋值给迭代器当前元素
} else {
//不为空的情况下,获取下一个元素(已经提前保存在iter->nextEntry中)
iter->entry = iter->nextEntry;
}
if (iter->entry) { //非空
/* We need to save the 'next' here, the iterator user
* may delete the entry we are returning. */
iter->nextEntry = iter->entry->next; //将iter->entry链表的下一个元素保存到iter->nextEntry
return iter->entry; //返回当前元素
}
}
return NULL;
}
******************************************************************
void dictReleaseIterator(dictIterator *iter)
{
if (!(iter->index == -1 && iter->table == 0)) {
//如果不是初始的值(iter->index == -1和 iter->table == 0),表示已经做过迭代
if (iter->safe) //在安全模式下,释放了迭代器数量就减少1
iter->d->iterators--;
else //非安全模式下,确认指纹是否有变化,有变化就出问题了
assert(iter->fingerprint == dictFingerprint(iter->d));
}
zfree(iter); // 释放掉迭代器申请的内存空间
}
******************************************************************
/* Add or Find:
* dictAddOrFind() is simply a version of dictAddRaw() that always
* returns the hash entry of the specified key, even if the key already
* exists and can't be added (in that case the entry of the already
* existing key is returned.)
*
* See dictAddRaw() for more information. */
新增或者查找:
本函数是一个简单版本dictAddRaw包装,总是返回键对应的值,即使键已经存在所以不能添加,
在这种情况下,返回存在键对应的元素
dictEntry *dictAddOrFind(dict *d, void *key) {
dictEntry *entry, *existing;
entry = dictAddRaw(d,key,&existing); //新增元素,已经存在就返回元素保存在existing
return entry ? entry : existing;
}
******************************************************************
/* Finds the dictEntry reference by using pointer and pre-calculated hash.
* oldkey is a dead pointer and should not be accessed.
* the hash value should be provided using dictGetHash.
* no string / key comparison is performed.
* return value is the reference to the dictEntry if found, or NULL if not found. */
通过指针和预先计算的hash值查找元素索引,oldkey是一个不能被修改的常量指针。
传入的hash值应该由dictGetHash提供。
不需要做字符串/键比较
如果找到,返回的值是对元素的一个引用,如果找不到就返回空
dictEntry **dictFindEntryRefByPtrAndHash(dict *d, const void *oldptr, uint64_t hash) {
dictEntry *he, **heref;
unsigned long idx, table;
if (dictSize(d) == 0) return NULL; /* dict is empty */
for (table = 0; table <= 1; table++) {
idx = hash & d->ht[table].sizemask;
heref = &d->ht[table].table[idx];
he = *heref;
while(he) {
if (oldptr==he->key) //找到返回元素引用
return heref;
heref = &he->next;
he = *heref;
}
if (!dictIsRehashing(d)) return NULL; //没有扩表的情况下,只需要查找一张表即可
}
return NULL;
}