]> git.saurik.com Git - redis.git/blame_incremental - src/dict.c
Redis 2.6.0 RC8 (2.5.14)
[redis.git] / src / dict.c
... / ...
CommitLineData
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. */
58static int dict_can_resize = 1;
59static unsigned int dict_force_resize_ratio = 5;
60
61/* -------------------------- private prototypes ---------------------------- */
62
63static int _dictExpandIfNeeded(dict *ht);
64static unsigned long _dictNextPower(unsigned long size);
65static int _dictKeyIndex(dict *ht, const void *key);
66static int _dictInit(dict *ht, dictType *type, void *privDataPtr);
67
68/* -------------------------- hash functions -------------------------------- */
69
70/* Thomas Wang's 32 bit Mix Function */
71unsigned int dictIntHashFunction(unsigned int key)
72{
73 key += ~(key << 15);
74 key ^= (key >> 10);
75 key += (key << 3);
76 key ^= (key >> 6);
77 key += ~(key << 11);
78 key ^= (key >> 16);
79 return key;
80}
81
82/* Identity hash function for integer keys */
83unsigned int dictIdentityHashFunction(unsigned int key)
84{
85 return key;
86}
87
88static uint32_t dict_hash_function_seed = 5381;
89
90void dictSetHashFunctionSeed(uint32_t seed) {
91 dict_hash_function_seed = seed;
92}
93
94uint32_t dictGetHashFunctionSeed(void) {
95 return dict_hash_function_seed;
96}
97
98/* MurmurHash2, by Austin Appleby
99 * Note - This code makes a few assumptions about how your machine behaves -
100 * 1. We can read a 4-byte value from any address without crashing
101 * 2. sizeof(int) == 4
102 *
103 * And it has a few limitations -
104 *
105 * 1. It will not work incrementally.
106 * 2. It will not produce the same results on little-endian and big-endian
107 * machines.
108 */
109unsigned int dictGenHashFunction(const void *key, int len) {
110 /* 'm' and 'r' are mixing constants generated offline.
111 They're not really 'magic', they just happen to work well. */
112 uint32_t seed = dict_hash_function_seed;
113 const uint32_t m = 0x5bd1e995;
114 const int r = 24;
115
116 /* Initialize the hash to a 'random' value */
117 uint32_t h = seed ^ len;
118
119 /* Mix 4 bytes at a time into the hash */
120 const unsigned char *data = (const unsigned char *)key;
121
122 while(len >= 4) {
123 uint32_t k = *(uint32_t*)data;
124
125 k *= m;
126 k ^= k >> r;
127 k *= m;
128
129 h *= m;
130 h ^= k;
131
132 data += 4;
133 len -= 4;
134 }
135
136 /* Handle the last few bytes of the input array */
137 switch(len) {
138 case 3: h ^= data[2] << 16;
139 case 2: h ^= data[1] << 8;
140 case 1: h ^= data[0]; h *= m;
141 };
142
143 /* Do a few final mixes of the hash to ensure the last few
144 * bytes are well-incorporated. */
145 h ^= h >> 13;
146 h *= m;
147 h ^= h >> 15;
148
149 return (unsigned int)h;
150}
151
152/* And a case insensitive hash function (based on djb hash) */
153unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) {
154 unsigned int hash = (unsigned int)dict_hash_function_seed;
155
156 while (len--)
157 hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */
158 return hash;
159}
160
161/* ----------------------------- API implementation ------------------------- */
162
163/* Reset a hash table already initialized with ht_init().
164 * NOTE: This function should only be called by ht_destroy(). */
165static void _dictReset(dictht *ht)
166{
167 ht->table = NULL;
168 ht->size = 0;
169 ht->sizemask = 0;
170 ht->used = 0;
171}
172
173/* Create a new hash table */
174dict *dictCreate(dictType *type,
175 void *privDataPtr)
176{
177 dict *d = zmalloc(sizeof(*d));
178
179 _dictInit(d,type,privDataPtr);
180 return d;
181}
182
183/* Initialize the hash table */
184int _dictInit(dict *d, dictType *type,
185 void *privDataPtr)
186{
187 _dictReset(&d->ht[0]);
188 _dictReset(&d->ht[1]);
189 d->type = type;
190 d->privdata = privDataPtr;
191 d->rehashidx = -1;
192 d->iterators = 0;
193 return DICT_OK;
194}
195
196/* Resize the table to the minimal size that contains all the elements,
197 * but with the invariant of a USED/BUCKETS ratio near to <= 1 */
198int dictResize(dict *d)
199{
200 int minimal;
201
202 if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;
203 minimal = d->ht[0].used;
204 if (minimal < DICT_HT_INITIAL_SIZE)
205 minimal = DICT_HT_INITIAL_SIZE;
206 return dictExpand(d, minimal);
207}
208
209/* Expand or create the hash table */
210int dictExpand(dict *d, unsigned long size)
211{
212 dictht n; /* the new hash table */
213 unsigned long realsize = _dictNextPower(size);
214
215 /* the size is invalid if it is smaller than the number of
216 * elements already inside the hash table */
217 if (dictIsRehashing(d) || d->ht[0].used > size)
218 return DICT_ERR;
219
220 /* Allocate the new hash table and initialize all pointers to NULL */
221 n.size = realsize;
222 n.sizemask = realsize-1;
223 n.table = zcalloc(realsize*sizeof(dictEntry*));
224 n.used = 0;
225
226 /* Is this the first initialization? If so it's not really a rehashing
227 * we just set the first hash table so that it can accept keys. */
228 if (d->ht[0].table == NULL) {
229 d->ht[0] = n;
230 return DICT_OK;
231 }
232
233 /* Prepare a second hash table for incremental rehashing */
234 d->ht[1] = n;
235 d->rehashidx = 0;
236 return DICT_OK;
237}
238
239/* Performs N steps of incremental rehashing. Returns 1 if there are still
240 * keys to move from the old to the new hash table, otherwise 0 is returned.
241 * Note that a rehashing step consists in moving a bucket (that may have more
242 * thank one key as we use chaining) from the old to the new hash table. */
243int dictRehash(dict *d, int n) {
244 if (!dictIsRehashing(d)) return 0;
245
246 while(n--) {
247 dictEntry *de, *nextde;
248
249 /* Check if we already rehashed the whole table... */
250 if (d->ht[0].used == 0) {
251 zfree(d->ht[0].table);
252 d->ht[0] = d->ht[1];
253 _dictReset(&d->ht[1]);
254 d->rehashidx = -1;
255 return 0;
256 }
257
258 /* Note that rehashidx can't overflow as we are sure there are more
259 * elements because ht[0].used != 0 */
260 assert(d->ht[0].size > (unsigned)d->rehashidx);
261 while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++;
262 de = d->ht[0].table[d->rehashidx];
263 /* Move all the keys in this bucket from the old to the new hash HT */
264 while(de) {
265 unsigned int h;
266
267 nextde = de->next;
268 /* Get the index in the new hash table */
269 h = dictHashKey(d, de->key) & d->ht[1].sizemask;
270 de->next = d->ht[1].table[h];
271 d->ht[1].table[h] = de;
272 d->ht[0].used--;
273 d->ht[1].used++;
274 de = nextde;
275 }
276 d->ht[0].table[d->rehashidx] = NULL;
277 d->rehashidx++;
278 }
279 return 1;
280}
281
282long long timeInMilliseconds(void) {
283 struct timeval tv;
284
285 gettimeofday(&tv,NULL);
286 return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);
287}
288
289/* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */
290int dictRehashMilliseconds(dict *d, int ms) {
291 long long start = timeInMilliseconds();
292 int rehashes = 0;
293
294 while(dictRehash(d,100)) {
295 rehashes += 100;
296 if (timeInMilliseconds()-start > ms) break;
297 }
298 return rehashes;
299}
300
301/* This function performs just a step of rehashing, and only if there are
302 * no safe iterators bound to our hash table. When we have iterators in the
303 * middle of a rehashing we can't mess with the two hash tables otherwise
304 * some element can be missed or duplicated.
305 *
306 * This function is called by common lookup or update operations in the
307 * dictionary so that the hash table automatically migrates from H1 to H2
308 * while it is actively used. */
309static void _dictRehashStep(dict *d) {
310 if (d->iterators == 0) dictRehash(d,1);
311}
312
313/* Add an element to the target hash table */
314int dictAdd(dict *d, void *key, void *val)
315{
316 dictEntry *entry = dictAddRaw(d,key);
317
318 if (!entry) return DICT_ERR;
319 dictSetVal(d, entry, val);
320 return DICT_OK;
321}
322
323/* Low level add. This function adds the entry but instead of setting
324 * a value returns the dictEntry structure to the user, that will make
325 * sure to fill the value field as he wishes.
326 *
327 * This function is also directly exposed to user API to be called
328 * mainly in order to store non-pointers inside the hash value, example:
329 *
330 * entry = dictAddRaw(dict,mykey);
331 * if (entry != NULL) dictSetSignedIntegerVal(entry,1000);
332 *
333 * Return values:
334 *
335 * If key already exists NULL is returned.
336 * If key was added, the hash entry is returned to be manipulated by the caller.
337 */
338dictEntry *dictAddRaw(dict *d, void *key)
339{
340 int index;
341 dictEntry *entry;
342 dictht *ht;
343
344 if (dictIsRehashing(d)) _dictRehashStep(d);
345
346 /* Get the index of the new element, or -1 if
347 * the element already exists. */
348 if ((index = _dictKeyIndex(d, key)) == -1)
349 return NULL;
350
351 /* Allocate the memory and store the new entry */
352 ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
353 entry = zmalloc(sizeof(*entry));
354 entry->next = ht->table[index];
355 ht->table[index] = entry;
356 ht->used++;
357
358 /* Set the hash entry fields. */
359 dictSetKey(d, entry, key);
360 return entry;
361}
362
363/* Add an element, discarding the old if the key already exists.
364 * Return 1 if the key was added from scratch, 0 if there was already an
365 * element with such key and dictReplace() just performed a value update
366 * operation. */
367int dictReplace(dict *d, void *key, void *val)
368{
369 dictEntry *entry, auxentry;
370
371 /* Try to add the element. If the key
372 * does not exists dictAdd will suceed. */
373 if (dictAdd(d, key, val) == DICT_OK)
374 return 1;
375 /* It already exists, get the entry */
376 entry = dictFind(d, key);
377 /* Set the new value and free the old one. Note that it is important
378 * to do that in this order, as the value may just be exactly the same
379 * as the previous one. In this context, think to reference counting,
380 * you want to increment (set), and then decrement (free), and not the
381 * reverse. */
382 auxentry = *entry;
383 dictSetVal(d, entry, val);
384 dictFreeVal(d, &auxentry);
385 return 0;
386}
387
388/* dictReplaceRaw() is simply a version of dictAddRaw() that always
389 * returns the hash entry of the specified key, even if the key already
390 * exists and can't be added (in that case the entry of the already
391 * existing key is returned.)
392 *
393 * See dictAddRaw() for more information. */
394dictEntry *dictReplaceRaw(dict *d, void *key) {
395 dictEntry *entry = dictFind(d,key);
396
397 return entry ? entry : dictAddRaw(d,key);
398}
399
400/* Search and remove an element */
401static int dictGenericDelete(dict *d, const void *key, int nofree)
402{
403 unsigned int h, idx;
404 dictEntry *he, *prevHe;
405 int table;
406
407 if (d->ht[0].size == 0) return DICT_ERR; /* d->ht[0].table is NULL */
408 if (dictIsRehashing(d)) _dictRehashStep(d);
409 h = dictHashKey(d, key);
410
411 for (table = 0; table <= 1; table++) {
412 idx = h & d->ht[table].sizemask;
413 he = d->ht[table].table[idx];
414 prevHe = NULL;
415 while(he) {
416 if (dictCompareKeys(d, key, he->key)) {
417 /* Unlink the element from the list */
418 if (prevHe)
419 prevHe->next = he->next;
420 else
421 d->ht[table].table[idx] = he->next;
422 if (!nofree) {
423 dictFreeKey(d, he);
424 dictFreeVal(d, he);
425 }
426 zfree(he);
427 d->ht[table].used--;
428 return DICT_OK;
429 }
430 prevHe = he;
431 he = he->next;
432 }
433 if (!dictIsRehashing(d)) break;
434 }
435 return DICT_ERR; /* not found */
436}
437
438int dictDelete(dict *ht, const void *key) {
439 return dictGenericDelete(ht,key,0);
440}
441
442int dictDeleteNoFree(dict *ht, const void *key) {
443 return dictGenericDelete(ht,key,1);
444}
445
446/* Destroy an entire dictionary */
447int _dictClear(dict *d, dictht *ht)
448{
449 unsigned long i;
450
451 /* Free all the elements */
452 for (i = 0; i < ht->size && ht->used > 0; i++) {
453 dictEntry *he, *nextHe;
454
455 if ((he = ht->table[i]) == NULL) continue;
456 while(he) {
457 nextHe = he->next;
458 dictFreeKey(d, he);
459 dictFreeVal(d, he);
460 zfree(he);
461 ht->used--;
462 he = nextHe;
463 }
464 }
465 /* Free the table and the allocated cache structure */
466 zfree(ht->table);
467 /* Re-initialize the table */
468 _dictReset(ht);
469 return DICT_OK; /* never fails */
470}
471
472/* Clear & Release the hash table */
473void dictRelease(dict *d)
474{
475 _dictClear(d,&d->ht[0]);
476 _dictClear(d,&d->ht[1]);
477 zfree(d);
478}
479
480dictEntry *dictFind(dict *d, const void *key)
481{
482 dictEntry *he;
483 unsigned int h, idx, table;
484
485 if (d->ht[0].size == 0) return NULL; /* We don't have a table at all */
486 if (dictIsRehashing(d)) _dictRehashStep(d);
487 h = dictHashKey(d, key);
488 for (table = 0; table <= 1; table++) {
489 idx = h & d->ht[table].sizemask;
490 he = d->ht[table].table[idx];
491 while(he) {
492 if (dictCompareKeys(d, key, he->key))
493 return he;
494 he = he->next;
495 }
496 if (!dictIsRehashing(d)) return NULL;
497 }
498 return NULL;
499}
500
501void *dictFetchValue(dict *d, const void *key) {
502 dictEntry *he;
503
504 he = dictFind(d,key);
505 return he ? dictGetVal(he) : NULL;
506}
507
508dictIterator *dictGetIterator(dict *d)
509{
510 dictIterator *iter = zmalloc(sizeof(*iter));
511
512 iter->d = d;
513 iter->table = 0;
514 iter->index = -1;
515 iter->safe = 0;
516 iter->entry = NULL;
517 iter->nextEntry = NULL;
518 return iter;
519}
520
521dictIterator *dictGetSafeIterator(dict *d) {
522 dictIterator *i = dictGetIterator(d);
523
524 i->safe = 1;
525 return i;
526}
527
528dictEntry *dictNext(dictIterator *iter)
529{
530 while (1) {
531 if (iter->entry == NULL) {
532 dictht *ht = &iter->d->ht[iter->table];
533 if (iter->safe && iter->index == -1 && iter->table == 0)
534 iter->d->iterators++;
535 iter->index++;
536 if (iter->index >= (signed) ht->size) {
537 if (dictIsRehashing(iter->d) && iter->table == 0) {
538 iter->table++;
539 iter->index = 0;
540 ht = &iter->d->ht[1];
541 } else {
542 break;
543 }
544 }
545 iter->entry = ht->table[iter->index];
546 } else {
547 iter->entry = iter->nextEntry;
548 }
549 if (iter->entry) {
550 /* We need to save the 'next' here, the iterator user
551 * may delete the entry we are returning. */
552 iter->nextEntry = iter->entry->next;
553 return iter->entry;
554 }
555 }
556 return NULL;
557}
558
559void dictReleaseIterator(dictIterator *iter)
560{
561 if (iter->safe && !(iter->index == -1 && iter->table == 0))
562 iter->d->iterators--;
563 zfree(iter);
564}
565
566/* Return a random entry from the hash table. Useful to
567 * implement randomized algorithms */
568dictEntry *dictGetRandomKey(dict *d)
569{
570 dictEntry *he, *orighe;
571 unsigned int h;
572 int listlen, listele;
573
574 if (dictSize(d) == 0) return NULL;
575 if (dictIsRehashing(d)) _dictRehashStep(d);
576 if (dictIsRehashing(d)) {
577 do {
578 h = random() % (d->ht[0].size+d->ht[1].size);
579 he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] :
580 d->ht[0].table[h];
581 } while(he == NULL);
582 } else {
583 do {
584 h = random() & d->ht[0].sizemask;
585 he = d->ht[0].table[h];
586 } while(he == NULL);
587 }
588
589 /* Now we found a non empty bucket, but it is a linked
590 * list and we need to get a random element from the list.
591 * The only sane way to do so is counting the elements and
592 * select a random index. */
593 listlen = 0;
594 orighe = he;
595 while(he) {
596 he = he->next;
597 listlen++;
598 }
599 listele = random() % listlen;
600 he = orighe;
601 while(listele--) he = he->next;
602 return he;
603}
604
605/* ------------------------- private functions ------------------------------ */
606
607/* Expand the hash table if needed */
608static int _dictExpandIfNeeded(dict *d)
609{
610 /* Incremental rehashing already in progress. Return. */
611 if (dictIsRehashing(d)) return DICT_OK;
612
613 /* If the hash table is empty expand it to the intial size. */
614 if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);
615
616 /* If we reached the 1:1 ratio, and we are allowed to resize the hash
617 * table (global setting) or we should avoid it but the ratio between
618 * elements/buckets is over the "safe" threshold, we resize doubling
619 * the number of buckets. */
620 if (d->ht[0].used >= d->ht[0].size &&
621 (dict_can_resize ||
622 d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
623 {
624 return dictExpand(d, ((d->ht[0].size > d->ht[0].used) ?
625 d->ht[0].size : d->ht[0].used)*2);
626 }
627 return DICT_OK;
628}
629
630/* Our hash table capability is a power of two */
631static unsigned long _dictNextPower(unsigned long size)
632{
633 unsigned long i = DICT_HT_INITIAL_SIZE;
634
635 if (size >= LONG_MAX) return LONG_MAX;
636 while(1) {
637 if (i >= size)
638 return i;
639 i *= 2;
640 }
641}
642
643/* Returns the index of a free slot that can be populated with
644 * an hash entry for the given 'key'.
645 * If the key already exists, -1 is returned.
646 *
647 * Note that if we are in the process of rehashing the hash table, the
648 * index is always returned in the context of the second (new) hash table. */
649static int _dictKeyIndex(dict *d, const void *key)
650{
651 unsigned int h, idx, table;
652 dictEntry *he;
653
654 /* Expand the hash table if needed */
655 if (_dictExpandIfNeeded(d) == DICT_ERR)
656 return -1;
657 /* Compute the key hash value */
658 h = dictHashKey(d, key);
659 for (table = 0; table <= 1; table++) {
660 idx = h & d->ht[table].sizemask;
661 /* Search if this slot does not already contain the given key */
662 he = d->ht[table].table[idx];
663 while(he) {
664 if (dictCompareKeys(d, key, he->key))
665 return -1;
666 he = he->next;
667 }
668 if (!dictIsRehashing(d)) break;
669 }
670 return idx;
671}
672
673void dictEmpty(dict *d) {
674 _dictClear(d,&d->ht[0]);
675 _dictClear(d,&d->ht[1]);
676 d->rehashidx = -1;
677 d->iterators = 0;
678}
679
680void dictEnableResize(void) {
681 dict_can_resize = 1;
682}
683
684void dictDisableResize(void) {
685 dict_can_resize = 0;
686}
687
688#if 0
689
690/* The following is code that we don't use for Redis currently, but that is part
691of the library. */
692
693/* ----------------------- Debugging ------------------------*/
694
695#define DICT_STATS_VECTLEN 50
696static void _dictPrintStatsHt(dictht *ht) {
697 unsigned long i, slots = 0, chainlen, maxchainlen = 0;
698 unsigned long totchainlen = 0;
699 unsigned long clvector[DICT_STATS_VECTLEN];
700
701 if (ht->used == 0) {
702 printf("No stats available for empty dictionaries\n");
703 return;
704 }
705
706 for (i = 0; i < DICT_STATS_VECTLEN; i++) clvector[i] = 0;
707 for (i = 0; i < ht->size; i++) {
708 dictEntry *he;
709
710 if (ht->table[i] == NULL) {
711 clvector[0]++;
712 continue;
713 }
714 slots++;
715 /* For each hash entry on this slot... */
716 chainlen = 0;
717 he = ht->table[i];
718 while(he) {
719 chainlen++;
720 he = he->next;
721 }
722 clvector[(chainlen < DICT_STATS_VECTLEN) ? chainlen : (DICT_STATS_VECTLEN-1)]++;
723 if (chainlen > maxchainlen) maxchainlen = chainlen;
724 totchainlen += chainlen;
725 }
726 printf("Hash table stats:\n");
727 printf(" table size: %ld\n", ht->size);
728 printf(" number of elements: %ld\n", ht->used);
729 printf(" different slots: %ld\n", slots);
730 printf(" max chain length: %ld\n", maxchainlen);
731 printf(" avg chain length (counted): %.02f\n", (float)totchainlen/slots);
732 printf(" avg chain length (computed): %.02f\n", (float)ht->used/slots);
733 printf(" Chain length distribution:\n");
734 for (i = 0; i < DICT_STATS_VECTLEN-1; i++) {
735 if (clvector[i] == 0) continue;
736 printf(" %s%ld: %ld (%.02f%%)\n",(i == DICT_STATS_VECTLEN-1)?">= ":"", i, clvector[i], ((float)clvector[i]/ht->size)*100);
737 }
738}
739
740void dictPrintStats(dict *d) {
741 _dictPrintStatsHt(&d->ht[0]);
742 if (dictIsRehashing(d)) {
743 printf("-- Rehashing into ht[1]:\n");
744 _dictPrintStatsHt(&d->ht[1]);
745 }
746}
747
748/* ----------------------- StringCopy Hash Table Type ------------------------*/
749
750static unsigned int _dictStringCopyHTHashFunction(const void *key)
751{
752 return dictGenHashFunction(key, strlen(key));
753}
754
755static void *_dictStringDup(void *privdata, const void *key)
756{
757 int len = strlen(key);
758 char *copy = zmalloc(len+1);
759 DICT_NOTUSED(privdata);
760
761 memcpy(copy, key, len);
762 copy[len] = '\0';
763 return copy;
764}
765
766static int _dictStringCopyHTKeyCompare(void *privdata, const void *key1,
767 const void *key2)
768{
769 DICT_NOTUSED(privdata);
770
771 return strcmp(key1, key2) == 0;
772}
773
774static void _dictStringDestructor(void *privdata, void *key)
775{
776 DICT_NOTUSED(privdata);
777
778 zfree(key);
779}
780
781dictType dictTypeHeapStringCopyKey = {
782 _dictStringCopyHTHashFunction, /* hash function */
783 _dictStringDup, /* key dup */
784 NULL, /* val dup */
785 _dictStringCopyHTKeyCompare, /* key compare */
786 _dictStringDestructor, /* key destructor */
787 NULL /* val destructor */
788};
789
790/* This is like StringCopy but does not auto-duplicate the key.
791 * It's used for intepreter's shared strings. */
792dictType dictTypeHeapStrings = {
793 _dictStringCopyHTHashFunction, /* hash function */
794 NULL, /* key dup */
795 NULL, /* val dup */
796 _dictStringCopyHTKeyCompare, /* key compare */
797 _dictStringDestructor, /* key destructor */
798 NULL /* val destructor */
799};
800
801/* This is like StringCopy but also automatically handle dynamic
802 * allocated C strings as values. */
803dictType dictTypeHeapStringCopyKeyValue = {
804 _dictStringCopyHTHashFunction, /* hash function */
805 _dictStringDup, /* key dup */
806 _dictStringDup, /* val dup */
807 _dictStringCopyHTKeyCompare, /* key compare */
808 _dictStringDestructor, /* key destructor */
809 _dictStringDestructor, /* val destructor */
810};
811#endif