dict.c 23.0 KB
Newer Older
A
antirez 已提交
1 2 3 4 5 6 7
/* Hash Tables Implementation.
 *
 * This file implements in memory hash tables with insert/del/replace/find/
 * get-random-element operations. Hash tables will auto resize if needed
 * tables of power of two in size are used, collisions are handled by
 * chaining. See the source code for more information... :)
 *
8
 * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
A
antirez 已提交
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of Redis nor the names of its contributors may be used
 *     to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

36 37
#include "fmacros.h"

A
antirez 已提交
38 39 40 41 42
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
43
#include <limits.h>
A
antirez 已提交
44
#include <sys/time.h>
45
#include <ctype.h>
A
antirez 已提交
46 47 48 49

#include "dict.h"
#include "zmalloc.h"

50 51 52
/* 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
53 54 55 56 57
 * 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. */
58
static int dict_can_resize = 1;
59
static unsigned int dict_force_resize_ratio = 5;
60

A
antirez 已提交
61 62 63
/* -------------------------- private prototypes ---------------------------- */

static int _dictExpandIfNeeded(dict *ht);
64
static unsigned long _dictNextPower(unsigned long size);
A
antirez 已提交
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
static int _dictKeyIndex(dict *ht, const void *key);
static int _dictInit(dict *ht, dictType *type, void *privDataPtr);

/* -------------------------- hash functions -------------------------------- */

/* Thomas Wang's 32 bit Mix Function */
unsigned int dictIntHashFunction(unsigned int key)
{
    key += ~(key << 15);
    key ^=  (key >> 10);
    key +=  (key << 3);
    key ^=  (key >> 6);
    key += ~(key << 11);
    key ^=  (key >> 16);
    return key;
}

/* Identity hash function for integer keys */
unsigned int dictIdentityHashFunction(unsigned int key)
{
    return key;
}

88 89 90 91 92 93 94 95 96 97
static int dict_hash_function_seed = 5183;

void dictSetHashFunctionSeed(unsigned int seed) {
    dict_hash_function_seed = seed;
}

unsigned int dictGetHashFunctionSeed(void) {
    return dict_hash_function_seed;
}

A
antirez 已提交
98 99 100
/* Generic hash function (a popular one from Bernstein).
 * I tested a few and this was the best. */
unsigned int dictGenHashFunction(const unsigned char *buf, int len) {
101
    unsigned int hash = dict_hash_function_seed;
A
antirez 已提交
102 103 104 105 106 107

    while (len--)
        hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */
    return hash;
}

108 109
/* And a case insensitive version */
unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) {
110
    unsigned int hash = dict_hash_function_seed;
111 112 113 114 115 116

    while (len--)
        hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */
    return hash;
}

A
antirez 已提交
117 118 119 120
/* ----------------------------- API implementation ------------------------- */

/* Reset an hashtable already initialized with ht_init().
 * NOTE: This function should only called by ht_destroy(). */
121
static void _dictReset(dictht *ht)
A
antirez 已提交
122 123 124 125 126 127 128 129 130 131 132
{
    ht->table = NULL;
    ht->size = 0;
    ht->sizemask = 0;
    ht->used = 0;
}

/* Create a new hash table */
dict *dictCreate(dictType *type,
        void *privDataPtr)
{
B
Benjamin Kramer 已提交
133
    dict *d = zmalloc(sizeof(*d));
A
antirez 已提交
134

135 136
    _dictInit(d,type,privDataPtr);
    return d;
A
antirez 已提交
137 138 139
}

/* Initialize the hash table */
140
int _dictInit(dict *d, dictType *type,
A
antirez 已提交
141 142
        void *privDataPtr)
{
143 144 145 146 147 148
    _dictReset(&d->ht[0]);
    _dictReset(&d->ht[1]);
    d->type = type;
    d->privdata = privDataPtr;
    d->rehashidx = -1;
    d->iterators = 0;
A
antirez 已提交
149 150 151 152
    return DICT_OK;
}

/* Resize the table to the minimal size that contains all the elements,
153
 * but with the invariant of a USER/BUCKETS ratio near to <= 1 */
154
int dictResize(dict *d)
A
antirez 已提交
155
{
156
    int minimal;
A
antirez 已提交
157

158 159
    if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;
    minimal = d->ht[0].used;
A
antirez 已提交
160 161
    if (minimal < DICT_HT_INITIAL_SIZE)
        minimal = DICT_HT_INITIAL_SIZE;
162
    return dictExpand(d, minimal);
A
antirez 已提交
163 164 165
}

