這篇blog介紹dict的實現(xiàn)。
dict.c
1
#include
"
fmacros.h
"
2
3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <
string
.h>
6
#include <stdarg.h>
7
#include <assert.h>
8
#include <limits.h>
9
#include <sys/time.h>
10
#include <ctype.h>
11
12
#include
"
dict.h
"
13
#include
"
zmalloc.h
"
14
15
/*
Using dictEnableResize() / dictDisableResize() we make possible to
16
* enable/disable resizing of the hash table as needed. This is very important
17
* for Redis, as we use copy-on-write and don't want to move too much memory
18
* around when there is a child performing saving operations.
19
*
20
* Note that even when dict_can_resize is set to 0, not all resizes are
21
* prevented: an hash table is still allowed to grow if the ratio between
22
* the number of elements and the buckets > dict_force_resize_ratio.
*/
注釋已經說的很清楚了。redis有些后臺進程會做一些工作,比如rdb save、aof rewrite之類的,所以如果在后臺進程運行過程中,將允許對hash table進行resize操作的話,會造成大量的內存拷貝(默認是copy-on-write,只在內存有變化時才拷貝)。注釋中說,也沒有完全避免,如果存的entry過多,比如存的entry是slot數(shù)的五倍,則也會強制進行resize
23
static
int
dict_can_resize =
1
;
24
static
unsigned
int
dict_force_resize_ratio =
5
;
25
26
/*
-------------------------- private prototypes ----------------------------
*/
27
28
static
int
_dictExpandIfNeeded(dict *
ht); //如果需要的話,將hash表進行擴展
29
static
unsigned
long
_dictNextPower(unsigned
long
size);
30
static
int
_dictKeyIndex(dict *ht,
const
void
*
key);
31
static
int
_dictInit(dict *ht, dictType *type,
void
*
privDataPtr);
32
//這4個函數(shù)的具體含義,到后邊實現(xiàn)以及用到的時候再討論
33
/*
-------------------------- hash functions --------------------------------
*/
34
35
/*
Thomas Wang's 32 bit Mix Function
*/
36
unsigned
int
dictIntHashFunction(unsigned
int
key)
37
{
38
key += ~(key <<
15
);
39
key ^= (key >>
10
);
40
key += (key <<
3
);
41
key ^= (key >>
6
);
42
key += ~(key <<
11
);
43
key ^= (key >>
16
);
44
return
key;
45
}
46
47
/*
Identity hash function for integer keys
*/
48
unsigned
int
dictIdentityHashFunction(unsigned
int
key)
49
{
50
return
key;
51
}
52
53
/*
Generic hash function (a popular one from Bernstein).
54
* I tested a few and this was the best.
*/
55
unsigned
int
dictGenHashFunction(
const
unsigned
char
*buf,
int
len) {
56
unsigned
int
hash =
5381
;
57
58
while
(len--
)
59
hash = ((hash <<
5
) + hash) + (*buf++);
/*
hash * 33 + c
*/
60
return
hash;
61
}
62
63
/*
And a case insensitive version
*/
64
unsigned
int
dictGenCaseHashFunction(
const
unsigned
char
*buf,
int
len) {
65
unsigned
int
hash =
5381
;
66
67
while
(len--
)
68
hash = ((hash <<
5
) + hash) + (tolower(*buf++));
/*
hash * 33 + c
*/
69
return
hash;
70
}
71 //給出針對整數(shù)和字符串的hash函數(shù),因為redis中做dict key的只有這兩種類型
72
/*
----------------------------- API implementation -------------------------
*/
73
74
/*
Reset an hashtable already initialized with ht_init().
75
* NOTE: This function should only called by ht_destroy().
*/ //Note: and _dictInit
76
static
void
_dictReset(dictht *
ht)
77
{
78
ht->table =
NULL;
79
ht->size =
0
;
80
ht->sizemask =
0
;
81
ht->used =
0
;
82
}
83
84
/*
Create a new hash table
*/
85
dict *dictCreate(dictType *
type,
86
void
*
privDataPtr)
87
{
88
dict *d = zmalloc(
sizeof
(*
d));
89
90
_dictInit(d,type,privDataPtr);
91
return
d;
92
}
93
//解釋下參數(shù)中的privDataPtr,它是dict struct的一個成員,然后分別在dictType的keyDup函數(shù)、valDup函數(shù)、keyCompare函數(shù)、keyDestructor函數(shù)、valDestructor函數(shù)中作為第一個參數(shù)使用。dict的對外API通過dictCreate函數(shù)對其進行初始化。我在看dict.h時也很疑惑這個接口設計。這里揭底,在redis當前版本對dictCreate的所有調用中(networking.c一次、object.c兩次、redis.c七次、t_hash.c一次、t_set.c一次、t_zset.c一次),此參數(shù)均被賦為了NULL,可能是歷史原因或是考慮后續(xù)兼容性?求大牛指點。anyway,現(xiàn)在可以無視之了。
94
/*
Initialize the hash table
*/
95
int
_dictInit(dict *d, dictType *
type,
96
void
*
privDataPtr)
97
{
98
_dictReset(&d->ht[
0
]);
99
_dictReset(&d->ht[
1
]);
100
d->type =
type;
101
d->privdata =
privDataPtr;
102
d->rehashidx = -
1
;
103
d->iterators =
0
;
104
return
DICT_OK;
105
}
106
107
/*
Resize the table to the minimal size that contains all the elements,
108
* but with the invariant of a USER/BUCKETS ratio near to <= 1
*/
109
int
dictResize(dict *
d)
110
{
111
int
minimal;
112
113
if
(!dict_can_resize || dictIsRehashing(d))
return
DICT_ERR;
114
minimal = d->ht[
0
].used;
115
if
(minimal <
DICT_HT_INITIAL_SIZE)
116
minimal =
DICT_HT_INITIAL_SIZE;
117
return
dictExpand(d, minimal);
118
}
119
120
/*
Expand or create the hashtable
*/
121
int
dictExpand(dict *d, unsigned
long
size)
122
{
123
dictht n;
/*
the new hashtable
*/
124
unsigned
long
realsize =
_dictNextPower(size); //dict的size是2的倍數(shù),所以這里記錄了真是的記錄長度,需要比size長
125
126
/*
the size is invalid if it is smaller than the number of
127
* elements already inside the hashtable
*/
128
if
(dictIsRehashing(d) || d->ht[
0
].used >
size)
129
return
DICT_ERR;
130
131
/*
Allocate the new hashtable and initialize all pointers to NULL
*/
132
n.size =
realsize;
133
n.sizemask = realsize-
1
;
134
n.table = zcalloc(realsize*
sizeof
(dictEntry*
));
135
n.used =
0
;
136
137
/*
Is this the first initialization? If so it's not really a rehashing
138
* we just set the first hash table so that it can accept keys.
*/
139
if
(d->ht[
0
].table ==
NULL) {
140
d->ht[
0
] =
n;
141
return
DICT_OK;
142
}
143
144
/*
Prepare a second hash table for incremental rehashing
*/
145
d->ht[
1
] =
n;
146
d->rehashidx =
0
; //只有在第二次調用dictExpand時,才是rehash的前奏
147
return
DICT_OK;
148
}
149
150
/*
Performs N steps of incremental rehashing. Returns 1 if there are still
151
* keys to move from the old to the new hash table, otherwise 0 is returned.
152
* Note that a rehashing step consists in moving a bucket (that may have more
153
* thank one key as we use chaining) from the old to the new hash table.
*/
154
int
dictRehash(dict *d,
int
n) {
155
if
(!dictIsRehashing(d))
return
0
;
156
157
while
(n--
) {
158
dictEntry *de, *
nextde;
159
160
/*
Check if we already rehashed the whole table...
*/
161
if
(d->ht[
0
].used ==
0
) {
162
zfree(d->ht[
0
].table);
163
d->ht[
0
] = d->ht[
1
];
164
_dictReset(&d->ht[
1
]);
165
d->rehashidx = -
1
;
166
return
0
;
167
}
168
169
/*
Note that rehashidx can't overflow as we are sure there are more
170
* elements because ht[0].used != 0
*/
171
while
(d->ht[
0
].table[d->rehashidx] == NULL) d->rehashidx++
;
172
de = d->ht[
0
].table[d->
rehashidx];
173
/*
Move all the keys in this bucket from the old to the new hash HT
*/
174
while
(de) {
175
unsigned
int
h;
176
177
nextde = de->
next;
178
/*
Get the index in the new hash table
*/
179
h = dictHashKey(d, de->key) & d->ht[
1
].sizemask;
180
de->next = d->ht[
1
].table[h];
181
d->ht[
1
].table[h] =
de;
182
d->ht[
0
].used--
;
183
d->ht[
1
].used++
;
184
de =
nextde;
185
}
186
d->ht[
0
].table[d->rehashidx] =
NULL;
187
d->rehashidx++
;
188
}
189
return
1
;
190
}
191
192
long
long
timeInMilliseconds(
void
) {
193
struct
timeval tv;
194
195
gettimeofday(&
tv,NULL);
196
return
(((
long
long
)tv.tv_sec)*
1000
)+(tv.tv_usec/
1000
);
197
} //時間單位是ms
198
199
/*
Rehash for an amount of time between ms milliseconds and ms+1 milliseconds
*/
200
int
dictRehashMilliseconds(dict *d,
int
ms) {
201
long
long
start =
timeInMilliseconds();
202
int
rehashes =
0
;
203
204
while
(dictRehash(d,
100
)) {
205
rehashes +=
100
;
206
if
(timeInMilliseconds()-start > ms)
break
;
207
}
208
return
rehashes;
209
} //返回值是rehash數(shù)目,為100的整數(shù)倍,傳進參數(shù)的ms只是個下限,并不嚴格限制
210
211
/*
This function performs just a step of rehashing, and only if there are
212
* no safe iterators bound to our hash table. When we have iterators in the
213
* middle of a rehashing we can't mess with the two hash tables otherwise
214
* some element can be missed or duplicated.
215
*
216
* This function is called by common lookup or update operations in the
217
* dictionary so that the hash table automatically migrates from H1 to H2
218
* while it is actively used.
*/
219
static
void
_dictRehashStep(dict *
d) {
220
if
(d->iterators ==
0
) dictRehash(d,
1
);
221
} //在dict上存在遍歷用的iterator時,不會盡心rehash操作,否則遍歷將會丟數(shù)據或出現(xiàn)重復。這個函數(shù)是在lookup和update時調用,使得rehash的過程伴隨著針對dict操作的過程慢慢完成
222
223
/*
Add an element to the target hash table
*/
224
int
dictAdd(dict *d,
void
*key,
void
*
val)
225
{
226
int
index;
227
dictEntry *
entry;
228
dictht *
ht;
229
230
if
(dictIsRehashing(d)) _dictRehashStep(d);
231
232
/*
Get the index of the new element, or -1 if
233
* the element already exists.
*/
234
if
((index = _dictKeyIndex(d, key)) == -
1
)
235
return
DICT_ERR;
236
237
/*
Allocates the memory and stores key
*/
238
ht = dictIsRehashing(d) ? &d->ht[
1
] : &d->ht[
0
]; //如果dict正在進行rehash,則將新來的數(shù)據存在d->ht[1]上,因為d->ht[0]是“舊表”,不再接受新的數(shù)據
239
entry = zmalloc(
sizeof
(*
entry));
240
entry->next = ht->
table[index];
241
ht->table[index] =
entry;
242
ht->used++
;
243
244
/*
Set the hash entry fields.
*/
245
dictSetHashKey(d, entry, key);
246
dictSetHashVal(d, entry, val);
247
return
DICT_OK;
248
}
249
250
/*
Add an element, discarding the old if the key already exists.
251
* Return 1 if the key was added from scratch, 0 if there was already an
252
* element with such key and dictReplace() just performed a value update
253
* operation.
*/
254
int
dictReplace(dict *d,
void
*key,
void
*
val)
255
{
256
dictEntry *
entry, auxentry;
257
258
/*
Try to add the element. If the key
259
* does not exists dictAdd will suceed.
*/
260
if
(dictAdd(d, key, val) ==
DICT_OK)
261
return
1
;
262
/*
It already exists, get the entry
*/
263
entry =
dictFind(d, key);
264
/*
Free the old value and set the new one
*/
265
/*
Set the new value and free the old one. Note that it is important
266
* to do that in this order, as the value may just be exactly the same
267
* as the previous one. In this context, think to reference counting,
268
* you want to increment (set), and then decrement (free), and not the
269
* reverse.
*/
270
auxentry = *
entry;
271
dictSetHashVal(d, entry, val);
272
dictFreeEntryVal(d, &
auxentry);
273
return
0
;
274
}
275
276
/*
Search and remove an element
*/
277
static
int
dictGenericDelete(dict *d,
const
void
*key,
int
nofree)
278
{
279
unsigned
int
h, idx;
280
dictEntry *he, *
prevHe;
281
int
table;
282
283
if
(d->ht[
0
].size ==
0
)
return
DICT_ERR;
/*
d->ht[0].table is NULL
*/
284
if
(dictIsRehashing(d)) _dictRehashStep(d);
285
h =
dictHashKey(d, key);
286
287
for
(table =
0
; table <=
1
; table++
) {
288
idx = h & d->
ht[table].sizemask;
289
he = d->
ht[table].table[idx];
290
prevHe =
NULL;
291
while
(he) {
292
if
(dictCompareHashKeys(d, key, he->
key)) {
293
/*
Unlink the element from the list
*/
294
if
(prevHe)
295
prevHe->next = he->
next;
296
else
297
d->ht[table].table[idx] = he->
next;
298
if
(!
nofree) {
299
dictFreeEntryKey(d, he);
300
dictFreeEntryVal(d, he);
301
}
302
zfree(he);
303
d->ht[table].used--
;
304
return
DICT_OK;
305
}
306
prevHe =
he;
307
he = he->
next;
308
}
309
if
(!dictIsRehashing(d))
break
; //如果在查找的時刻,dict沒有正在進行rehash操作,則此時dict->ht[1]未使用,所以只在dict->ht[0]進行查找操作就可以了
310
}
311
return
DICT_ERR;
/*
not found
*/
312
}
313
314
int
dictDelete(dict *ht,
const
void
*
key) {
315
return
dictGenericDelete(ht,key,
0
);
316
}
317
318
int
dictDeleteNoFree(dict *ht,
const
void
*
key) {
319
return
dictGenericDelete(ht,key,
1
);
320
} //。。。redis的src目錄下沒有任何一處調用這個不料理自己后事的delete函數(shù)
321
322
/*
Destroy an entire dictionary
*/
323
int
_dictClear(dict *d, dictht *
ht) //clear的粒度是hash table而不是dict
324
{
325
unsigned
long
i;
326
327
/*
Free all the elements
*/
328
for
(i =
0
; i < ht->size && ht->used >
0
; i++
) {
329
dictEntry *he, *
nextHe;
330
331
if
((he = ht->table[i]) == NULL)
continue
;
332
while
(he) {
333
nextHe = he->
next;
334
dictFreeEntryKey(d, he);
335
dictFreeEntryVal(d, he);
336
zfree(he);
337
ht->used--
;
338
he =
nextHe;
339
}
340
}
341
/*
Free the table and the allocated cache structure
*/
342
zfree(ht->
table);
343
/*
Re-initialize the table
*/
344
_dictReset(ht);
345
return
DICT_OK;
/*
never fails
*/
346
}
347
348
/*
Clear & Release the hash table
*/
349
void
dictRelease(dict *
d)
350
{
351
_dictClear(d,&d->ht[
0
]);
352
_dictClear(d,&d->ht[
1
]);
353
zfree(d);
354
} //release的粒度是dict,_dictClear只是一個內部接口
355
356
dictEntry *dictFind(dict *d,
const
void
*
key)
357
{
358
dictEntry *
he;
359
unsigned
int
h, idx, table;
360
361
if
(d->ht[
0
].size ==
0
)
return
NULL;
/*
We don't have a table at all
*/
362
if
(dictIsRehashing(d)) _dictRehashStep(d);
363
h =
dictHashKey(d, key);
364
for
(table =
0
; table <=
1
; table++
) {
365
idx = h & d->
ht[table].sizemask;
366
he = d->
ht[table].table[idx];
367
while
(he) {
368
if
(dictCompareHashKeys(d, key, he->
key)) //為何要傳dict參數(shù),因為需要知道具體用什么compare函數(shù)作比較操作
369
return
he;
370
he = he->
next;
371
}
372
if
(!dictIsRehashing(d))
return
NULL; //沒找到,返回NULL,函數(shù)就結束了
373
}
374
return
NULL;
375
}
376
377
void
*dictFetchValue(dict *d,
const
void
*
key) {
378
dictEntry *
he;
379
380
he =
dictFind(d,key);
381
return
he ?
dictGetEntryVal(he) : NULL;
382
}
383
384
dictIterator *dictGetIterator(dict *
d)
385
{
386
dictIterator *iter = zmalloc(
sizeof
(*
iter));
387
388
iter->d =
d;
389
iter->table =
0
;
390
iter->index = -
1
;
391
iter->safe =
0
;
392
iter->entry =
NULL;
393
iter->nextEntry =
NULL;
394
return
iter;
395
} //在調用GetIterator函數(shù)時,并沒有將這個iter在對應的dict上進行注冊
396
397
dictIterator *dictGetSafeIterator(dict *
d) {
398
dictIterator *i =
dictGetIterator(d);
399
400
i->safe =
1
;
401
return
i;
402
}
403
404
dictEntry *dictNext(dictIterator *
iter)
405
{
406
while
(
1
) {
407
if
(iter->entry ==
NULL) {
408
dictht *ht = &iter->d->ht[iter->
table];
409
if
(iter->safe && iter->index == -
1
&& iter->table ==
0
)
410
iter->d->iterators++
; //在對應的dict上進行注冊
411
iter->index++
;
412
if
(iter->index >= (signed) ht->
size) { //在對第一個ht遍歷完成后,轉到第二張表
413
if
(dictIsRehashing(iter->d) && iter->table ==
0
) {
414
iter->table++
;
415
iter->index =
0
;
416
ht = &iter->d->ht[
1
];
417
}
else
{
418
break
;
419
}
420
}
421
iter->entry = ht->table[iter->
index];
422
}
else
{
423
iter->entry = iter->
nextEntry;
424
}
425
if
(iter->
entry) {
426
/*
We need to save the 'next' here, the iterator user
427
* may delete the entry we are returning.
*/
428
iter->nextEntry = iter->entry->
next;
429
return
iter->
entry;
430
}
431
}
432
return
NULL;
433
}
434
435
void
dictReleaseIterator(dictIterator *
iter)
436
{
437
if
(iter->safe && !(iter->index == -
1
&& iter->table ==
0
))
438
iter->d->iterators--
;
439
zfree(iter);
440
}
441
442
/*
Return a random entry from the hash table. Useful to
443
* implement randomized algorithms
*/
444
dictEntry *dictGetRandomKey(dict *
d)
445
{
446
dictEntry *he, *
orighe;
447
unsigned
int
h;
448
int
listlen, listele;
449
450
if
(dictSize(d) ==
0
)
return
NULL;
451
if
(dictIsRehashing(d)) _dictRehashStep(d);
452
if
(dictIsRehashing(d)) {
453
do
{
454
h = random() % (d->ht[
0
].size+d->ht[
1
].size);
455
he = (h >= d->ht[
0
].size) ? d->ht[
1
].table[h - d->ht[
0
].size] :
456
d->ht[
0
].table[h];
457
}
while
(he ==
NULL);
458
}
else
{
459
do
{
460
h = random() & d->ht[
0
].sizemask;
461
he = d->ht[
0
].table[h];
462
}
while
(he ==
NULL);
463
}
464
465
/*
Now we found a non empty bucket, but it is a linked
466
* list and we need to get a random element from the list.
467
* The only sane way to do so is counting the elements and
468
* select a random index.
*/
469
listlen =
0
;
470
orighe =
he;
471
while
(he) {
472
he = he->
next;
473
listlen++
;
474
}
475
listele = random() %
listlen;
476
he =
orighe;
477
while
(listele--) he = he->
next;
478
return
he;
479
} //這個函數(shù)效率很低啊。
480
481
/*
------------------------- private functions ------------------------------
*/
482
483
/*
Expand the hash table if needed
*/
484
static
int
_dictExpandIfNeeded(dict *
d)
485
{
486
/*
Incremental rehashing already in progress. Return.
*/
487
if
(dictIsRehashing(d))
return
DICT_OK;
488
489
/*
If the hash table is empty expand it to the intial size.
*/
490
if
(d->ht[
0
].size ==
0
)
return
dictExpand(d, DICT_HT_INITIAL_SIZE);
491
492
/*
If we reached the 1:1 ratio, and we are allowed to resize the hash
493
* table (global setting) or we should avoid it but the ratio between
494
* elements/buckets is over the "safe" threshold, we resize doubling
495
* the number of buckets.
*/
496
if
(d->ht[
0
].used >= d->ht[
0
].size &&
497
(dict_can_resize ||
498
d->ht[
0
].used/d->ht[
0
].size >
dict_force_resize_ratio))
499
{
500
return
dictExpand(d, ((d->ht[
0
].size > d->ht[
0
].used) ?
501
d->ht[
0
].size : d->ht[
0
].used)*
2
);
502
}
503
return
DICT_OK;
504
}
505
506
/*
Our hash table capability is a power of two
*/
507
static
unsigned
long
_dictNextPower(unsigned
long
size)
508
{
509
unsigned
long
i =
DICT_HT_INITIAL_SIZE;
510
511
if
(size >= LONG_MAX)
return
LONG_MAX;
512
while
(
1
) {
513
if
(i >=
size)
514
return
i;
515
i *=
2
;
516
}
517
}
518
519
/*
Returns the index of a free slot that can be populated with
520
* an hash entry for the given 'key'.
521
* If the key already exists, -1 is returned.
522
*
523
* Note that if we are in the process of rehashing the hash table, the
524
* index is always returned in the context of the second (new) hash table.
*/
525
static
int
_dictKeyIndex(dict *d,
const
void
*
key)
526
{
527
unsigned
int
h, idx, table;
528
dictEntry *
he;
529
530
/*
Expand the hashtable if needed
*/
531
if
(_dictExpandIfNeeded(d) ==
DICT_ERR)
532
return
-
1
;
533
/*
Compute the key hash value
*/
534
h =
dictHashKey(d, key);
535
for
(table =
0
; table <=
1
; table++
) {
536
idx = h & d->
ht[table].sizemask;
537
/*
Search if this slot does not already contain the given key
*/
538
he = d->
ht[table].table[idx];
539
while
(he) {
540
if
(dictCompareHashKeys(d, key, he->
key))
541
return
-
1
;
542
he = he->
next;
543
}
544
if
(!dictIsRehashing(d))
break
;
545
}
546
return
idx;
547
}
548
549
void
dictEmpty(dict *
d) {
550
_dictClear(d,&d->ht[
0
]);
551
_dictClear(d,&d->ht[
1
]);
552
d->rehashidx = -
1
;
553
d->iterators =
0
;
554
}
555
556
#define
DICT_STATS_VECTLEN 50
557
static
void
_dictPrintStatsHt(dictht *
ht) {
558
unsigned
long
i, slots =
0
, chainlen, maxchainlen =
0
;
559
unsigned
long
totchainlen =
0
;
560
unsigned
long
clvector[DICT_STATS_VECTLEN]; //這個clvector是統(tǒng)計hash table中長度為其下標的槽位的個數(shù)
561
562
if
(ht->used ==
0
) {
563
printf(
"
No stats available for empty dictionaries\n
"
);
564
return
;
565
}
566
567
for
(i =
0
; i < DICT_STATS_VECTLEN; i++) clvector[i] =
0
;
568
for
(i =
0
; i < ht->size; i++
) {
569
dictEntry *
he;
570
571
if
(ht->table[i] ==
NULL) {
572
clvector[
0
]++
;
573
continue
;
574
}
575
slots++
;
576
/*
For each hash entry on this slot...
*/
577
chainlen =
0
;
578
he = ht->
table[i];
579
while
(he) {
580
chainlen++
;
581
he = he->
next;
582
}
583
clvector[(chainlen < DICT_STATS_VECTLEN) ? chainlen : (DICT_STATS_VECTLEN-
1
)]++
;
584
if
(chainlen > maxchainlen) maxchainlen =
chainlen;
585
totchainlen +=
chainlen;
586
}
587
printf(
"
Hash table stats:\n
"
);
588
printf(
"
table size: %ld\n
"
, ht->
size);
589
printf(
"
number of elements: %ld\n
"
, ht->
used);
590
printf(
"
different slots: %ld\n
"
, slots); //非空槽位數(shù)
591
printf(
"
max chain length: %ld\n
"
, maxchainlen);
592
printf(
"
avg chain length (counted): %.02f\n
"
, (
float
)totchainlen/
slots);
593
printf(
"
avg chain length (computed): %.02f\n
"
, (
float
)ht->used/
slots);
594
printf(
"
Chain length distribution:\n
"
);
595
for
(i =
0
; i < DICT_STATS_VECTLEN-
1
; i++
) {
596
if
(clvector[i] ==
0
)
continue
;
597
printf(
"
%s%ld: %ld (%.02f%%)\n
"
,(i == DICT_STATS_VECTLEN-
1
)?
"
>=
"
:
""
, i, clvector[i], ((
float
)clvector[i]/ht->size)*
100
);
598
}
599
}
600
601
void
dictPrintStats(dict *
d) {
602
_dictPrintStatsHt(&d->ht[
0
]);
603
if
(dictIsRehashing(d)) {
604
printf(
"
-- Rehashing into ht[1]:\n
"
);
605
_dictPrintStatsHt(&d->ht[
1
]);
606
}
607
}
608
609
void
dictEnableResize(
void
) {
610
dict_can_resize =
1
;
611
}
612
613
void
dictDisableResize(
void
) {
614
dict_can_resize =
0
;
615
}
更多文章、技術交流、商務合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

