1 : /* Hash Tables Implementation.
2 : *
3 : * This file implements in memory hash tables with insert/del/replace/find/
4 : * get-random-element operations. Hash tables will auto resize if needed
5 : * tables of power of two in size are used, collisions are handled by
6 : * chaining. See the source code for more information... :)
7 : *
8 : * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
9 : * All rights reserved.
10 : *
11 : * Redistribution and use in source and binary forms, with or without
12 : * modification, are permitted provided that the following conditions are met:
13 : *
14 : * * Redistributions of source code must retain the above copyright notice,
15 : * this list of conditions and the following disclaimer.
16 : * * Redistributions in binary form must reproduce the above copyright
17 : * notice, this list of conditions and the following disclaimer in the
18 : * documentation and/or other materials provided with the distribution.
19 : * * Neither the name of Redis nor the names of its contributors may be used
20 : * to endorse or promote products derived from this software without
21 : * specific prior written permission.
22 : *
23 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 : * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 : * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27 : * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 : * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 : * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 : * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 : * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 : * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 : * POSSIBILITY OF SUCH DAMAGE.
34 : */
35 :
36 : #include "fmacros.h"
37 :
38 : #include <stdio.h>
39 : #include <stdlib.h>
40 : #include <string.h>
41 : #include <stdarg.h>
42 : #include <assert.h>
43 : #include <limits.h>
44 : #include <sys/time.h>
45 : #include <ctype.h>
46 :
47 : #include "dict.h"
48 : #include "zmalloc.h"
49 :
50 : /* Using dictEnableResize() / dictDisableResize() we make possible to
51 : * enable/disable resizing of the hash table as needed. This is very important
52 : * for Redis, as we use copy-on-write and don't want to move too much memory
53 : * around when there is a child performing saving operations.
54 : *
55 : * Note that even when dict_can_resize is set to 0, not all resizes are
56 : * prevented: an hash table is still allowed to grow if the ratio between
57 : * the number of elements and the buckets > dict_force_resize_ratio. */
58 : static int dict_can_resize = 1;
59 : static unsigned int dict_force_resize_ratio = 5;
60 :
61 : /* -------------------------- private prototypes ---------------------------- */
62 :
63 : static int _dictExpandIfNeeded(dict *ht);
64 : static unsigned long _dictNextPower(unsigned long size);
65 : static int _dictKeyIndex(dict *ht, const void *key);
66 : static int _dictInit(dict *ht, dictType *type, void *privDataPtr);
67 :
68 : /* -------------------------- hash functions -------------------------------- */
69 :
70 : /* Thomas Wang's 32 bit Mix Function */
71 0 : unsigned int dictIntHashFunction(unsigned int key)
72 : {
73 0 : key += ~(key << 15);
74 0 : key ^= (key >> 10);
75 0 : key += (key << 3);
76 0 : key ^= (key >> 6);
77 0 : key += ~(key << 11);
78 0 : key ^= (key >> 16);
79 0 : return key;
80 : }
81 :
82 : /* Identity hash function for integer keys */
83 0 : unsigned int dictIdentityHashFunction(unsigned int key)
84 : {
85 0 : return key;
86 : }
87 :
88 : static int dict_hash_function_seed = 5381;
89 :
90 53 : void dictSetHashFunctionSeed(unsigned int seed) {
91 53 : dict_hash_function_seed = seed;
92 53 : }
93 :
94 0 : unsigned int dictGetHashFunctionSeed(void) {
95 0 : return dict_hash_function_seed;
96 : }
97 :
98 : /* Generic hash function (a popular one from Bernstein).
99 : * I tested a few and this was the best. */
100 8564434 : unsigned int dictGenHashFunction(const unsigned char *buf, int len) {
101 8564434 : unsigned int hash = dict_hash_function_seed;
102 :
103 166933723 : while (len--)
104 149804855 : hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */
105 8564434 : return hash;
106 : }
107 :
108 : /* And a case insensitive version */
109 1780223 : unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) {
110 1780223 : unsigned int hash = dict_hash_function_seed;
111 :
112 10785816 : while (len--)
113 7225370 : hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */
114 1780223 : return hash;
115 : }
116 :
117 : /* ----------------------------- API implementation ------------------------- */
118 :
119 : /* Reset an hashtable already initialized with ht_init().
120 : * NOTE: This function should only called by ht_destroy(). */
121 : static void _dictReset(dictht *ht)
122 : {
123 52671 : ht->table = NULL;
124 52671 : ht->size = 0;
125 52671 : ht->sizemask = 0;
126 52671 : ht->used = 0;
127 : }
128 :
129 : /* Create a new hash table */
130 17120 : dict *dictCreate(dictType *type,
131 : void *privDataPtr)
132 : {
133 17120 : dict *d = zmalloc(sizeof(*d));
134 :
135 : _dictInit(d,type,privDataPtr);
136 17120 : return d;
137 : }
138 :
139 : /* Initialize the hash table */
140 : int _dictInit(dict *d, dictType *type,
141 : void *privDataPtr)
142 : {
143 17120 : _dictReset(&d->ht[0]);
144 17120 : _dictReset(&d->ht[1]);
145 17120 : d->type = type;
146 17120 : d->privdata = privDataPtr;
147 17120 : d->rehashidx = -1;
148 17120 : d->iterators = 0;
149 17120 : return DICT_OK;
150 : }
151 :
152 : /* Resize the table to the minimal size that contains all the elements,
153 : * but with the invariant of a USED/BUCKETS ratio near to <= 1 */
154 433 : int dictResize(dict *d)
155 : {
156 : int minimal;
157 :
158 433 : if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;
159 34 : minimal = d->ht[0].used;
160 34 : if (minimal < DICT_HT_INITIAL_SIZE)
161 10 : minimal = DICT_HT_INITIAL_SIZE;
162 34 : return dictExpand(d, minimal);
163 : }
164 :
165 : /* Expand or create the hashtable */
166 13208 : int dictExpand(dict *d, unsigned long size)
167 : {
168 : dictht n; /* the new hashtable */
169 13208 : unsigned long realsize = _dictNextPower(size);
170 :
171 : /* the size is invalid if it is smaller than the number of
172 : * elements already inside the hashtable */
173 13208 : if (dictIsRehashing(d) || d->ht[0].used > size)
174 0 : return DICT_ERR;
175 :
176 : /* Allocate the new hashtable and initialize all pointers to NULL */
177 13208 : n.size = realsize;
178 13208 : n.sizemask = realsize-1;
179 13208 : n.table = zcalloc(realsize*sizeof(dictEntry*));
180 13208 : n.used = 0;
181 :
182 : /* Is this the first initialization? If so it's not really a rehashing
183 : * we just set the first hash table so that it can accept keys. */
184 13208 : if (d->ht[0].table == NULL) {
185 11771 : d->ht[0] = n;
186 11771 : return DICT_OK;
187 : }
188 :
189 : /* Prepare a second hash table for incremental rehashing */
190 1437 : d->ht[1] = n;
191 1437 : d->rehashidx = 0;
192 1437 : return DICT_OK;
193 : }
194 :
195 : /* Performs N steps of incremental rehashing. Returns 1 if there are still
196 : * keys to move from the old to the new hash table, otherwise 0 is returned.
197 : * Note that a rehashing step consists in moving a bucket (that may have more
198 : * thank one key as we use chaining) from the old to the new hash table. */
199 1232079 : int dictRehash(dict *d, int n) {
200 1232079 : if (!dictIsRehashing(d)) return 0;
201 :
202 2738837 : while(n--) {
203 : dictEntry *de, *nextde;
204 :
205 : /* Check if we already rehashed the whole table... */
206 1507957 : if (d->ht[0].used == 0) {
207 1199 : zfree(d->ht[0].table);
208 1199 : d->ht[0] = d->ht[1];
209 1199 : _dictReset(&d->ht[1]);
210 1199 : d->rehashidx = -1;
211 1199 : return 0;
212 : }
213 :
214 : /* Note that rehashidx can't overflow as we are sure there are more
215 : * elements because ht[0].used != 0 */
216 1506758 : assert(d->ht[0].size > (unsigned)d->rehashidx);
217 984421 : while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++;
218 1506758 : de = d->ht[0].table[d->rehashidx];
219 : /* Move all the keys in this bucket from the old to the new hash HT */
220 5464287 : while(de) {
221 : unsigned int h;
222 :
223 2450771 : nextde = de->next;
224 : /* Get the index in the new hash table */
225 2450771 : h = dictHashKey(d, de->key) & d->ht[1].sizemask;
226 2450771 : de->next = d->ht[1].table[h];
227 2450771 : d->ht[1].table[h] = de;
228 2450771 : d->ht[0].used--;
229 2450771 : d->ht[1].used++;
230 2450771 : de = nextde;
231 : }
232 1506758 : d->ht[0].table[d->rehashidx] = NULL;
233 1506758 : d->rehashidx++;
234 : }
235 1230880 : return 1;
236 : }
237 :
238 2834 : long long timeInMilliseconds(void) {
239 : struct timeval tv;
240 :
241 2834 : gettimeofday(&tv,NULL);
242 2834 : return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);
243 : }
244 :
245 : /* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */
246 67 : int dictRehashMilliseconds(dict *d, int ms) {
247 67 : long long start = timeInMilliseconds();
248 67 : int rehashes = 0;
249 :
250 2880 : while(dictRehash(d,100)) {
251 2767 : rehashes += 100;
252 2767 : if (timeInMilliseconds()-start > ms) break;
253 : }
254 67 : return rehashes;
255 : }
256 :
257 : /* This function performs just a step of rehashing, and only if there are
258 : * no safe iterators bound to our hash table. When we have iterators in the
259 : * middle of a rehashing we can't mess with the two hash tables otherwise
260 : * some element can be missed or duplicated.
261 : *
262 : * This function is called by common lookup or update operations in the
263 : * dictionary so that the hash table automatically migrates from H1 to H2
264 : * while it is actively used. */
265 : static void _dictRehashStep(dict *d) {
266 1229266 : if (d->iterators == 0) dictRehash(d,1);
267 : }
268 :
269 : /* Add an element to the target hash table */
270 1998552 : int dictAdd(dict *d, void *key, void *val)
271 : {
272 1998552 : dictEntry *entry = dictAddRaw(d,key);
273 :
274 1998552 : if (!entry) return DICT_ERR;
275 1980278 : dictSetVal(d, entry, val);
276 1980278 : return DICT_OK;
277 : }
278 :
279 : /* Low level add. This function adds the entry but instead of setting
280 : * a value returns the dictEntry structure to the user, that will make
281 : * sure to fill the value field as he wishes.
282 : *
283 : * This function is also directly expoed to user API to be called
284 : * mainly in order to store non-pointers inside the hash value, example:
285 : *
286 : * entry = dictAddRaw(dict,mykey);
287 : * if (entry != NULL) dictSetSignedIntegerVal(entry,1000);
288 : *
289 : * Return values:
290 : *
291 : * If key already exists NULL is returned.
292 : * If key was added, the hash entry is returned to be manipulated by the caller.
293 : */
294 2017851 : dictEntry *dictAddRaw(dict *d, void *key)
295 : {
296 : int index;
297 : dictEntry *entry;
298 : dictht *ht;
299 :
300 2017851 : if (dictIsRehashing(d)) _dictRehashStep(d);
301 :
302 : /* Get the index of the new element, or -1 if
303 : * the element already exists. */
304 2017851 : if ((index = _dictKeyIndex(d, key)) == -1)
305 18274 : return NULL;
306 :
307 : /* Allocate the memory and store the new entry */
308 1999577 : ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
309 1999577 : entry = zmalloc(sizeof(*entry));
310 1999577 : entry->next = ht->table[index];
311 1999577 : ht->table[index] = entry;
312 1999577 : ht->used++;
313 :
314 : /* Set the hash entry fields. */
315 1999577 : dictSetKey(d, entry, key);
316 1999577 : return entry;
317 : }
318 :
319 : /* Add an element, discarding the old if the key already exists.
320 : * Return 1 if the key was added from scratch, 0 if there was already an
321 : * element with such key and dictReplace() just performed a value update
322 : * operation. */
323 22092 : int dictReplace(dict *d, void *key, void *val)
324 : {
325 : dictEntry *entry, auxentry;
326 :
327 : /* Try to add the element. If the key
328 : * does not exists dictAdd will suceed. */
329 22092 : if (dictAdd(d, key, val) == DICT_OK)
330 4552 : return 1;
331 : /* It already exists, get the entry */
332 17540 : entry = dictFind(d, key);
333 : /* Set the new value and free the old one. Note that it is important
334 : * to do that in this order, as the value may just be exactly the same
335 : * as the previous one. In this context, think to reference counting,
336 : * you want to increment (set), and then decrement (free), and not the
337 : * reverse. */
338 17540 : auxentry = *entry;
339 17540 : dictSetVal(d, entry, val);
340 17540 : dictFreeVal(d, &auxentry);
341 17540 : return 0;
342 : }
343 :
344 : /* dictReplaceRaw() is simply a version of dictAddRaw() that always
345 : * returns the hash entry of the specified key, even if the key already
346 : * exists and can't be added (in that case the entry of the already
347 : * existing key is returned.)
348 : *
349 : * See dictAddRaw() for more information. */
350 19301 : dictEntry *dictReplaceRaw(dict *d, void *key) {
351 19301 : dictEntry *entry = dictFind(d,key);
352 :
353 19301 : return entry ? entry : dictAddRaw(d,key);
354 : }
355 :
356 : /* Search and remove an element */
357 953152 : static int dictGenericDelete(dict *d, const void *key, int nofree)
358 : {
359 : unsigned int h, idx;
360 : dictEntry *he, *prevHe;
361 : int table;
362 :
363 953152 : if (d->ht[0].size == 0) return DICT_ERR; /* d->ht[0].table is NULL */
364 233643 : if (dictIsRehashing(d)) _dictRehashStep(d);
365 233643 : h = dictHashKey(d, key);
366 :
367 242134 : for (table = 0; table <= 1; table++) {
368 238799 : idx = h & d->ht[table].sizemask;
369 238799 : he = d->ht[table].table[idx];
370 238799 : prevHe = NULL;
371 605052 : while(he) {
372 207275 : if (dictCompareKeys(d, key, he->key)) {
373 : /* Unlink the element from the list */
374 79821 : if (prevHe)
375 8615 : prevHe->next = he->next;
376 : else
377 71206 : d->ht[table].table[idx] = he->next;
378 79821 : if (!nofree) {
379 79821 : dictFreeKey(d, he);
380 79821 : dictFreeVal(d, he);
381 : }
382 79821 : zfree(he);
383 79821 : d->ht[table].used--;
384 79821 : return DICT_OK;
385 : }
386 127454 : prevHe = he;
387 127454 : he = he->next;
388 : }
389 158978 : if (!dictIsRehashing(d)) break;
390 : }
391 153822 : return DICT_ERR; /* not found */
392 : }
393 :
394 953152 : int dictDelete(dict *ht, const void *key) {
395 953152 : return dictGenericDelete(ht,key,0);
396 : }
397 :
398 0 : int dictDeleteNoFree(dict *ht, const void *key) {
399 0 : return dictGenericDelete(ht,key,1);
400 : }
401 :
402 : /* Destroy an entire dictionary */
403 17232 : int _dictClear(dict *d, dictht *ht)
404 : {
405 : unsigned long i;
406 :
407 : /* Free all the elements */
408 263225 : for (i = 0; i < ht->size && ht->used > 0; i++) {
409 : dictEntry *he, *nextHe;
410 :
411 245993 : if ((he = ht->table[i]) == NULL) continue;
412 278690 : while(he) {
413 169500 : nextHe = he->next;
414 169500 : dictFreeKey(d, he);
415 169500 : dictFreeVal(d, he);
416 169500 : zfree(he);
417 169500 : ht->used--;
418 169500 : he = nextHe;
419 : }
420 : }
421 : /* Free the table and the allocated cache structure */
422 17232 : zfree(ht->table);
423 : /* Re-initialize the table */
424 : _dictReset(ht);
425 17232 : return DICT_OK; /* never fails */
426 : }
427 :
428 : /* Clear & Release the hash table */
429 6308 : void dictRelease(dict *d)
430 : {
431 6308 : _dictClear(d,&d->ht[0]);
432 6308 : _dictClear(d,&d->ht[1]);
433 6308 : zfree(d);
434 6308 : }
435 :
436 5687199 : dictEntry *dictFind(dict *d, const void *key)
437 : {
438 : dictEntry *he;
439 : unsigned int h, idx, table;
440 :
441 5687199 : if (d->ht[0].size == 0) return NULL; /* We don't have a table at all */
442 5642392 : if (dictIsRehashing(d)) _dictRehashStep(d);
443 5642392 : h = dictHashKey(d, key);
444 6245109 : for (table = 0; table <= 1; table++) {
445 6008447 : idx = h & d->ht[table].sizemask;
446 6008447 : he = d->ht[table].table[idx];
447 14660180 : while(he) {
448 6098215 : if (dictCompareKeys(d, key, he->key))
449 3454929 : return he;
450 2643286 : he = he->next;
451 : }
452 2553518 : if (!dictIsRehashing(d)) return NULL;
453 : }
454 236662 : return NULL;
455 : }
456 :
457 1762161 : void *dictFetchValue(dict *d, const void *key) {
458 : dictEntry *he;
459 :
460 1762161 : he = dictFind(d,key);
461 1762161 : return he ? dictGetVal(he) : NULL;
462 : }
463 :
464 31493 : dictIterator *dictGetIterator(dict *d)
465 : {
466 31493 : dictIterator *iter = zmalloc(sizeof(*iter));
467 :
468 31493 : iter->d = d;
469 31493 : iter->table = 0;
470 31493 : iter->index = -1;
471 31493 : iter->safe = 0;
472 31493 : iter->entry = NULL;
473 31493 : iter->nextEntry = NULL;
474 31493 : return iter;
475 : }
476 :
477 151 : dictIterator *dictGetSafeIterator(dict *d) {
478 151 : dictIterator *i = dictGetIterator(d);
479 :
480 151 : i->safe = 1;
481 151 : return i;
482 : }
483 :
484 12555080 : dictEntry *dictNext(dictIterator *iter)
485 : {
486 : while (1) {
487 12555080 : if (iter->entry == NULL) {
488 7126069 : dictht *ht = &iter->d->ht[iter->table];
489 7126069 : if (iter->safe && iter->index == -1 && iter->table == 0)
490 151 : iter->d->iterators++;
491 7126069 : iter->index++;
492 7126069 : if (iter->index >= (signed) ht->size) {
493 32030 : if (dictIsRehashing(iter->d) && iter->table == 0) {
494 537 : iter->table++;
495 537 : iter->index = 0;
496 537 : ht = &iter->d->ht[1];
497 : } else {
498 : break;
499 : }
500 : }
501 7094576 : iter->entry = ht->table[iter->index];
502 : } else {
503 5429011 : iter->entry = iter->nextEntry;
504 : }
505 12523587 : if (iter->entry) {
506 : /* We need to save the 'next' here, the iterator user
507 : * may delete the entry we are returning. */
508 5429011 : iter->nextEntry = iter->entry->next;
509 5429011 : return iter->entry;
510 : }
511 : }
512 31493 : return NULL;
513 : }
514 :
515 31493 : void dictReleaseIterator(dictIterator *iter)
516 : {
517 31493 : if (iter->safe && !(iter->index == -1 && iter->table == 0))
518 151 : iter->d->iterators--;
519 31493 : zfree(iter);
520 31493 : }
521 :
522 : /* Return a random entry from the hash table. Useful to
523 : * implement randomized algorithms */
524 48320 : dictEntry *dictGetRandomKey(dict *d)
525 : {
526 : dictEntry *he, *orighe;
527 : unsigned int h;
528 : int listlen, listele;
529 :
530 48320 : if (dictSize(d) == 0) return NULL;
531 48318 : if (dictIsRehashing(d)) _dictRehashStep(d);
532 48318 : if (dictIsRehashing(d)) {
533 : do {
534 14145 : h = random() % (d->ht[0].size+d->ht[1].size);
535 23285 : he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] :
536 9140 : d->ht[0].table[h];
537 14145 : } while(he == NULL);
538 : } else {
539 : do {
540 116121 : h = random() & d->ht[0].sizemask;
541 116121 : he = d->ht[0].table[h];
542 116121 : } while(he == NULL);
543 : }
544 :
545 : /* Now we found a non empty bucket, but it is a linked
546 : * list and we need to get a random element from the list.
547 : * The only sane way to do so is counting the elements and
548 : * select a random index. */
549 48318 : listlen = 0;
550 48318 : orighe = he;
551 169496 : while(he) {
552 72860 : he = he->next;
553 72860 : listlen++;
554 : }
555 48318 : listele = random() % listlen;
556 48318 : he = orighe;
557 48318 : while(listele--) he = he->next;
558 48318 : return he;
559 : }
560 :
561 : /* ------------------------- private functions ------------------------------ */
562 :
563 : /* Expand the hash table if needed */
564 : static int _dictExpandIfNeeded(dict *d)
565 : {
566 : /* Incremental rehashing already in progress. Return. */
567 2017851 : if (dictIsRehashing(d)) return DICT_OK;
568 :
569 : /* If the hash table is empty expand it to the intial size. */
570 1166523 : if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);
571 :
572 : /* If we reached the 1:1 ratio, and we are allowed to resize the hash
573 : * table (global setting) or we should avoid it but the ratio between
574 : * elements/buckets is over the "safe" threshold, we resize doubling
575 : * the number of buckets. */
576 1157968 : if (d->ht[0].used >= d->ht[0].size &&
577 1207 : (dict_can_resize ||
578 4 : d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
579 : {
580 1203 : return dictExpand(d, ((d->ht[0].size > d->ht[0].used) ?
581 1203 : d->ht[0].size : d->ht[0].used)*2);
582 : }
583 1155554 : return DICT_OK;
584 : }
585 :
586 : /* Our hash table capability is a power of two */
587 : static unsigned long _dictNextPower(unsigned long size)
588 : {
589 13208 : unsigned long i = DICT_HT_INITIAL_SIZE;
590 :
591 13208 : if (size >= LONG_MAX) return LONG_MAX;
592 : while(1) {
593 18235 : if (i >= size)
594 13208 : return i;
595 5027 : i *= 2;
596 : }
597 : }
598 :
599 : /* Returns the index of a free slot that can be populated with
600 : * an hash entry for the given 'key'.
601 : * If the key already exists, -1 is returned.
602 : *
603 : * Note that if we are in the process of rehashing the hash table, the
604 : * index is always returned in the context of the second (new) hash table. */
605 2017851 : static int _dictKeyIndex(dict *d, const void *key)
606 : {
607 : unsigned int h, idx, table;
608 : dictEntry *he;
609 :
610 : /* Expand the hashtable if needed */
611 2017851 : if (_dictExpandIfNeeded(d) == DICT_ERR)
612 0 : return -1;
613 : /* Compute the key hash value */
614 2017851 : h = dictHashKey(d, key);
615 3722172 : for (table = 0; table <= 1; table++) {
616 2870113 : idx = h & d->ht[table].sizemask;
617 : /* Search if this slot does not already contain the given key */
618 2870113 : he = d->ht[table].table[idx];
619 7463057 : while(he) {
620 1741105 : if (dictCompareKeys(d, key, he->key))
621 18274 : return -1;
622 1722831 : he = he->next;
623 : }
624 2851839 : if (!dictIsRehashing(d)) break;
625 : }
626 1999577 : return idx;
627 : }
628 :
629 2308 : void dictEmpty(dict *d) {
630 2308 : _dictClear(d,&d->ht[0]);
631 2308 : _dictClear(d,&d->ht[1]);
632 2308 : d->rehashidx = -1;
633 2308 : d->iterators = 0;
634 2308 : }
635 :
636 : #define DICT_STATS_VECTLEN 50
637 0 : static void _dictPrintStatsHt(dictht *ht) {
638 0 : unsigned long i, slots = 0, chainlen, maxchainlen = 0;
639 0 : unsigned long totchainlen = 0;
640 : unsigned long clvector[DICT_STATS_VECTLEN];
641 :
642 0 : if (ht->used == 0) {
643 0 : printf("No stats available for empty dictionaries\n");
644 : return;
645 : }
646 :
647 0 : for (i = 0; i < DICT_STATS_VECTLEN; i++) clvector[i] = 0;
648 0 : for (i = 0; i < ht->size; i++) {
649 : dictEntry *he;
650 :
651 0 : if (ht->table[i] == NULL) {
652 0 : clvector[0]++;
653 0 : continue;
654 : }
655 0 : slots++;
656 : /* For each hash entry on this slot... */
657 0 : chainlen = 0;
658 0 : he = ht->table[i];
659 0 : while(he) {
660 0 : chainlen++;
661 0 : he = he->next;
662 : }
663 0 : clvector[(chainlen < DICT_STATS_VECTLEN) ? chainlen : (DICT_STATS_VECTLEN-1)]++;
664 0 : if (chainlen > maxchainlen) maxchainlen = chainlen;
665 0 : totchainlen += chainlen;
666 : }
667 0 : printf("Hash table stats:\n");
668 0 : printf(" table size: %ld\n", ht->size);
669 0 : printf(" number of elements: %ld\n", ht->used);
670 0 : printf(" different slots: %ld\n", slots);
671 0 : printf(" max chain length: %ld\n", maxchainlen);
672 0 : printf(" avg chain length (counted): %.02f\n", (float)totchainlen/slots);
673 0 : printf(" avg chain length (computed): %.02f\n", (float)ht->used/slots);
674 0 : printf(" Chain length distribution:\n");
675 0 : for (i = 0; i < DICT_STATS_VECTLEN-1; i++) {
676 0 : if (clvector[i] == 0) continue;
677 0 : printf(" %s%ld: %ld (%.02f%%)\n",(i == DICT_STATS_VECTLEN-1)?">= ":"", i, clvector[i], ((float)clvector[i]/ht->size)*100);
678 : }
679 : }
680 :
681 0 : void dictPrintStats(dict *d) {
682 0 : _dictPrintStatsHt(&d->ht[0]);
683 0 : if (dictIsRehashing(d)) {
684 0 : printf("-- Rehashing into ht[1]:\n");
685 0 : _dictPrintStatsHt(&d->ht[1]);
686 : }
687 0 : }
688 :
689 27 : void dictEnableResize(void) {
690 27 : dict_can_resize = 1;
691 27 : }
692 :
693 27 : void dictDisableResize(void) {
694 27 : dict_can_resize = 0;
695 27 : }
696 :
697 : #if 0
698 :
699 : /* The following are just example hash table types implementations.
700 : * Not useful for Redis so they are commented out.
701 : */
702 :
703 : /* ----------------------- StringCopy Hash Table Type ------------------------*/
704 :
705 : static unsigned int _dictStringCopyHTHashFunction(const void *key)
706 : {
707 : return dictGenHashFunction(key, strlen(key));
708 : }
709 :
710 : static void *_dictStringDup(void *privdata, const void *key)
711 : {
712 : int len = strlen(key);
713 : char *copy = zmalloc(len+1);
714 : DICT_NOTUSED(privdata);
715 :
716 : memcpy(copy, key, len);
717 : copy[len] = '\0';
718 : return copy;
719 : }
720 :
721 : static int _dictStringCopyHTKeyCompare(void *privdata, const void *key1,
722 : const void *key2)
723 : {
724 : DICT_NOTUSED(privdata);
725 :
726 : return strcmp(key1, key2) == 0;
727 : }
728 :
729 : static void _dictStringDestructor(void *privdata, void *key)
730 : {
731 : DICT_NOTUSED(privdata);
732 :
733 : zfree(key);
734 : }
735 :
736 : dictType dictTypeHeapStringCopyKey = {
737 : _dictStringCopyHTHashFunction, /* hash function */
738 : _dictStringDup, /* key dup */
739 : NULL, /* val dup */
740 : _dictStringCopyHTKeyCompare, /* key compare */
741 : _dictStringDestructor, /* key destructor */
742 : NULL /* val destructor */
743 : };
744 :
745 : /* This is like StringCopy but does not auto-duplicate the key.
746 : * It's used for intepreter's shared strings. */
747 : dictType dictTypeHeapStrings = {
748 : _dictStringCopyHTHashFunction, /* hash function */
749 : NULL, /* key dup */
750 : NULL, /* val dup */
751 : _dictStringCopyHTKeyCompare, /* key compare */
752 : _dictStringDestructor, /* key destructor */
753 : NULL /* val destructor */
754 : };
755 :
756 : /* This is like StringCopy but also automatically handle dynamic
757 : * allocated C strings as values. */
758 : dictType dictTypeHeapStringCopyKeyValue = {
759 : _dictStringCopyHTHashFunction, /* hash function */
760 : _dictStringDup, /* key dup */
761 : _dictStringDup, /* val dup */
762 : _dictStringCopyHTKeyCompare, /* key compare */
763 : _dictStringDestructor, /* key destructor */
764 : _dictStringDestructor, /* val destructor */
765 : };
766 : #endif
|