/* Expand or create the hashtable */
166
int dictExpand(dict *d, unsigned long size)
A
antirez 已提交
167
{
168 169
    dictht n; /* the new hashtable */
    unsigned long realsize = _dictNextPower(size);
A
antirez 已提交
170 171 172

    /* the size is invalid if it is smaller than the number of
     * elements already inside the hashtable */
173
    if (dictIsRehashing(d) || d->ht[0].used > size)
A
antirez 已提交
174 175
        return DICT_ERR;

176
    /* Allocate the new hashtable and initialize all pointers to NULL */
A
antirez 已提交
177 178
    n.size = realsize;
    n.sizemask = realsize-1;
179
    n.table = zcalloc(realsize*sizeof(dictEntry*));
180
    n.used = 0;
A
antirez 已提交
181

182 183 184 185 186 187
    /* Is this the first initialization? If so it's not really a rehashing
     * we just set the first hash table so that it can accept keys. */
    if (d->ht[0].table == NULL) {
        d->ht[0] = n;
        return DICT_OK;
    }
A
antirez 已提交
188

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
    /* Prepare a second hash table for incremental rehashing */
    d->ht[1] = n;
    d->rehashidx = 0;
    return DICT_OK;
}

/* 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) {
B
Benjamin Kramer 已提交
207
            zfree(d->ht[0].table);
208 209 210 211 212 213 214 215
            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 */
216
        assert(d->ht[0].size > (unsigned)d->rehashidx);
217 218 219 220
        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) {
A
antirez 已提交
221 222
            unsigned int h;

223 224 225 226 227 228 229 230
            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;
A
antirez 已提交
231
        }
232 233
        d->ht[0].table[d->rehashidx] = NULL;
        d->rehashidx++;
A
antirez 已提交
234
    }
235 236
    return 1;
}
A
antirez 已提交
237

A
antirez 已提交
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
long long timeInMilliseconds(void) {
    struct timeval tv;

    gettimeofday(&tv,NULL);
    return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);
}

/* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */
int dictRehashMilliseconds(dict *d, int ms) {
    long long start = timeInMilliseconds();
    int rehashes = 0;

    while(dictRehash(d,100)) {
        rehashes += 100;
        if (timeInMilliseconds()-start > ms) break;
    }
    return rehashes;
}

257
/* This function performs just a step of rehashing, and only if there are
258 259 260
 * no safe iterators bound to our hash table. When we have iterators in the
 * middle of a rehashing we can't mess with the two hash tables otherwise
 * some element can be missed or duplicated.
261 262 263 264 265 266
 *
 * This function is called by common lookup or update operations in the
 * dictionary so that the hash table automatically migrates from H1 to H2
 * while it is actively used. */
static void _dictRehashStep(dict *d) {
    if (d->iterators == 0) dictRehash(d,1);
A
antirez 已提交
267 268 269
}

/* Add an element to the target hash table */
270
int dictAdd(dict *d, void *key, void *val)
271 272 273 274
{
    dictEntry *entry = dictAddRaw(d,key);

    if (!entry) return DICT_ERR;
275
    dictSetVal(d, entry, val);
276 277 278 279 280 281 282 283 284 285 286
    return DICT_OK;
}

/* Low level add. This function adds the entry but instead of setting
 * a value returns the dictEntry structure to the user, that will make
 * sure to fill the value field as he wishes.
 *
 * This function is also directly expoed to user API to be called
 * mainly in order to store non-pointers inside the hash value, example:
 *
 * entry = dictAddRaw(dict,mykey);
287
 * if (entry != NULL) dictSetSignedIntegerVal(entry,1000);
288 289 290 291 292 293 294
 *
 * Return values:
 *
 * If key already exists NULL is returned.
 * If key was added, the hash entry is returned to be manipulated by the caller.
 */
