/* Returns the index of a free slot that can be populated with * an hash entry for the given 'key'. * If the key already exists, -1 is returned. * * 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. */ static int _dictKeyIndex(dict *d, const void *key) { unsigned int h, idx, table; dictEntry *he; /* Expand the hash table if needed */ //在添加之前检查是否需要扩展 if (_dictExpandIfNeeded(d) == DICT_ERR) return -1; /* Compute the key hash value */ //得到对应的bucket索引值 h = dictHashKey(d, key); //这里之所以需要遍历两个哈希表,是因为有rehash的需求 //必须保证在字典中key是唯一的,并且是不变的 for (table = 0; table <= 1; table++) { idx = h & d->ht[table].sizemask; /* Search if this slot does not already contain the given key */ he = d->ht[table].table[idx]; //如果这个bucket中已经存在相同的key,直接返回错误 while(he) { if (dictCompareKeys(d, key, he->key)) return -1; he = he->next; } //如果当前未执行rehash操作(-1),说明所有的key-value都存放在ht[0]中 //所以这里避免了第二次的遍历 if (!dictIsRehashing(d)) break; } return idx; }
/* Search and remove an element */ static int dictDelete(dict *ht, const void *key) { unsigned int h; dictEntry *de, *prevde; if (ht->size == 0) return DICT_ERR; h = dictHashKey(ht, key) & ht->sizemask; de = ht->table[h]; prevde = NULL; while(de) { if (dictCompareHashKeys(ht,key,de->key)) { /* Unlink the element from the list */ if (prevde) prevde->next = de->next; else ht->table[h] = de->next; dictFreeEntryKey(ht,de); dictFreeEntryVal(ht,de); free(de); ht->used--; return DICT_OK; } prevde = de; de = de->next; } return DICT_ERR; /* not found */ }
/* 从字典中查找给定 key * * 查找过程是典型的 separate chaining find 操作 * 具体参见:http://en.wikipedia.org/wiki/Hash_table#Separate_chaining */ dictEntry *dictFind(dict *d, const void *key) { dictEntry *he; unsigned int h, idx, table; // 哈希表为空,直接返回 NULL if (d->ht[0].size == 0) return NULL; /* We don't have a table at all */ // 检查字典(的哈希表)能否执行 rehash 操作 // 如果可以的话,执行平摊 rehash 操作 if (dictIsRehashing(d)) _dictRehashStep(d); // 查找 h = dictHashKey(d, key); // 计算哈希值 for (table = 0; table <= 1; table++) { // 遍历两个哈希表 idx = h & d->ht[table].sizemask; // 计算地址 he = d->ht[table].table[idx]; // he 指向链表头 while(he) { // 遍历链查找 key if (dictCompareKeys(d, key, he->key)) return he; he = he->next; } if (!dictIsRehashing(d)) return NULL; } return NULL; }
/* 字典(的哈希表) rehash 函数 * * Args: * d * n 要执行 rehash 的元素数量 * * Returns: * 0 所有元素 rehash 完毕 * 1 还有元素没有 rehash */ int dictRehash(dict *d, int n) { if (!dictIsRehashing(d)) return 0; while(n--) { dictEntry *de, *nextde; // 0 号哈希表的所有元素 rehash 完毕? if (d->ht[0].used == 0) { zfree(d->ht[0].table); // 替换 1 号为 0 号 d->ht[0] = d->ht[1]; _dictReset(&d->ht[1]); // 重置 1 号哈希表 d->rehashidx = -1; // 重置 rehash flag return 0; } /* Note that rehashidx can't overflow as we are sure there are more * elements because ht[0].used != 0 */ assert(d->ht[0].size > (unsigned)d->rehashidx); // 略过所有空链 while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++; // 指向链头 de = d->ht[0].table[d->rehashidx]; // 将链表内的所有节点移动到 1 号哈希表 while(de) { unsigned int h; nextde = de->next; // 计算新的地址(用于 1 号哈希表) h = dictHashKey(d, de->key) & d->ht[1].sizemask; de->next = d->ht[1].table[h]; // 更新 next 指针 d->ht[1].table[h] = de; // 移动 d->ht[0].used--; // 更新 0 号表计算器 d->ht[1].used++; // 更新 1 号表计算器 de = nextde; } d->ht[0].table[d->rehashidx] = NULL; // 清空链头 d->rehashidx++; // 更新索引 } return 1; }
static dictEntry *dictFind(dict *ht, const void *key) { dictEntry *he; unsigned int h; if (ht->size == 0) return NULL; h = dictHashKey(ht, key) & ht->sizemask; he = ht->table[h]; while(he) { if (dictCompareHashKeys(ht, key, he->key)) return he; he = he->next; } return NULL; }
/* 删除指定元素的底层实现代码 * * Args: * d * key * nofree 指示是否释放被删除元素的键和值 * * Returns: * DICT_ERR 字典为空,删除失败 * DICT_OK 删除成功 */ static int dictGenericDelete(dict *d, const void *key, int nofree) { unsigned int h, idx; dictEntry *he, *prevHe; int table; // 字典为空,删除失败 if (d->ht[0].size == 0) return DICT_ERR; // 平摊 rehash if (dictIsRehashing(d)) _dictRehashStep(d); // 哈希值 h = dictHashKey(d, key); // 遍历 for (table = 0; table <= 1; table++) { idx = h & d->ht[table].sizemask; // 地址索引 he = d->ht[table].table[idx]; // 表头节点 prevHe = NULL; while(he) { if (dictCompareKeys(d, key, he->key)) { /* Unlink the element from the list */ if (prevHe) prevHe->next = he->next; else d->ht[table].table[idx] = he->next; if (!nofree) { dictFreeKey(d, he); dictFreeVal(d, he); } zfree(he); d->ht[table].used--; return DICT_OK; } // 推进指针 prevHe = he; he = he->next; } if (!dictIsRehashing(d)) break; } return DICT_ERR; /* not found */ }
//执行一次rehash操作,一般在检查到该字典的rehash标记被设置之后调用该函数 //这里的参数n表示至少执行多少bucket的移动(reahash),每次移动从rehashidx开始 int dictRehash(dict *d, int n) { if (!dictIsRehashing(d)) return 0; while(n--) { dictEntry *de, *nextde; /* Check if we already rehashed the whole table... */ //如果已经全部移动了,说明这一次rehash操作执行完成了 //释放ht[0],然后重新将移动之后的ht[1]替换,这样保证不再rehash操作 //的时候都是使用ht[0]保存新添加的元素 if (d->ht[0].used == 0) { zfree(d->ht[0].table); //复制&重置 d->ht[0] = d->ht[1]; _dictReset(&d->ht[1]); d->rehashidx = -1; return 0; } /* Note that rehashidx can't overflow as we are sure there are more * elements because ht[0].used != 0 */ //在rehash过程中,rehashidx保存的是上一次移动的bucket的下标 //这样在下一次移动的时候从rehashidx开始 assert(d->ht[0].size > (unsigned)d->rehashidx); //跳过没有任何元素的bucket while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++; de = d->ht[0].table[d->rehashidx]; /* Move all the keys in this bucket from the old to the new hash HT */ //将旧哈希表ht[0]中的每一个bucket上的每一个entry都移动到ht[1]上 //因为同一个entry在两个表定位的bucket不同,所以需要重新hash,然后添加到bucket //中entry链表的表头 while(de) { unsigned int h; nextde = de->next; /* Get the index in the new hash table */ h = dictHashKey(d, de->key) & d->ht[1].sizemask; de->next = d->ht[1].table[h]; d->ht[1].table[h] = de; d->ht[0].used--; d->ht[1].used++; de = nextde; } d->ht[0].table[d->rehashidx] = NULL; d->rehashidx++; } return 1; }
/* Expand or create the hashtable */ static int dictExpand(dict *ht, unsigned long size) { dict n; /* the new hashtable */ unsigned long realsize = _dictNextPower(size), i; /* the size is invalid if it is smaller than the number of * elements already inside the hashtable */ if (ht->used > size) return DICT_ERR; _dictInit(&n, ht->type, ht->privdata); n.size = realsize; n.sizemask = realsize-1; n.table = calloc(realsize,sizeof(dictEntry*)); /* Copy all the elements from the old to the new table: * note that if the old hash table is empty ht->size is zero, * so dictExpand just creates an hash table. */ n.used = ht->used; for (i = 0; i < ht->size && ht->used > 0; i++) { dictEntry *he, *nextHe; if (ht->table[i] == NULL) continue; /* For each hash entry on this slot... */ he = ht->table[i]; while(he) { unsigned int h; nextHe = he->next; /* Get the new element index */ h = dictHashKey(ht, he->key) & n.sizemask; he->next = n.table[h]; n.table[h] = he; ht->used--; /* Pass to the next element */ he = nextHe; } } assert(ht->used == 0); free(ht->table); /* Remap the new hashtable in the old */ *ht = n; return DICT_OK; }
/* Returns the index of a free slot that can be populated with * an hash entry for the given 'key'. * If the key already exists, -1 is returned. */ static int _dictKeyIndex(dict *ht, const void *key) { unsigned int h; dictEntry *he; /* Expand the hashtable if needed */ if (_dictExpandIfNeeded(ht) == DICT_ERR) return -1; /* Compute the key hash value */ h = dictHashKey(ht, key) & ht->sizemask; /* Search if this slot does not already contain the given key */ he = ht->table[h]; while(he) { if (dictCompareHashKeys(ht, key, he->key)) return -1; he = he->next; } return h; }
dictEntry *dictFind(dict *d, const void *key) { dictEntry *he; unsigned int h, idx, table; if (d->ht[0].size == 0) return NULL; /* We don't have a table at all */ if (dictIsRehashing(d)) _dictRehashStep(d); h = dictHashKey(d, key); for (table = 0; table <= 1; table++) { idx = h & d->ht[table].sizemask; he = d->ht[table].table[idx]; while(he) { if (dictCompareHashKeys(d, key, he->key)) return he; he = he->next; } if (!dictIsRehashing(d)) return NULL; } return NULL; }
/* 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. * Note that a rehashing step consists in moving a bucket (that may have more * thank one key as we use chaining) from the old to the new hash table. */ int dictRehash(dict *d, int n) { if (!dictIsRehashing(d)) return 0; while(n--) { dictEntry *de, *nextde; /* Check if we already rehashed the whole table... */ if (d->ht[0].used == 0) { free(d->ht[0].table); d->ht[0] = d->ht[1]; _dictReset(&d->ht[1]); d->rehashidx = -1; return 0; } /* Note that rehashidx can't overflow as we are sure there are more * elements because ht[0].used != 0 */ assert(d->ht[0].size > (unsigned)d->rehashidx); while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++; de = d->ht[0].table[d->rehashidx]; /* Move all the keys in this bucket from the old to the new hash HT */ while(de) { unsigned int h; nextde = de->next; /* Get the index in the new hash table */ h = dictHashKey(d, de->key) & d->ht[1].sizemask; de->next = d->ht[1].table[h]; d->ht[1].table[h] = de; d->ht[0].used--; d->ht[1].used++; de = nextde; } d->ht[0].table[d->rehashidx] = NULL; d->rehashidx++; } return 1; }
/* Returns the index of a free slot that can be populated with * an hash entry for the given 'key'. * If the key already exists, -1 is returned. * * 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. */ static int _dictKeyIndex(dict *d, const void *key) { unsigned int h, idx, table; dictEntry *he; /* Expand the hashtable if needed */ if (_dictExpandIfNeeded(d) == DICT_ERR) return -1; /* Compute the key hash value */ h = dictHashKey(d, key); for (table = 0; table <= 1; table++) { idx = h & d->ht[table].sizemask; /* Search if this slot does not already contain the given key */ he = d->ht[table].table[idx]; while(he) { if (dictCompareHashKeys(d, key, he->key)) return -1; he = he->next; } if (!dictIsRehashing(d)) break; } return idx; }