/* Performs N steps of incremental rehashing. Returns 1 if there are still * keys to move from the old to the new hash table, otherwise 0 is returned. * * 执行 N 步渐进式 rehash 。 * * 返回 1 表示仍有键需要从 0 号哈希表移动到 1 号哈希表, * 返回 0 则表示所有键都已经迁移完毕。 * * Note that a rehashing step consists in moving a bucket (that may have more * than one key as we use chaining) from the old to the new hash table. * * 注意,每步 rehash 都是以一个哈希表索引(桶)作为单位的, * 一个桶里可能会有多个节点, * 被 rehash 的桶里的所有节点都会被移动到新哈希表。 * * T = O(N) */ intdictRehash(dict *d, int n){ // 只可以在 rehash 进行中时执行 if (!dictIsRehashing(d)) return0; // 进行 N 步迁移 // T = O(N) while(n--) { dictEntry *de, *nextde;
/* Note that rehashidx can't overflow as we are sure there are more * elements because ht[0].used != 0 */ // 确保 rehashidx 没有越界 assert(d->ht[0].size > (unsigned)d->rehashidx);
// 指向该索引的链表表头节点 de = d->ht[0].table[d->rehashidx]; /* Move all the keys in this bucket from the old to the new hash HT */ // 将链表中的所有节点迁移到新哈希表 // T = O(1) while(de) { unsignedint h;
// 保存下个节点的指针 nextde = de->next;
/* Get the index in the new hash table */ // 计算新哈希表的哈希值,以及节点插入的索引位置 h = dictHashKey(d, de->key) & d->ht[1].sizemask;
/* the size is invalid if it is smaller than the number of * elements already inside the hash table */ // 不能在字典正在 rehash 时进行 // size 的值也不能小于 0 号哈希表的当前已使用节点 if (dictIsRehashing(d) || d->ht[0].used > size) return DICT_ERR;
/* Allocate the new hash table and initialize all pointers to NULL */ // 为哈希表分配空间,并将所有指针指向 NULL n.size = realsize; n.sizemask = realsize-1; // T = O(N) n.table = zcalloc( *sizeof(dictEntry*)); n.used = 0;
/* Is this the first initialization? If so it's not really a rehashing * we just set the first hash table so that it can accept keys. */ // 如果 0 号哈希表为空,那么这是一次初始化: // 程序将新哈希表赋给 0 号哈希表的指针,然后字典就可以开始处理键值对了。 if (d->ht[0].table == NULL) { d->ht[0] = n; return DICT_OK; }
/* Returns the index of a free slot that can be populated with * a hash entry for the given 'key'. * If the key already exists, -1 is returned. * * 返回可以将 key 插入到哈希表的索引位置 * 如果 key 已经存在于哈希表,那么返回 -1 * * Note that if we are in the process of rehashing the hash table, the * index is always returned in the context of the second (new) hash table. * * 注意,如果字典正在进行 rehash ,那么总是返回 1 号哈希表的索引。 * 因为在字典进行 rehash 时,新节点总是插入到 1 号哈希表。 * * T = O(N) */ staticint _dictKeyIndex(dict *d, constvoid *key) { unsignedint h, idx, table; dictEntry *he;
/* Expand the hash table if needed */ // 单步 rehash // T = O(N) if (_dictExpandIfNeeded(d) == DICT_ERR) return-1;
/* Compute the key hash value */ // 计算 key 的哈希值 h = dictHashKey(d, key); // T = O(1) for (table = 0; table <= 1; table++) {
// 计算索引值 idx = h & d->ht[table].sizemask;
/* Search if this slot does not already contain the given key */ // 查找 key 是否存在 // T = O(1) he = d->ht[table].table[idx]; while(he) { if (dictCompareKeys(d, key, he->key)) return-1; he = he->next; }
staticint _dictExpandIfNeeded(dict *d) { /* Incremental rehashing already in progress. Return. */ // 渐进式 rehash 已经在进行了,直接返回 if (dictIsRehashing(d)) return DICT_OK;
/* If the hash table is empty expand it to the initial size. */ // 如果字典(的 0 号哈希表)为空,那么创建并返回初始化大小(4)的0号哈希表 // T = O(1) if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);
/* If we reached the 1:1 ratio, and we are allowed to resize the hash * table (global setting) or we should avoid it but the ratio between * elements/buckets is over the "safe" threshold, we resize doubling * the number of buckets. */ // 一下两个条件之一为真时,对字典进行扩展 // 1)字典已使用节点数和字典大小之间的比率接近 1:1 // 并且 dict_can_resize 为真 // 2)已使用节点数和字典大小之间的比率超过 dict_force_resize_ratio if (d->ht[0].used >= d->ht[0].size && (dict_can_resize || d->ht[0].used/d->ht[0].size > dict_force_resize_ratio)) { // 新哈希表的大小至少是目前已使用节点数的两倍 // T = O(N) return dictExpand(d, d->ht[0].used*2); }
/* Add an element to the target hash table */ /* * 尝试将给定键值对添加到字典中 * 只有给定键 key 不存在于字典时,添加操作才会成功 * 添加成功返回 DICT_OK ,失败返回 DICT_ERR * 最坏 T = O(N) ,平摊 O(1) */ intdictAdd(dict *d, void *key, void *val) { // 尝试添加键到字典,并返回包含了这个键的新哈希节点 // T = O(N) dictEntry *entry = dictAddRaw(d,key);
// 键已存在,添加失败 if (!entry) return DICT_ERR;
// 键不存在,设置节点的值 // T = O(1) dictSetVal(d, entry, val);
// 添加成功 return DICT_OK; }
/* Low level add. This function adds the entry but instead of setting * a value returns the dictEntry structure to the user, that will make * sure to fill the value field as he wishes. * * This function is also directly exposed to user API to be called * mainly in order to store non-pointers inside the hash value, example: * * entry = dictAddRaw(dict,mykey); * if (entry != NULL) dictSetSignedIntegerVal(entry,1000); * * Return values: * * If key already exists NULL is returned. * If key was added, the hash entry is returned to be manipulated by the caller. */ /* * 尝试将键插入到字典中 * * 如果键已经在字典存在,那么返回 NULL * * 如果键不存在,那么程序创建新的哈希节点, * 将节点和键关联,并插入到字典,然后返回节点本身。 * * T = O(N) */ dictEntry *dictAddRaw(dict *d, void *key) { int index; dictEntry *entry; dictht *ht;
// 如果条件允许的话,进行单步 rehash // T = O(1) if (dictIsRehashing(d)) _dictRehashStep(d);
/* Get the index of the new element, or -1 if * the element already exists. */ // 计算键在哈希表中的索引值 // 如果值为 -1 ,那么表示键已经存在 // T = O(N) if ((index = _dictKeyIndex(d, key)) == -1) returnNULL;
// T = O(1) /* Allocate the memory and store the new entry */ // 如果字典正在 rehash ,那么将新键添加到 1 号哈希表 // 否则,将新键添加到 0 号哈希表 ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0]; // 为新节点分配空间 entry = zmalloc(sizeof(*entry)); // 将新节点插入到链表表头 entry->next = ht->table[index]; ht->table[index] = entry; // 更新哈希表已使用节点数量 ht->used++;
/* Set the hash entry fields. */ // 设置新节点的键 // T = O(1) dictSetKey(d, entry, key);
/* If safe is set to 1 this is a safe iterator, that means, you can call * dictAdd, dictFind, and other functions against the dictionary even while * iterating. Otherwise it is a non safe iterator, and only dictNext() * should be called while iterating. */ /* * 字典迭代器 * * 如果 safe 属性的值为 1 ,那么在迭代进行的过程中, * 程序仍然可以执行 dictAdd 、 dictFind 和其他函数,对字典进行修改。 * * 如果 safe 不为 1 ,那么程序只会调用 dictNext 对字典进行迭代, * 而不对字典进行修改。 */ typedefstructdictIterator { // 被迭代的字典 dict *d;
// table :正在被迭代的哈希表号码,值可以是 0 或 1 。 // index :迭代器当前所指向的哈希表索引位置。 // safe :标识这个迭代器是否安全 int table, index, safe;
// 如果当前节点不为空,那么也记录下该节点的下个节点 // 因为安全迭代器有可能会将迭代器返回的当前节点删除 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; return iter->entry; } }