dictEntry *dictAddRaw(dict *d, void *key)
A
antirez 已提交
295 296 297
{
    int index;
    dictEntry *entry;
298 299 300
    dictht *ht;

    if (dictIsRehashing(d)) _dictRehashStep(d);
A
antirez 已提交
301 302 303

    /* Get the index of the new element, or -1 if
     * the element already exists. */
304
    if ((index = _dictKeyIndex(d, key)) == -1)
305
        return NULL;
A
antirez 已提交
306

307
    /* Allocate the memory and store the new entry */
308
    ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
B
Benjamin Kramer 已提交
309
    entry = zmalloc(sizeof(*entry));
A
antirez 已提交
310 311
    entry->next = ht->table[index];
    ht->table[index] = entry;
312
    ht->used++;
A
antirez 已提交
313 314

    /* Set the hash entry fields. */
315
    dictSetKey(d, entry, key);
316
    return entry;
A
antirez 已提交
317 318
}

319 320 321 322
/* Add an element, discarding the old if the key already exists.
 * Return 1 if the key was added from scratch, 0 if there was already an
 * element with such key and dictReplace() just performed a value update
 * operation. */
323
int dictReplace(dict *d, void *key, void *val)
A
antirez 已提交
324
{
325
    dictEntry *entry, auxentry;
A
antirez 已提交
326 327 328

    /* Try to add the element. If the key
     * does not exists dictAdd will suceed. */
329
    if (dictAdd(d, key, val) == DICT_OK)
330
        return 1;
A
antirez 已提交
331
    /* It already exists, get the entry */
332
    entry = dictFind(d, key);
333 334 335 336 337 338
    /* Set the new value and free the old one. Note that it is important
     * to do that in this order, as the value may just be exactly the same
     * as the previous one. In this context, think to reference counting,
     * you want to increment (set), and then decrement (free), and not the
     * reverse. */
    auxentry = *entry;
339 340
    dictSetVal(d, entry, val);
    dictFreeVal(d, &auxentry);
341
    return 0;
A
antirez 已提交
342 343
}

344 345 346 347 348 349 350 351 352 353 354 355
/* dictReplaceRaw() is simply a version of dictAddRaw() that always
 * returns the hash entry of the specified key, even if the key already
 * exists and can't be added (in that case the entry of the already
 * existing key is returned.)
 *
 * See dictAddRaw() for more information. */
dictEntry *dictReplaceRaw(dict *d, void *key) {
    dictEntry *entry = dictFind(d,key);

    return entry ? entry : dictAddRaw(d,key);
}

A
antirez 已提交
356
/* Search and remove an element */
357
static int dictGenericDelete(dict *d, const void *key, int nofree)
A
antirez 已提交
358
{
359
    unsigned int h, idx;
A
antirez 已提交
360
    dictEntry *he, *prevHe;
361
    int table;
A
antirez 已提交
362

363 364 365
    if (d->ht[0].size == 0) return DICT_ERR; /* d->ht[0].table is NULL */
    if (dictIsRehashing(d)) _dictRehashStep(d);
    h = dictHashKey(d, key);
A
antirez 已提交
366

367 368 369 370 371
    for (table = 0; table <= 1; table++) {
        idx = h & d->ht[table].sizemask;
        he = d->ht[table].table[idx];
        prevHe = NULL;
        while(he) {
372
            if (dictCompareKeys(d, key, he->key)) {
373 374 375 376 377 378
                /* Unlink the element from the list */
                if (prevHe)
                    prevHe->next = he->next;
                else
                    d->ht[table].table[idx] = he->next;
                if (!nofree) {
379 380
                    dictFreeKey(d, he);
                    dictFreeVal(d, he);
381
                }
B
Benjamin Kramer 已提交
382
                zfree(he);
383 384
                d->ht[table].used--;
                return DICT_OK;
A
antirez 已提交
385
            }
386 387
            prevHe = he;
            he = he->next;
A
antirez 已提交
388
        }
389
        if (!dictIsRehashing(d)) break;
A
antirez 已提交
390 391 392 393 394 395 396 397 398 399 400 401
    }
    return DICT_ERR; /* not found */
}

int dictDelete(dict *ht, const void *key) {
    return dictGenericDelete(ht,key,0);
}

int dictDeleteNoFree(dict *ht, const void *key) {
    return dictGenericDelete(ht,key,1);
}

