1 typedef struct dict {
2 dictType *type;
3 void *privdata;
4 dictht ht[2];
5 int rehashidx; /* rehashing not in progress if rehashidx == -1 */
6 int iterators; /* number of iterators currently running */
7 } dict;
1 typedef struct dictht {
2 dictEntry **table;
3 unsigned long size;
4 unsigned long sizemask;
5 unsigned long used;
6 } dictht;
1 static unsigned long _dictNextPower(unsigned long size){
2 unsigned long i = DICT_HT_INITIAL_SIZE;
3
4 if (size >= LONG_MAX) return LONG_MAX;
5 while(1) {
6 if (i >= size)
7 return i;
8 i *= 2;
9 }
10 }
11
12 int dictExpand(dict *d, unsigned long size){
13 dictht n; /* the new hash table */
14
15 unsigned long realsize = _dictNextPower(size);
16
17 /* the size is invalid if it is smaller than the number of
18 * elements already inside the hash table */
19 if (dictIsRehashing(d) || d->ht[0].used > size)
20 return DICT_ERR;
21
22 /* Allocate the new hash table and initialize all pointers to NULL */
23 n.size = realsize;
24 n.sizemask = realsize-1;
25 n.table = zcalloc(realsize*sizeof(dictEntry*));
26 n.used = 0;
27
28 /* Is this the first initialization? If so it's not really a rehashing
29 * we just set the first hash table so that it can accept keys. */
30 if (d->ht[0].table == NULL) {
31 d->ht[0] = n;
32 return DICT_OK;
33 }
34
35 /* Prepare a second hash table for incremental rehashing */
36 d->ht[1] = n;
37 d->rehashidx = 0;
38
39 return DICT_OK;
40 }
新建了一个哈希表n,size是扩展后的size,ht[0].table 为空说明这是第一次初始化,不是扩展,直接赋值。
ht[0].table 不为空,说明这是一次扩展,把n赋给ht[1],ReHash标志rehashix也被设为0.
上边这段不大好理解,先看后面的,一会返过来再研究dictExpand函数。
--------------------6月20日--------------------------
向字典中添加元素需要调用dictAdd函数:
1 /* Add an element to the target hash table */
2 int dictAdd(dict *d, void *key, void *val){
3 dictEntry *entry = dictAddRaw(d,key);
4
5 if (!entry) return DICT_ERR;
6 dictSetVal(d, entry, val);
7 return DICT_OK;
8 }
具体实现需要看dictAddRaw函数:
1 dictEntry *dictAddRaw(dict *d, void *key){
2 int index;
3 dictEntry *entry;
4 dictht *ht;
5
6 if (dictIsRehashing(d)) _dictRehashStep(d);
7
8 /* Get the index of the new element, or -1 if
9 * the element already exists. */
10 if ((index = _dictKeyIndex(d, key)) == -1)
11 return NULL;
12
13 /* Allocate the memory and store the new entry */
14 ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
15 entry = zmalloc(sizeof(*entry));
16 entry->next = ht->table[index];
17 ht->table[index] = entry;
18 ht->used++;
19
20 /* Set the hash entry fields. */
21 dictSetKey(d, entry, key);
22 return entry;
23 }
先判断是不是在进行Rehash,如果在Rehash,执行渐进式Rehash。
找到要插入的key的位置,如果相同的key已经存在了,返回NULL
如果在进行Rehash,ht指向ht[1]表,然后利用链表头插法(这个我熟)将entry插入,更新used。
添加key前需要查找key的位置:
1 /* Returns the index of a free slot that can be populated with
2 * an hash entry for the given 'key'.
3 * If the key already exists, -1 is returned.
4 *
5 * Note that if we are in the process of rehashing the hash table, the
6 * index is always returned in the context of the second (new) hash table. */
7 static int _dictKeyIndex(dict *d, const void *key){
8 unsigned int h, idx, table;
9 dictEntry *he;
10
11 /* Expand the hash table if needed */
12 if (_dictExpandIfNeeded(d) == DICT_ERR)
13 return -1;
14 /* Compute the key hash value */
15 h = dictHashKey(d, key);
16 for (table = 0; table ht[table].sizemask;
18 /* Search if this slot does not already contain the given key */
19 he = d->ht[table].table[idx];
20 while(he) {
21 if (dictCompareKeys(d, key, he->key))
22 return -1;
23 he = he->next;
24 }
25 if (!dictIsRehashing(d)) break;
26 }
27 return idx;
28 }
1 /* Expand the hash table if needed */
2 static int _dictExpandIfNeeded(dict *d){
3 /* Incremental rehashing already in progress. Return. */
4 if (dictIsRehashing(d)) return DICT_OK;
5
6 /* If the hash table is empty expand it to the initial size. */
7 if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);
8
9 /* If we reached the 1:1 ratio, and we are allowed to resize the hash
10 * table (global setting) or we should avoid it but the ratio between
11 * elements/buckets is over the "safe" threshold, we resize doubling
12 * the number of buckets. */
13 if (d->ht[0].used >= d->ht[0].size &&
14 (dict_can_resize ||
15 d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
16 {
17 return dictExpand(d, d->ht[0].used*2);
18 }
19 return DICT_OK;
20 }
/* Using dictEnableResize() / dictDisableResize() we make possible to
* enable/disable resizing of the hash table as needed. This is very important
* for Redis, as we use copy-on-write and don't want to move too much memory
* around when there is a child performing saving operations.
*
* Note that even when dict_can_resize is set to 0, not all resizes are
* prevented: an hash table is still allowed to grow if the ratio between
* the number of elements and the buckets > dict_force_resize_ratio. */
1 int dictRehash(dict *d, int n) {
2 if (!dictIsRehashing(d)) return 0;
3
4 while(n--) {
5 dictEntry *de, *nextde;
6
7 /* Check if we already rehashed the whole table... */
8 if (d->ht[0].used == 0) {
9 zfree(d->ht[0].table);
10 d->ht[0] = d->ht[1];
11 _dictReset(&d->ht[1]);
12 d->rehashidx = -1;
13 return 0;
14 }
15
16 /* Note that rehashidx can't overflow as we are sure there are more
17 * elements because ht[0].used != 0 */
18 assert(d->ht[0].size > (unsigned)d->rehashidx);
19 while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++;
20 de = d->ht[0].table[d->rehashidx];
21 /* Move all the keys in this bucket from the old to the new hash HT */
22 while(de) {
23 unsigned int h;
24
25 nextde = de->next;
26 /* Get the index in the new hash table */
27 h = dictHashKey(d, de->key) & d->ht[1].sizemask;
28 de->next = d->ht[1].table[h];
29 d->ht[1].table[h] = de;
30 d->ht[0].used--;
31 d->ht[1].used++;
32 de = nextde;
33 }
34 d->ht[0].table[d->rehashidx] = NULL;
35 d->rehashidx++;
36 }
37 return 1;
38 }