402 403
/* Destroy an entire dictionary */
int _dictClear(dict *d, dictht *ht)
A
antirez 已提交
404
{
405
    unsigned long i;
A
antirez 已提交
406 407 408 409 410 411 412 413

    /* Free all the elements */
    for (i = 0; i < ht->size && ht->used > 0; i++) {
        dictEntry *he, *nextHe;

        if ((he = ht->table[i]) == NULL) continue;
        while(he) {
            nextHe = he->next;
414 415
            dictFreeKey(d, he);
            dictFreeVal(d, he);
B
Benjamin Kramer 已提交
416
            zfree(he);
A
antirez 已提交
417 418 419 420 421
            ht->used--;
            he = nextHe;
        }
    }
    /* Free the table and the allocated cache structure */
B
Benjamin Kramer 已提交
422
    zfree(ht->table);
A
antirez 已提交
423 424 425 426 427 428
    /* Re-initialize the table */
    _dictReset(ht);
    return DICT_OK; /* never fails */
}

/* Clear & Release the hash table */
429
void dictRelease(dict *d)
A
antirez 已提交
430
{
431 432
    _dictClear(d,&d->ht[0]);
    _dictClear(d,&d->ht[1]);
B
Benjamin Kramer 已提交
433
    zfree(d);
A
antirez 已提交
434 435
}

436
dictEntry *dictFind(dict *d, const void *key)
A
antirez 已提交
437 438
{
    dictEntry *he;
439 440 441 442 443 444 445 446 447
    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) {
448
            if (dictCompareKeys(d, key, he->key))
449 450 451 452
                return he;
            he = he->next;
        }
        if (!dictIsRehashing(d)) return NULL;
A
antirez 已提交
453 454 455 456
    }
    return NULL;
}

457 458 459 460
void *dictFetchValue(dict *d, const void *key) {
    dictEntry *he;

    he = dictFind(d,key);
461
    return he ? dictGetVal(he) : NULL;
462 463
}

464
dictIterator *dictGetIterator(dict *d)
A
antirez 已提交
465
{
B
Benjamin Kramer 已提交
466
    dictIterator *iter = zmalloc(sizeof(*iter));
A
antirez 已提交
467

468 469
    iter->d = d;
    iter->table = 0;
A
antirez 已提交
470
    iter->index = -1;
471
    iter->safe = 0;
A
antirez 已提交
472 473 474 475 476
    iter->entry = NULL;
    iter->nextEntry = NULL;
    return iter;
}

477 478 479 480 481 482 483
dictIterator *dictGetSafeIterator(dict *d) {
    dictIterator *i = dictGetIterator(d);

    i->safe = 1;
    return i;
}

A
antirez 已提交
484 485 486 487
dictEntry *dictNext(dictIterator *iter)
{
    while (1) {
        if (iter->entry == NULL) {
488
            dictht *ht = &iter->d->ht[iter->table];
489 490
            if (iter->safe && iter->index == -1 && iter->table == 0)
                iter->d->iterators++;
A
antirez 已提交
491
            iter->index++;
492 493 494 495 496 497 498 499 500 501
            if (iter->index >= (signed) ht->size) {
                if (dictIsRehashing(iter->d) && iter->table == 0) {
                    iter->table++;
                    iter->index = 0;
                    ht = &iter->d->ht[1];
                } else {
                    break;
                }
            }
            iter->entry = ht->table[iter->index];
A
antirez 已提交
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
        } else {
            iter->entry = iter->nextEntry;
        }
        if (iter->entry) {
            /* We need to save the 'next' here, the iterator user
             * may delete the entry we are returning. */
            iter->nextEntry = iter->entry->next;
            return iter->entry;
        }
    }
    return NULL;
}

void dictReleaseIterator(dictIterator *iter)
{
517 518
    if (iter->safe && !(iter->index == -1 && iter->table == 0))
        iter->d->iterators--;
B
Benjamin Kramer 已提交
519
    zfree(iter);
A
antirez 已提交
520 521 522 523
}

/* Return a random entry from the hash table. Useful to
 * implement randomized algorithms */
524
dictEntry *dictGetRandomKey(dict *d)
A
antirez 已提交
525
{
526
    dictEntry *he, *orighe;
A
antirez 已提交
527 528 529
    unsigned int h;
    int listlen, listele;

530 531 532 533 534 535 536 537 538 539 540 541 542 543
    if (dictSize(d) == 0) return NULL;
    if (dictIsRehashing(d)) _dictRehashStep(d);
    if (dictIsRehashing(d)) {
        do {
            h = random() % (d->ht[0].size+d->ht[1].size);
            he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] :
                                      d->ht[0].table[h];
        } while(he == NULL);
    } else {
        do {
            h = random() & d->ht[0].sizemask;
            he = d->ht[0].table[h];
        } while(he == NULL);
    }
A
antirez 已提交
544 545 546

    /* Now we found a non empty bucket, but it is a linked
     * 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
A
antirez 已提交
548 549
     * select a random index. */
    listlen = 0;
550
    orighe = he;
A
antirez 已提交
551 552 553 554 555
    while(he) {
        he = he->next;
        listlen++;
    }
    listele = random() % listlen;
556
    he = orighe;
A
antirez 已提交
557 558 559 560 561 562 563
    while(listele--) he = he->next;
    return he;
}

/* ------------------------- private functions ------------------------------ */

/* Expand the hash table if needed */
564
static int _dictExpandIfNeeded(dict *d)
A
antirez 已提交
565
{
566
    /* Incremental rehashing already in progress. Return. */
567
    if (dictIsRehashing(d)) return DICT_OK;
568 569 570 571 572 573 574 575 576 577 578 579

    /* If the hash table is empty expand it to the intial size. */
    if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);

    /* If we reached the 1:1 ratio, and we are allowed to resize the hash
     * table (global setting) or we should avoid it but the ratio between
     * elements/buckets is over the "safe" threshold, we resize doubling
     * the number of buckets. */
    if (d->ht[0].used >= d->ht[0].size &&
        (dict_can_resize ||
         d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
    {
580 581
        return dictExpand(d, ((d->ht[0].size > d->ht[0].used) ?
                                    d->ht[0].size : d->ht[0].used)*2);
582
    }
A
antirez 已提交
583 584 585 586
    return DICT_OK;
}

/* Our hash table capability is a power of two */
587
static unsigned long _dictNextPower(unsigned long size)
A
antirez 已提交
588
{
589
    unsigned long i = DICT_HT_INITIAL_SIZE;
A
antirez 已提交
590

591
    if (size >= LONG_MAX) return LONG_MAX;
A
antirez 已提交
592 593 594 595 596 597 598 599 600
    while(1) {
        if (i >= size)
            return i;
        i *= 2;
    }
}

/* Returns the index of a free slot that can be populated with
 * an hash entry for the given 'key'.
601 602 603 604 605
 * 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)
A
antirez 已提交
606
{
A
antirez 已提交
607
    unsigned int h, idx, table;
A
antirez 已提交
608 609 610
    dictEntry *he;

    /* Expand the hashtable if needed */
611
    if (_dictExpandIfNeeded(d) == DICT_ERR)
A
antirez 已提交
612 613
        return -1;
    /* Compute the key hash value */
614
    h = dictHashKey(d, key);
A
antirez 已提交
615 616 617 618 619
    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) {
620
            if (dictCompareKeys(d, key, he->key))
A
antirez 已提交
621 622 623 624
                return -1;
            he = he->next;
        }
        if (!dictIsRehashing(d)) break;
A
antirez 已提交
625
    }
A
antirez 已提交
626
    return idx;
A
antirez 已提交
627 628
}

629 630 631 632 633
void dictEmpty(dict *d) {
    _dictClear(d,&d->ht[0]);
    _dictClear(d,&d->ht[1]);
    d->rehashidx = -1;
    d->iterators = 0;
A
antirez 已提交
634 635 636
}

#define DICT_STATS_VECTLEN 50
637
static void _dictPrintStatsHt(dictht *ht) {
638 639 640
    unsigned long i, slots = 0, chainlen, maxchainlen = 0;
    unsigned long totchainlen = 0;
    unsigned long clvector[DICT_STATS_VECTLEN];
A
antirez 已提交
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667

    if (ht->used == 0) {
        printf("No stats available for empty dictionaries\n");
        return;
    }

    for (i = 0; i < DICT_STATS_VECTLEN; i++) clvector[i] = 0;
    for (i = 0; i < ht->size; i++) {
        dictEntry *he;

        if (ht->table[i] == NULL) {
            clvector[0]++;
            continue;
        }
        slots++;
        /* For each hash entry on this slot... */
        chainlen = 0;
        he = ht->table[i];
        while(he) {
            chainlen++;
            he = he->next;
        }
        clvector[(chainlen < DICT_STATS_VECTLEN) ? chainlen : (DICT_STATS_VECTLEN-1)]++;
        if (chainlen > maxchainlen) maxchainlen = chainlen;
        totchainlen += chainlen;
    }
    printf("Hash table stats:\n");
668 669 670 671
    printf(" table size: %ld\n", ht->size);
    printf(" number of elements: %ld\n", ht->used);
    printf(" different slots: %ld\n", slots);
    printf(" max chain length: %ld\n", maxchainlen);
A
antirez 已提交
672 673 674 675 676
    printf(" avg chain length (counted): %.02f\n", (float)totchainlen/slots);
    printf(" avg chain length (computed): %.02f\n", (float)ht->used/slots);
    printf(" Chain length distribution:\n");
    for (i = 0; i < DICT_STATS_VECTLEN-1; i++) {
        if (clvector[i] == 0) continue;
677
        printf("   %s%ld: %ld (%.02f%%)\n",(i == DICT_STATS_VECTLEN-1)?">= ":"", i, clvector[i], ((float)clvector[i]/ht->size)*100);
A
antirez 已提交
678 679 680
    }
}

681 682 683 684 685 686 687 688
void dictPrintStats(dict *d) {
    _dictPrintStatsHt(&d->ht[0]);
    if (dictIsRehashing(d)) {
        printf("-- Rehashing into ht[1]:\n");
        _dictPrintStatsHt(&d->ht[1]);
    }
}

689 690 691 692 693
void dictEnableResize(void) {
    dict_can_resize = 1;
}

void dictDisableResize(void) {
694
    dict_can_resize = 0;
695 696
}

697 698 699 700 701 702
#if 0

/* The following are just example hash table types implementations.
 * Not useful for Redis so they are commented out.
 */

A
antirez 已提交
703 704 705 706 707 708 709
/* ----------------------- StringCopy Hash Table Type ------------------------*/

static unsigned int _dictStringCopyHTHashFunction(const void *key)
{
    return dictGenHashFunction(key, strlen(key));
}

B
Benjamin Kramer 已提交
710
static void *_dictStringDup(void *privdata, const void *key)
A
antirez 已提交
711 712
{
    int len = strlen(key);
B
Benjamin Kramer 已提交
713
    char *copy = zmalloc(len+1);
A
antirez 已提交
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
    DICT_NOTUSED(privdata);

    memcpy(copy, key, len);
    copy[len] = '\0';
    return copy;
}

static int _dictStringCopyHTKeyCompare(void *privdata, const void *key1,
        const void *key2)
{
    DICT_NOTUSED(privdata);

    return strcmp(key1, key2) == 0;
}

B
Benjamin Kramer 已提交
729
static void _dictStringDestructor(void *privdata, void *key)
A
antirez 已提交
730 731 732
{
    DICT_NOTUSED(privdata);

B
Benjamin Kramer 已提交
733
    zfree(key);
A
antirez 已提交
734 735 736
}

dictType dictTypeHeapStringCopyKey = {
B
Benjamin Kramer 已提交
737 738 739 740 741 742
    _dictStringCopyHTHashFunction, /* hash function */
    _dictStringDup,                /* key dup */
    NULL,                          /* val dup */
    _dictStringCopyHTKeyCompare,   /* key compare */
    _dictStringDestructor,         /* key destructor */
    NULL                           /* val destructor */
A
antirez 已提交
743 744 745 746 747
};

/* This is like StringCopy but does not auto-duplicate the key.
 * It's used for intepreter's shared strings. */
dictType dictTypeHeapStrings = {
B
Benjamin Kramer 已提交
748 749 750 751 752 753
    _dictStringCopyHTHashFunction, /* hash function */
    NULL,                          /* key dup */
    NULL,                          /* val dup */
    _dictStringCopyHTKeyCompare,   /* key compare */
    _dictStringDestructor,         /* key destructor */
    NULL                           /* val destructor */
A
antirez 已提交
754 755 756 757 758
};

/* This is like StringCopy but also automatically handle dynamic
 * allocated C strings as values. */
dictType dictTypeHeapStringCopyKeyValue = {
B
Benjamin Kramer 已提交
759 760 761 762 763 764
    _dictStringCopyHTHashFunction, /* hash function */
    _dictStringDup,                /* key dup */
    _dictStringDup,                /* val dup */
    _dictStringCopyHTKeyCompare,   /* key compare */
    _dictStringDestructor,         /* key destructor */
    _dictStringDestructor,         /* val destructor */
A
antirez 已提交
765
};
766
#endif