2009-03-22 05:30:00 -04:00
|
|
|
/* 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... :)
|
|
|
|
*
|
2012-11-08 12:25:23 -05:00
|
|
|
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
2009-03-22 05:30:00 -04:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2009-03-27 17:00:27 -04:00
|
|
|
#include "fmacros.h"
|
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
2009-04-30 08:48:02 -04:00
|
|
|
#include <limits.h>
|
2010-04-15 12:07:57 -04:00
|
|
|
#include <sys/time.h>
|
2010-11-03 06:23:59 -04:00
|
|
|
#include <ctype.h>
|
2009-03-22 05:30:00 -04:00
|
|
|
|
|
|
|
#include "dict.h"
|
|
|
|
#include "zmalloc.h"
|
2016-09-07 04:32:57 -04:00
|
|
|
#ifndef DICT_BENCHMARK_MAIN
|
2013-08-19 08:54:31 -04:00
|
|
|
#include "redisassert.h"
|
2016-09-07 04:32:57 -04:00
|
|
|
#else
|
|
|
|
#include <assert.h>
|
|
|
|
#endif
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-04-08 14:08:51 -04:00
|
|
|
/* 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
|
2010-09-15 08:09:41 -04:00
|
|
|
* around when there is a child performing saving operations.
|
|
|
|
*
|
|
|
|
* Note that even when dict_can_resize is set to 0, not all resizes are
|
2013-12-05 10:35:32 -05:00
|
|
|
* prevented: a hash table is still allowed to grow if the ratio between
|
2010-09-15 08:09:41 -04:00
|
|
|
* the number of elements and the buckets > dict_force_resize_ratio. */
|
2010-04-08 14:08:51 -04:00
|
|
|
static int dict_can_resize = 1;
|
2010-09-15 08:09:41 -04:00
|
|
|
static unsigned int dict_force_resize_ratio = 5;
|
2010-04-08 14:08:51 -04:00
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
/* -------------------------- private prototypes ---------------------------- */
|
|
|
|
|
|
|
|
static int _dictExpandIfNeeded(dict *ht);
|
2009-04-30 08:48:02 -04:00
|
|
|
static unsigned long _dictNextPower(unsigned long size);
|
2009-03-22 05:30:00 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-10-03 13:14:46 -04:00
|
|
|
static uint32_t dict_hash_function_seed = 5381;
|
2012-01-21 17:05:32 -05:00
|
|
|
|
2012-10-03 13:14:46 -04:00
|
|
|
void dictSetHashFunctionSeed(uint32_t seed) {
|
2012-01-21 17:05:32 -05:00
|
|
|
dict_hash_function_seed = seed;
|
|
|
|
}
|
|
|
|
|
2012-10-03 13:14:46 -04:00
|
|
|
uint32_t dictGetHashFunctionSeed(void) {
|
2012-01-21 17:05:32 -05:00
|
|
|
return dict_hash_function_seed;
|
|
|
|
}
|
|
|
|
|
2012-10-03 13:14:46 -04:00
|
|
|
/* MurmurHash2, by Austin Appleby
|
|
|
|
* Note - This code makes a few assumptions about how your machine behaves -
|
|
|
|
* 1. We can read a 4-byte value from any address without crashing
|
|
|
|
* 2. sizeof(int) == 4
|
|
|
|
*
|
|
|
|
* And it has a few limitations -
|
|
|
|
*
|
|
|
|
* 1. It will not work incrementally.
|
|
|
|
* 2. It will not produce the same results on little-endian and big-endian
|
|
|
|
* machines.
|
|
|
|
*/
|
|
|
|
unsigned int dictGenHashFunction(const void *key, int len) {
|
|
|
|
/* 'm' and 'r' are mixing constants generated offline.
|
|
|
|
They're not really 'magic', they just happen to work well. */
|
|
|
|
uint32_t seed = dict_hash_function_seed;
|
|
|
|
const uint32_t m = 0x5bd1e995;
|
|
|
|
const int r = 24;
|
|
|
|
|
|
|
|
/* Initialize the hash to a 'random' value */
|
|
|
|
uint32_t h = seed ^ len;
|
|
|
|
|
|
|
|
/* Mix 4 bytes at a time into the hash */
|
|
|
|
const unsigned char *data = (const unsigned char *)key;
|
|
|
|
|
|
|
|
while(len >= 4) {
|
|
|
|
uint32_t k = *(uint32_t*)data;
|
|
|
|
|
|
|
|
k *= m;
|
|
|
|
k ^= k >> r;
|
|
|
|
k *= m;
|
|
|
|
|
|
|
|
h *= m;
|
|
|
|
h ^= k;
|
|
|
|
|
|
|
|
data += 4;
|
|
|
|
len -= 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle the last few bytes of the input array */
|
|
|
|
switch(len) {
|
|
|
|
case 3: h ^= data[2] << 16;
|
|
|
|
case 2: h ^= data[1] << 8;
|
|
|
|
case 1: h ^= data[0]; h *= m;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Do a few final mixes of the hash to ensure the last few
|
|
|
|
* bytes are well-incorporated. */
|
|
|
|
h ^= h >> 13;
|
|
|
|
h *= m;
|
|
|
|
h ^= h >> 15;
|
|
|
|
|
|
|
|
return (unsigned int)h;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2012-10-03 13:14:46 -04:00
|
|
|
/* And a case insensitive hash function (based on djb hash) */
|
2010-11-03 06:23:59 -04:00
|
|
|
unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) {
|
2012-10-03 13:14:46 -04:00
|
|
|
unsigned int hash = (unsigned int)dict_hash_function_seed;
|
2010-11-03 06:23:59 -04:00
|
|
|
|
|
|
|
while (len--)
|
|
|
|
hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
/* ----------------------------- API implementation ------------------------- */
|
|
|
|
|
2012-04-07 08:40:29 -04:00
|
|
|
/* Reset a hash table already initialized with ht_init().
|
|
|
|
* NOTE: This function should only be called by ht_destroy(). */
|
2010-04-15 05:59:13 -04:00
|
|
|
static void _dictReset(dictht *ht)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
|
|
|
ht->table = NULL;
|
|
|
|
ht->size = 0;
|
|
|
|
ht->sizemask = 0;
|
|
|
|
ht->used = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a new hash table */
|
|
|
|
dict *dictCreate(dictType *type,
|
|
|
|
void *privDataPtr)
|
|
|
|
{
|
2010-07-24 17:10:42 -04:00
|
|
|
dict *d = zmalloc(sizeof(*d));
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-04-15 05:59:13 -04:00
|
|
|
_dictInit(d,type,privDataPtr);
|
|
|
|
return d;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize the hash table */
|
2010-04-15 05:59:13 -04:00
|
|
|
int _dictInit(dict *d, dictType *type,
|
2009-03-22 05:30:00 -04:00
|
|
|
void *privDataPtr)
|
|
|
|
{
|
2010-04-15 05:59:13 -04:00
|
|
|
_dictReset(&d->ht[0]);
|
|
|
|
_dictReset(&d->ht[1]);
|
|
|
|
d->type = type;
|
|
|
|
d->privdata = privDataPtr;
|
|
|
|
d->rehashidx = -1;
|
|
|
|
d->iterators = 0;
|
2009-03-22 05:30:00 -04:00
|
|
|
return DICT_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Resize the table to the minimal size that contains all the elements,
|
2012-03-15 02:27:14 -04:00
|
|
|
* but with the invariant of a USED/BUCKETS ratio near to <= 1 */
|
2010-04-15 05:59:13 -04:00
|
|
|
int dictResize(dict *d)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2010-04-15 05:59:13 -04:00
|
|
|
int minimal;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-04-15 05:59:13 -04:00
|
|
|
if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;
|
|
|
|
minimal = d->ht[0].used;
|
2009-03-22 05:30:00 -04:00
|
|
|
if (minimal < DICT_HT_INITIAL_SIZE)
|
|
|
|
minimal = DICT_HT_INITIAL_SIZE;
|
2010-04-15 05:59:13 -04:00
|
|
|
return dictExpand(d, minimal);
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2012-04-07 08:40:29 -04:00
|
|
|
/* Expand or create the hash table */
|
2010-04-15 05:59:13 -04:00
|
|
|
int dictExpand(dict *d, unsigned long size)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2012-04-07 08:40:29 -04:00
|
|
|
dictht n; /* the new hash table */
|
2010-04-15 05:59:13 -04:00
|
|
|
unsigned long realsize = _dictNextPower(size);
|
2009-03-22 05:30:00 -04:00
|
|
|
|
|
|
|
/* the size is invalid if it is smaller than the number of
|
2012-04-07 08:40:29 -04:00
|
|
|
* elements already inside the hash table */
|
2010-04-15 05:59:13 -04:00
|
|
|
if (dictIsRehashing(d) || d->ht[0].used > size)
|
2009-03-22 05:30:00 -04:00
|
|
|
return DICT_ERR;
|
|
|
|
|
2015-02-05 04:42:09 -05:00
|
|
|
/* Rehashing to the same table size is not useful. */
|
|
|
|
if (realsize == d->ht[0].size) return DICT_ERR;
|
|
|
|
|
2012-04-07 08:40:29 -04:00
|
|
|
/* Allocate the new hash table and initialize all pointers to NULL */
|
2009-03-22 05:30:00 -04:00
|
|
|
n.size = realsize;
|
|
|
|
n.sizemask = realsize-1;
|
2010-07-24 17:20:00 -04:00
|
|
|
n.table = zcalloc(realsize*sizeof(dictEntry*));
|
2010-04-15 05:59:13 -04:00
|
|
|
n.used = 0;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-04-15 05:59:13 -04:00
|
|
|
/* 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;
|
|
|
|
}
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-04-15 05:59:13 -04:00
|
|
|
/* 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.
|
2015-02-05 04:51:05 -05:00
|
|
|
*
|
2010-04-15 05:59:13 -04:00
|
|
|
* Note that a rehashing step consists in moving a bucket (that may have more
|
2015-02-05 04:51:05 -05:00
|
|
|
* than one key as we use chaining) from the old to the new hash table, however
|
|
|
|
* since part of the hash table may be composed of empty spaces, it is not
|
|
|
|
* guaranteed that this function will rehash even a single bucket, since it
|
|
|
|
* will visit at max N*10 empty buckets in total, otherwise the amount of
|
|
|
|
* work it does would be unbound and the function may block for a long time. */
|
2010-04-15 05:59:13 -04:00
|
|
|
int dictRehash(dict *d, int n) {
|
2015-02-05 04:51:05 -05:00
|
|
|
int empty_visits = n*10; /* Max number of empty buckets to visit. */
|
2010-04-15 05:59:13 -04:00
|
|
|
if (!dictIsRehashing(d)) return 0;
|
|
|
|
|
2015-02-06 04:48:13 -05:00
|
|
|
while(n-- && d->ht[0].used != 0) {
|
2010-04-15 05:59:13 -04:00
|
|
|
dictEntry *de, *nextde;
|
|
|
|
|
|
|
|
/* Note that rehashidx can't overflow as we are sure there are more
|
|
|
|
* elements because ht[0].used != 0 */
|
2014-08-26 04:32:37 -04:00
|
|
|
assert(d->ht[0].size > (unsigned long)d->rehashidx);
|
2015-02-05 04:51:05 -05:00
|
|
|
while(d->ht[0].table[d->rehashidx] == NULL) {
|
|
|
|
d->rehashidx++;
|
|
|
|
if (--empty_visits == 0) return 1;
|
|
|
|
}
|
2010-04-15 05:59:13 -04:00
|
|
|
de = d->ht[0].table[d->rehashidx];
|
|
|
|
/* Move all the keys in this bucket from the old to the new hash HT */
|
|
|
|
while(de) {
|
2009-03-22 05:30:00 -04:00
|
|
|
unsigned int h;
|
|
|
|
|
2010-04-15 05:59:13 -04:00
|
|
|
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;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
2010-04-15 05:59:13 -04:00
|
|
|
d->ht[0].table[d->rehashidx] = NULL;
|
|
|
|
d->rehashidx++;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
2015-02-06 04:48:13 -05:00
|
|
|
|
|
|
|
/* Check if we already rehashed the whole table... */
|
2015-02-05 22:18:58 -05:00
|
|
|
if (d->ht[0].used == 0) {
|
|
|
|
zfree(d->ht[0].table);
|
|
|
|
d->ht[0] = d->ht[1];
|
|
|
|
_dictReset(&d->ht[1]);
|
|
|
|
d->rehashidx = -1;
|
2015-02-06 04:48:13 -05:00
|
|
|
return 0;
|
2015-02-05 22:18:58 -05:00
|
|
|
}
|
2015-02-06 04:48:13 -05:00
|
|
|
|
|
|
|
/* More to rehash... */
|
2010-04-15 05:59:13 -04:00
|
|
|
return 1;
|
|
|
|
}
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-04-15 12:07:57 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-04-15 05:59:13 -04:00
|
|
|
/* This function performs just a step of rehashing, and only if there are
|
2011-05-10 04:15:50 -04:00
|
|
|
* 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.
|
2010-04-15 05:59:13 -04:00
|
|
|
*
|
|
|
|
* 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);
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add an element to the target hash table */
|
2010-04-15 05:59:13 -04:00
|
|
|
int dictAdd(dict *d, void *key, void *val)
|
2011-11-08 10:57:20 -05:00
|
|
|
{
|
|
|
|
dictEntry *entry = dictAddRaw(d,key);
|
|
|
|
|
|
|
|
if (!entry) return DICT_ERR;
|
2011-11-08 11:07:55 -05:00
|
|
|
dictSetVal(d, entry, val);
|
2011-11-08 10:57:20 -05:00
|
|
|
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.
|
|
|
|
*
|
2012-04-07 08:45:53 -04:00
|
|
|
* This function is also directly exposed to the user API to be called
|
2011-11-08 10:57:20 -05:00
|
|
|
* mainly in order to store non-pointers inside the hash value, example:
|
|
|
|
*
|
|
|
|
* entry = dictAddRaw(dict,mykey);
|
2011-11-08 13:41:29 -05:00
|
|
|
* if (entry != NULL) dictSetSignedIntegerVal(entry,1000);
|
2011-11-08 10:57:20 -05:00
|
|
|
*
|
|
|
|
* 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)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
|
|
|
int index;
|
|
|
|
dictEntry *entry;
|
2010-04-15 05:59:13 -04:00
|
|
|
dictht *ht;
|
|
|
|
|
|
|
|
if (dictIsRehashing(d)) _dictRehashStep(d);
|
2009-03-22 05:30:00 -04:00
|
|
|
|
|
|
|
/* Get the index of the new element, or -1 if
|
|
|
|
* the element already exists. */
|
2010-04-15 05:59:13 -04:00
|
|
|
if ((index = _dictKeyIndex(d, key)) == -1)
|
2011-11-08 10:57:20 -05:00
|
|
|
return NULL;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2015-01-23 12:11:05 -05:00
|
|
|
/* Allocate the memory and store the new entry.
|
|
|
|
* Insert the element in top, with the assumption that in a database
|
|
|
|
* system it is more likely that recently added entries are accessed
|
|
|
|
* more frequently. */
|
2010-04-15 05:59:13 -04:00
|
|
|
ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
|
2010-07-24 17:10:42 -04:00
|
|
|
entry = zmalloc(sizeof(*entry));
|
2009-03-22 05:30:00 -04:00
|
|
|
entry->next = ht->table[index];
|
|
|
|
ht->table[index] = entry;
|
2010-04-15 05:59:13 -04:00
|
|
|
ht->used++;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
|
|
|
/* Set the hash entry fields. */
|
2011-11-08 11:07:55 -05:00
|
|
|
dictSetKey(d, entry, key);
|
2011-11-08 10:57:20 -05:00
|
|
|
return entry;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2009-11-04 05:59:27 -05:00
|
|
|
/* 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. */
|
2010-04-15 05:59:13 -04:00
|
|
|
int dictReplace(dict *d, void *key, void *val)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2010-03-16 21:00:03 -04:00
|
|
|
dictEntry *entry, auxentry;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
|
|
|
/* Try to add the element. If the key
|
|
|
|
* does not exists dictAdd will suceed. */
|
2010-04-15 05:59:13 -04:00
|
|
|
if (dictAdd(d, key, val) == DICT_OK)
|
2009-11-04 05:59:27 -05:00
|
|
|
return 1;
|
2009-03-22 05:30:00 -04:00
|
|
|
/* It already exists, get the entry */
|
2010-04-15 05:59:13 -04:00
|
|
|
entry = dictFind(d, key);
|
2010-03-16 21:00:03 -04:00
|
|
|
/* 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;
|
2011-11-08 11:07:55 -05:00
|
|
|
dictSetVal(d, entry, val);
|
|
|
|
dictFreeVal(d, &auxentry);
|
2009-11-04 05:59:27 -05:00
|
|
|
return 0;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2011-11-08 10:57:20 -05:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
/* Search and remove an element */
|
2010-04-15 05:59:13 -04:00
|
|
|
static int dictGenericDelete(dict *d, const void *key, int nofree)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2010-04-15 05:59:13 -04:00
|
|
|
unsigned int h, idx;
|
2009-03-22 05:30:00 -04:00
|
|
|
dictEntry *he, *prevHe;
|
2010-04-15 05:59:13 -04:00
|
|
|
int table;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-04-15 05:59:13 -04:00
|
|
|
if (d->ht[0].size == 0) return DICT_ERR; /* d->ht[0].table is NULL */
|
|
|
|
if (dictIsRehashing(d)) _dictRehashStep(d);
|
|
|
|
h = dictHashKey(d, key);
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-04-15 05:59:13 -04:00
|
|
|
for (table = 0; table <= 1; table++) {
|
|
|
|
idx = h & d->ht[table].sizemask;
|
|
|
|
he = d->ht[table].table[idx];
|
|
|
|
prevHe = NULL;
|
|
|
|
while(he) {
|
2016-04-25 09:48:25 -04:00
|
|
|
if (key==he->key || dictCompareKeys(d, key, he->key)) {
|
2010-04-15 05:59:13 -04:00
|
|
|
/* Unlink the element from the list */
|
|
|
|
if (prevHe)
|
|
|
|
prevHe->next = he->next;
|
|
|
|
else
|
|
|
|
d->ht[table].table[idx] = he->next;
|
|
|
|
if (!nofree) {
|
2011-11-08 11:07:55 -05:00
|
|
|
dictFreeKey(d, he);
|
|
|
|
dictFreeVal(d, he);
|
2010-04-15 05:59:13 -04:00
|
|
|
}
|
2010-07-24 17:10:42 -04:00
|
|
|
zfree(he);
|
2010-04-15 05:59:13 -04:00
|
|
|
d->ht[table].used--;
|
|
|
|
return DICT_OK;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
2010-04-15 05:59:13 -04:00
|
|
|
prevHe = he;
|
|
|
|
he = he->next;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
2010-04-15 05:59:13 -04:00
|
|
|
if (!dictIsRehashing(d)) break;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2010-04-15 05:59:13 -04:00
|
|
|
/* Destroy an entire dictionary */
|
2013-12-10 12:18:24 -05:00
|
|
|
int _dictClear(dict *d, dictht *ht, void(callback)(void *)) {
|
2009-04-30 08:48:02 -04:00
|
|
|
unsigned long i;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
|
|
|
/* Free all the elements */
|
|
|
|
for (i = 0; i < ht->size && ht->used > 0; i++) {
|
|
|
|
dictEntry *he, *nextHe;
|
|
|
|
|
2013-12-10 12:18:24 -05:00
|
|
|
if (callback && (i & 65535) == 0) callback(d->privdata);
|
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
if ((he = ht->table[i]) == NULL) continue;
|
|
|
|
while(he) {
|
|
|
|
nextHe = he->next;
|
2011-11-08 11:07:55 -05:00
|
|
|
dictFreeKey(d, he);
|
|
|
|
dictFreeVal(d, he);
|
2010-07-24 17:10:42 -04:00
|
|
|
zfree(he);
|
2009-03-22 05:30:00 -04:00
|
|
|
ht->used--;
|
|
|
|
he = nextHe;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Free the table and the allocated cache structure */
|
2010-07-24 17:10:42 -04:00
|
|
|
zfree(ht->table);
|
2009-03-22 05:30:00 -04:00
|
|
|
/* Re-initialize the table */
|
|
|
|
_dictReset(ht);
|
|
|
|
return DICT_OK; /* never fails */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear & Release the hash table */
|
2010-04-15 05:59:13 -04:00
|
|
|
void dictRelease(dict *d)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2013-12-10 12:18:24 -05:00
|
|
|
_dictClear(d,&d->ht[0],NULL);
|
|
|
|
_dictClear(d,&d->ht[1],NULL);
|
2010-07-24 17:10:42 -04:00
|
|
|
zfree(d);
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2010-04-15 05:59:13 -04:00
|
|
|
dictEntry *dictFind(dict *d, const void *key)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
|
|
|
dictEntry *he;
|
2010-04-15 05:59:13 -04:00
|
|
|
unsigned int h, idx, table;
|
|
|
|
|
2016-04-25 09:48:25 -04:00
|
|
|
if (d->ht[0].used + d->ht[1].used == 0) return NULL; /* dict is empty */
|
2010-04-15 05:59:13 -04:00
|
|
|
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) {
|
2016-04-25 09:48:25 -04:00
|
|
|
if (key==he->key || dictCompareKeys(d, key, he->key))
|
2010-04-15 05:59:13 -04:00
|
|
|
return he;
|
|
|
|
he = he->next;
|
|
|
|
}
|
|
|
|
if (!dictIsRehashing(d)) return NULL;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-04-16 04:04:51 -04:00
|
|
|
void *dictFetchValue(dict *d, const void *key) {
|
|
|
|
dictEntry *he;
|
|
|
|
|
|
|
|
he = dictFind(d,key);
|
2011-11-08 11:07:55 -05:00
|
|
|
return he ? dictGetVal(he) : NULL;
|
2010-04-16 04:04:51 -04:00
|
|
|
}
|
|
|
|
|
dict.c iterator API misuse protection.
dict.c allows the user to create unsafe iterators, that are iterators
that will not touch the dictionary data structure in any way, preventing
copy on write, but at the same time are limited in their usage.
The limitation is that when itearting with an unsafe iterator, no call
to other dictionary functions must be done inside the iteration loop,
otherwise the dictionary may be incrementally rehashed resulting into
missing elements in the set of the elements returned by the iterator.
However after introducing this kind of iterators a number of bugs were
found due to misuses of the API, and we are still finding
bugs about this issue. The bugs are not trivial to track because the
effect is just missing elements during the iteartion.
This commit introduces auto-detection of the API misuse. The idea is
that an unsafe iterator has a contract: from initialization to the
release of the iterator the dictionary should not change.
So we take a fingerprint of the dictionary state, xoring a few important
dict properties when the unsafe iteartor is initialized. We later check
when the iterator is released if the fingerprint is still the same. If it
is not, we found a misuse of the iterator, as not allowed API calls
changed the internal state of the dictionary.
This code was checked against a real bug, issue #1240.
This is what Redis prints (aborting) when a misuse is detected:
Assertion failed: (iter->fingerprint == dictFingerprint(iter->d)),
function dictReleaseIterator, file dict.c, line 587.
2013-08-16 08:08:04 -04:00
|
|
|
/* A fingerprint is a 64 bit number that represents the state of the dictionary
|
|
|
|
* at a given time, it's just a few dict properties xored together.
|
|
|
|
* When an unsafe iterator is initialized, we get the dict fingerprint, and check
|
|
|
|
* the fingerprint again when the iterator is released.
|
|
|
|
* If the two fingerprints are different it means that the user of the iterator
|
|
|
|
* performed forbidden operations against the dictionary while iterating. */
|
|
|
|
long long dictFingerprint(dict *d) {
|
2013-08-19 05:29:18 -04:00
|
|
|
long long integers[6], hash = 0;
|
|
|
|
int j;
|
|
|
|
|
2013-08-20 05:49:55 -04:00
|
|
|
integers[0] = (long) d->ht[0].table;
|
2013-08-19 05:29:18 -04:00
|
|
|
integers[1] = d->ht[0].size;
|
|
|
|
integers[2] = d->ht[0].used;
|
2013-08-20 05:49:55 -04:00
|
|
|
integers[3] = (long) d->ht[1].table;
|
2013-08-19 05:29:18 -04:00
|
|
|
integers[4] = d->ht[1].size;
|
|
|
|
integers[5] = d->ht[1].used;
|
|
|
|
|
|
|
|
/* We hash N integers by summing every successive integer with the integer
|
|
|
|
* hashing of the previous sum. Basically:
|
|
|
|
*
|
|
|
|
* Result = hash(hash(hash(int1)+int2)+int3) ...
|
|
|
|
*
|
|
|
|
* This way the same set of integers in a different order will (likely) hash
|
|
|
|
* to a different number. */
|
|
|
|
for (j = 0; j < 6; j++) {
|
|
|
|
hash += integers[j];
|
|
|
|
/* For the hashing step we use Tomas Wang's 64 bit integer hash. */
|
|
|
|
hash = (~hash) + (hash << 21); // hash = (hash << 21) - hash - 1;
|
|
|
|
hash = hash ^ (hash >> 24);
|
2013-08-19 11:25:48 -04:00
|
|
|
hash = (hash + (hash << 3)) + (hash << 8); // hash * 265
|
2013-08-19 05:29:18 -04:00
|
|
|
hash = hash ^ (hash >> 14);
|
|
|
|
hash = (hash + (hash << 2)) + (hash << 4); // hash * 21
|
|
|
|
hash = hash ^ (hash >> 28);
|
|
|
|
hash = hash + (hash << 31);
|
|
|
|
}
|
|
|
|
return hash;
|
dict.c iterator API misuse protection.
dict.c allows the user to create unsafe iterators, that are iterators
that will not touch the dictionary data structure in any way, preventing
copy on write, but at the same time are limited in their usage.
The limitation is that when itearting with an unsafe iterator, no call
to other dictionary functions must be done inside the iteration loop,
otherwise the dictionary may be incrementally rehashed resulting into
missing elements in the set of the elements returned by the iterator.
However after introducing this kind of iterators a number of bugs were
found due to misuses of the API, and we are still finding
bugs about this issue. The bugs are not trivial to track because the
effect is just missing elements during the iteartion.
This commit introduces auto-detection of the API misuse. The idea is
that an unsafe iterator has a contract: from initialization to the
release of the iterator the dictionary should not change.
So we take a fingerprint of the dictionary state, xoring a few important
dict properties when the unsafe iteartor is initialized. We later check
when the iterator is released if the fingerprint is still the same. If it
is not, we found a misuse of the iterator, as not allowed API calls
changed the internal state of the dictionary.
This code was checked against a real bug, issue #1240.
This is what Redis prints (aborting) when a misuse is detected:
Assertion failed: (iter->fingerprint == dictFingerprint(iter->d)),
function dictReleaseIterator, file dict.c, line 587.
2013-08-16 08:08:04 -04:00
|
|
|
}
|
|
|
|
|
2010-04-15 05:59:13 -04:00
|
|
|
dictIterator *dictGetIterator(dict *d)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2010-07-24 17:10:42 -04:00
|
|
|
dictIterator *iter = zmalloc(sizeof(*iter));
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-04-15 05:59:13 -04:00
|
|
|
iter->d = d;
|
|
|
|
iter->table = 0;
|
2009-03-22 05:30:00 -04:00
|
|
|
iter->index = -1;
|
2011-05-10 04:15:50 -04:00
|
|
|
iter->safe = 0;
|
2009-03-22 05:30:00 -04:00
|
|
|
iter->entry = NULL;
|
|
|
|
iter->nextEntry = NULL;
|
|
|
|
return iter;
|
|
|
|
}
|
|
|
|
|
2011-05-10 04:15:50 -04:00
|
|
|
dictIterator *dictGetSafeIterator(dict *d) {
|
|
|
|
dictIterator *i = dictGetIterator(d);
|
|
|
|
|
|
|
|
i->safe = 1;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
dictEntry *dictNext(dictIterator *iter)
|
|
|
|
{
|
|
|
|
while (1) {
|
|
|
|
if (iter->entry == NULL) {
|
2010-04-15 05:59:13 -04:00
|
|
|
dictht *ht = &iter->d->ht[iter->table];
|
dict.c iterator API misuse protection.
dict.c allows the user to create unsafe iterators, that are iterators
that will not touch the dictionary data structure in any way, preventing
copy on write, but at the same time are limited in their usage.
The limitation is that when itearting with an unsafe iterator, no call
to other dictionary functions must be done inside the iteration loop,
otherwise the dictionary may be incrementally rehashed resulting into
missing elements in the set of the elements returned by the iterator.
However after introducing this kind of iterators a number of bugs were
found due to misuses of the API, and we are still finding
bugs about this issue. The bugs are not trivial to track because the
effect is just missing elements during the iteartion.
This commit introduces auto-detection of the API misuse. The idea is
that an unsafe iterator has a contract: from initialization to the
release of the iterator the dictionary should not change.
So we take a fingerprint of the dictionary state, xoring a few important
dict properties when the unsafe iteartor is initialized. We later check
when the iterator is released if the fingerprint is still the same. If it
is not, we found a misuse of the iterator, as not allowed API calls
changed the internal state of the dictionary.
This code was checked against a real bug, issue #1240.
This is what Redis prints (aborting) when a misuse is detected:
Assertion failed: (iter->fingerprint == dictFingerprint(iter->d)),
function dictReleaseIterator, file dict.c, line 587.
2013-08-16 08:08:04 -04:00
|
|
|
if (iter->index == -1 && iter->table == 0) {
|
|
|
|
if (iter->safe)
|
|
|
|
iter->d->iterators++;
|
|
|
|
else
|
|
|
|
iter->fingerprint = dictFingerprint(iter->d);
|
|
|
|
}
|
2009-03-22 05:30:00 -04:00
|
|
|
iter->index++;
|
2014-08-26 04:26:36 -04:00
|
|
|
if (iter->index >= (long) ht->size) {
|
2010-04-15 05:59:13 -04:00
|
|
|
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];
|
2009-03-22 05:30:00 -04:00
|
|
|
} 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)
|
|
|
|
{
|
dict.c iterator API misuse protection.
dict.c allows the user to create unsafe iterators, that are iterators
that will not touch the dictionary data structure in any way, preventing
copy on write, but at the same time are limited in their usage.
The limitation is that when itearting with an unsafe iterator, no call
to other dictionary functions must be done inside the iteration loop,
otherwise the dictionary may be incrementally rehashed resulting into
missing elements in the set of the elements returned by the iterator.
However after introducing this kind of iterators a number of bugs were
found due to misuses of the API, and we are still finding
bugs about this issue. The bugs are not trivial to track because the
effect is just missing elements during the iteartion.
This commit introduces auto-detection of the API misuse. The idea is
that an unsafe iterator has a contract: from initialization to the
release of the iterator the dictionary should not change.
So we take a fingerprint of the dictionary state, xoring a few important
dict properties when the unsafe iteartor is initialized. We later check
when the iterator is released if the fingerprint is still the same. If it
is not, we found a misuse of the iterator, as not allowed API calls
changed the internal state of the dictionary.
This code was checked against a real bug, issue #1240.
This is what Redis prints (aborting) when a misuse is detected:
Assertion failed: (iter->fingerprint == dictFingerprint(iter->d)),
function dictReleaseIterator, file dict.c, line 587.
2013-08-16 08:08:04 -04:00
|
|
|
if (!(iter->index == -1 && iter->table == 0)) {
|
|
|
|
if (iter->safe)
|
|
|
|
iter->d->iterators--;
|
|
|
|
else
|
|
|
|
assert(iter->fingerprint == dictFingerprint(iter->d));
|
|
|
|
}
|
2010-07-24 17:10:42 -04:00
|
|
|
zfree(iter);
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return a random entry from the hash table. Useful to
|
|
|
|
* implement randomized algorithms */
|
2010-04-15 05:59:13 -04:00
|
|
|
dictEntry *dictGetRandomKey(dict *d)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2010-04-15 05:59:13 -04:00
|
|
|
dictEntry *he, *orighe;
|
2009-03-22 05:30:00 -04:00
|
|
|
unsigned int h;
|
|
|
|
int listlen, listele;
|
|
|
|
|
2010-04-15 05:59:13 -04:00
|
|
|
if (dictSize(d) == 0) return NULL;
|
|
|
|
if (dictIsRehashing(d)) _dictRehashStep(d);
|
|
|
|
if (dictIsRehashing(d)) {
|
|
|
|
do {
|
2015-02-05 06:15:58 -05:00
|
|
|
/* We are sure there are no elements in indexes from 0
|
|
|
|
* to rehashidx-1 */
|
|
|
|
h = d->rehashidx + (random() % (d->ht[0].size +
|
|
|
|
d->ht[1].size -
|
|
|
|
d->rehashidx));
|
2010-04-15 05:59:13 -04:00
|
|
|
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);
|
|
|
|
}
|
2009-03-22 05:30:00 -04:00
|
|
|
|
|
|
|
/* Now we found a non empty bucket, but it is a linked
|
|
|
|
* list and we need to get a random element from the list.
|
2010-04-15 05:59:13 -04:00
|
|
|
* The only sane way to do so is counting the elements and
|
2009-03-22 05:30:00 -04:00
|
|
|
* select a random index. */
|
|
|
|
listlen = 0;
|
2010-04-15 05:59:13 -04:00
|
|
|
orighe = he;
|
2009-03-22 05:30:00 -04:00
|
|
|
while(he) {
|
|
|
|
he = he->next;
|
|
|
|
listlen++;
|
|
|
|
}
|
|
|
|
listele = random() % listlen;
|
2010-04-15 05:59:13 -04:00
|
|
|
he = orighe;
|
2009-03-22 05:30:00 -04:00
|
|
|
while(listele--) he = he->next;
|
|
|
|
return he;
|
|
|
|
}
|
|
|
|
|
2015-02-06 09:48:42 -05:00
|
|
|
/* This function samples the dictionary to return a few keys from random
|
|
|
|
* locations.
|
|
|
|
*
|
|
|
|
* It does not guarantee to return all the keys specified in 'count', nor
|
|
|
|
* it does guarantee to return non-duplicated elements, however it will make
|
|
|
|
* some effort to do both things.
|
|
|
|
*
|
|
|
|
* Returned pointers to hash table entries are stored into 'des' that
|
|
|
|
* points to an array of dictEntry pointers. The array must have room for
|
|
|
|
* at least 'count' elements, that is the argument we pass to the function
|
|
|
|
* to tell how many random elements we need.
|
|
|
|
*
|
|
|
|
* The function returns the number of items stored into 'des', that may
|
|
|
|
* be less than 'count' if the hash table has less than 'count' elements
|
|
|
|
* inside, or if not enough elements were found in a reasonable amount of
|
|
|
|
* steps.
|
|
|
|
*
|
|
|
|
* Note that this function is not suitable when you need a good distribution
|
|
|
|
* of the returned items, but only when you need to "sample" a given number
|
|
|
|
* of continuous elements to run some kind of algorithm or to produce
|
|
|
|
* statistics. However the function is much faster than dictGetRandomKey()
|
|
|
|
* at producing N elements. */
|
|
|
|
unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) {
|
2015-03-27 05:14:52 -04:00
|
|
|
unsigned long j; /* internal hash table id, 0 or 1. */
|
|
|
|
unsigned long tables; /* 1 or 2 tables? */
|
|
|
|
unsigned long stored = 0, maxsizemask;
|
|
|
|
unsigned long maxsteps;
|
2015-02-06 09:48:42 -05:00
|
|
|
|
|
|
|
if (dictSize(d) < count) count = dictSize(d);
|
|
|
|
maxsteps = count*10;
|
|
|
|
|
|
|
|
/* Try to do a rehashing work proportional to 'count'. */
|
|
|
|
for (j = 0; j < count; j++) {
|
|
|
|
if (dictIsRehashing(d))
|
|
|
|
_dictRehashStep(d);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
tables = dictIsRehashing(d) ? 2 : 1;
|
|
|
|
maxsizemask = d->ht[0].sizemask;
|
|
|
|
if (tables > 1 && maxsizemask < d->ht[1].sizemask)
|
|
|
|
maxsizemask = d->ht[1].sizemask;
|
|
|
|
|
|
|
|
/* Pick a random point inside the larger table. */
|
2015-03-27 05:14:52 -04:00
|
|
|
unsigned long i = random() & maxsizemask;
|
|
|
|
unsigned long emptylen = 0; /* Continuous empty entries so far. */
|
2015-02-06 09:48:42 -05:00
|
|
|
while(stored < count && maxsteps--) {
|
|
|
|
for (j = 0; j < tables; j++) {
|
|
|
|
/* Invariant of the dict.c rehashing: up to the indexes already
|
|
|
|
* visited in ht[0] during the rehashing, there are no populated
|
|
|
|
* buckets, so we can skip ht[0] for indexes between 0 and idx-1. */
|
2015-03-27 05:14:52 -04:00
|
|
|
if (tables == 2 && j == 0 && i < (unsigned long) d->rehashidx) {
|
2015-02-06 09:48:42 -05:00
|
|
|
/* Moreover, if we are currently out of range in the second
|
|
|
|
* table, there will be no elements in both tables up to
|
|
|
|
* the current rehashing index, so we jump if possible.
|
|
|
|
* (this happens when going from big to small table). */
|
|
|
|
if (i >= d->ht[1].size) i = d->rehashidx;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (i >= d->ht[j].size) continue; /* Out of range for this table. */
|
|
|
|
dictEntry *he = d->ht[j].table[i];
|
|
|
|
|
|
|
|
/* Count contiguous empty buckets, and jump to other
|
|
|
|
* locations if they reach 'count' (with a minimum of 5). */
|
|
|
|
if (he == NULL) {
|
|
|
|
emptylen++;
|
|
|
|
if (emptylen >= 5 && emptylen > count) {
|
|
|
|
i = random() & maxsizemask;
|
|
|
|
emptylen = 0;
|
|
|
|
}
|
|
|
|
} else {
|
2015-02-08 05:19:47 -05:00
|
|
|
emptylen = 0;
|
2015-02-06 09:48:42 -05:00
|
|
|
while (he) {
|
|
|
|
/* Collect all the elements of the buckets found non
|
|
|
|
* empty while iterating. */
|
|
|
|
*des = he;
|
|
|
|
des++;
|
|
|
|
he = he->next;
|
|
|
|
stored++;
|
|
|
|
if (stored == count) return stored;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i = (i+1) & maxsizemask;
|
|
|
|
}
|
|
|
|
return stored;
|
|
|
|
}
|
|
|
|
|
2012-07-09 04:00:26 -04:00
|
|
|
/* Function to reverse bits. Algorithm from:
|
|
|
|
* http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel */
|
|
|
|
static unsigned long rev(unsigned long v) {
|
|
|
|
unsigned long s = 8 * sizeof(v); // bit size; must be power of 2
|
|
|
|
unsigned long mask = ~0;
|
|
|
|
while ((s >>= 1) > 0) {
|
|
|
|
mask ^= (mask << s);
|
|
|
|
v = ((v >> s) & mask) | ((v << s) & ~mask);
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2013-10-25 11:01:30 -04:00
|
|
|
/* dictScan() is used to iterate over the elements of a dictionary.
|
|
|
|
*
|
2014-08-11 13:28:07 -04:00
|
|
|
* Iterating works the following way:
|
2013-10-25 11:01:30 -04:00
|
|
|
*
|
|
|
|
* 1) Initially you call the function using a cursor (v) value of 0.
|
|
|
|
* 2) The function performs one step of the iteration, and returns the
|
2014-08-11 13:28:07 -04:00
|
|
|
* new cursor value you must use in the next call.
|
2013-10-25 11:01:30 -04:00
|
|
|
* 3) When the returned cursor is 0, the iteration is complete.
|
|
|
|
*
|
2014-08-11 13:28:07 -04:00
|
|
|
* The function guarantees all elements present in the
|
|
|
|
* dictionary get returned between the start and end of the iteration.
|
|
|
|
* However it is possible some elements get returned multiple times.
|
2013-10-25 11:01:30 -04:00
|
|
|
*
|
2014-08-11 13:28:07 -04:00
|
|
|
* For every element returned, the callback argument 'fn' is
|
|
|
|
* called with 'privdata' as first argument and the dictionary entry
|
2013-10-25 11:01:30 -04:00
|
|
|
* 'de' as second argument.
|
|
|
|
*
|
|
|
|
* HOW IT WORKS.
|
|
|
|
*
|
2014-08-11 13:28:07 -04:00
|
|
|
* The iteration algorithm was designed by Pieter Noordhuis.
|
2013-10-25 11:01:30 -04:00
|
|
|
* The main idea is to increment a cursor starting from the higher order
|
2014-08-11 13:28:07 -04:00
|
|
|
* bits. That is, instead of incrementing the cursor normally, the bits
|
2013-10-25 11:01:30 -04:00
|
|
|
* of the cursor are reversed, then the cursor is incremented, and finally
|
|
|
|
* the bits are reversed again.
|
|
|
|
*
|
2014-08-11 13:28:07 -04:00
|
|
|
* This strategy is needed because the hash table may be resized between
|
|
|
|
* iteration calls.
|
2013-10-25 11:01:30 -04:00
|
|
|
*
|
|
|
|
* dict.c hash tables are always power of two in size, and they
|
|
|
|
* use chaining, so the position of an element in a given table is given
|
2014-08-11 13:28:07 -04:00
|
|
|
* by computing the bitwise AND between Hash(key) and SIZE-1
|
2013-10-25 11:01:30 -04:00
|
|
|
* (where SIZE-1 is always the mask that is equivalent to taking the rest
|
|
|
|
* of the division between the Hash of the key and SIZE).
|
|
|
|
*
|
2013-12-22 23:32:57 -05:00
|
|
|
* For example if the current hash table size is 16, the mask is
|
2014-08-11 13:28:07 -04:00
|
|
|
* (in binary) 1111. The position of a key in the hash table will always be
|
2013-10-25 11:01:30 -04:00
|
|
|
* the last four bits of the hash output, and so forth.
|
|
|
|
*
|
2013-10-25 11:05:55 -04:00
|
|
|
* WHAT HAPPENS IF THE TABLE CHANGES IN SIZE?
|
2013-10-25 11:01:30 -04:00
|
|
|
*
|
2014-08-11 13:28:07 -04:00
|
|
|
* If the hash table grows, elements can go anywhere in one multiple of
|
|
|
|
* the old bucket: for example let's say we already iterated with
|
|
|
|
* a 4 bit cursor 1100 (the mask is 1111 because hash table size = 16).
|
2013-10-25 11:01:30 -04:00
|
|
|
*
|
2014-08-11 13:28:07 -04:00
|
|
|
* If the hash table will be resized to 64 elements, then the new mask will
|
|
|
|
* be 111111. The new buckets you obtain by substituting in ??1100
|
|
|
|
* with either 0 or 1 can be targeted only by keys we already visited
|
2013-10-25 11:01:30 -04:00
|
|
|
* when scanning the bucket 1100 in the smaller hash table.
|
|
|
|
*
|
|
|
|
* By iterating the higher bits first, because of the inverted counter, the
|
2014-08-11 13:28:07 -04:00
|
|
|
* cursor does not need to restart if the table size gets bigger. It will
|
|
|
|
* continue iterating using cursors without '1100' at the end, and also
|
|
|
|
* without any other combination of the final 4 bits already explored.
|
2013-10-25 11:01:30 -04:00
|
|
|
*
|
|
|
|
* Similarly when the table size shrinks over time, for example going from
|
2014-08-11 13:28:07 -04:00
|
|
|
* 16 to 8, if a combination of the lower three bits (the mask for size 8
|
|
|
|
* is 111) were already completely explored, it would not be visited again
|
|
|
|
* because we are sure we tried, for example, both 0111 and 1111 (all the
|
2013-10-25 11:01:30 -04:00
|
|
|
* variations of the higher bit) so we don't need to test it again.
|
|
|
|
*
|
2013-10-25 11:05:55 -04:00
|
|
|
* WAIT... YOU HAVE *TWO* TABLES DURING REHASHING!
|
2013-10-25 11:01:30 -04:00
|
|
|
*
|
2014-08-11 13:28:07 -04:00
|
|
|
* Yes, this is true, but we always iterate the smaller table first, then
|
|
|
|
* we test all the expansions of the current cursor into the larger
|
|
|
|
* table. For example if the current cursor is 101 and we also have a
|
2013-10-25 11:01:30 -04:00
|
|
|
* larger table of size 16, we also test (0)101 and (1)101 inside the larger
|
|
|
|
* table. This reduces the problem back to having only one table, where
|
2014-08-11 13:28:07 -04:00
|
|
|
* the larger one, if it exists, is just an expansion of the smaller one.
|
2013-10-25 11:01:30 -04:00
|
|
|
*
|
|
|
|
* LIMITATIONS
|
|
|
|
*
|
|
|
|
* This iterator is completely stateless, and this is a huge advantage,
|
|
|
|
* including no additional memory used.
|
|
|
|
*
|
|
|
|
* The disadvantages resulting from this design are:
|
|
|
|
*
|
2014-08-11 13:28:07 -04:00
|
|
|
* 1) It is possible we return elements more than once. However this is usually
|
2013-10-25 11:01:30 -04:00
|
|
|
* easy to deal with in the application level.
|
|
|
|
* 2) The iterator must return multiple elements per call, as it needs to always
|
|
|
|
* return all the keys chained in a given bucket, and all the expansions, so
|
2014-08-11 13:28:07 -04:00
|
|
|
* we are sure we don't miss keys moving during rehashing.
|
2013-10-25 11:01:30 -04:00
|
|
|
* 3) The reverse cursor is somewhat hard to understand at first, but this
|
|
|
|
* comment is supposed to help.
|
|
|
|
*/
|
2012-07-09 04:00:26 -04:00
|
|
|
unsigned long dictScan(dict *d,
|
|
|
|
unsigned long v,
|
|
|
|
dictScanFunction *fn,
|
|
|
|
void *privdata)
|
|
|
|
{
|
|
|
|
dictht *t0, *t1;
|
2015-07-30 05:46:31 -04:00
|
|
|
const dictEntry *de, *next;
|
2012-07-09 04:00:26 -04:00
|
|
|
unsigned long m0, m1;
|
|
|
|
|
2013-10-28 06:17:18 -04:00
|
|
|
if (dictSize(d) == 0) return 0;
|
|
|
|
|
2012-07-09 04:00:26 -04:00
|
|
|
if (!dictIsRehashing(d)) {
|
|
|
|
t0 = &(d->ht[0]);
|
|
|
|
m0 = t0->sizemask;
|
|
|
|
|
|
|
|
/* Emit entries at cursor */
|
|
|
|
de = t0->table[v & m0];
|
|
|
|
while (de) {
|
2015-07-30 05:46:31 -04:00
|
|
|
next = de->next;
|
2012-07-09 04:00:26 -04:00
|
|
|
fn(privdata, de);
|
2015-07-30 05:46:31 -04:00
|
|
|
de = next;
|
2012-07-09 04:00:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
t0 = &d->ht[0];
|
|
|
|
t1 = &d->ht[1];
|
|
|
|
|
|
|
|
/* Make sure t0 is the smaller and t1 is the bigger table */
|
|
|
|
if (t0->size > t1->size) {
|
|
|
|
t0 = &d->ht[1];
|
|
|
|
t1 = &d->ht[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
m0 = t0->sizemask;
|
|
|
|
m1 = t1->sizemask;
|
|
|
|
|
|
|
|
/* Emit entries at cursor */
|
|
|
|
de = t0->table[v & m0];
|
|
|
|
while (de) {
|
2015-07-30 05:46:31 -04:00
|
|
|
next = de->next;
|
2012-07-09 04:00:26 -04:00
|
|
|
fn(privdata, de);
|
2015-07-30 05:46:31 -04:00
|
|
|
de = next;
|
2012-07-09 04:00:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Iterate over indices in larger table that are the expansion
|
|
|
|
* of the index pointed to by the cursor in the smaller table */
|
|
|
|
do {
|
|
|
|
/* Emit entries at cursor */
|
|
|
|
de = t1->table[v & m1];
|
|
|
|
while (de) {
|
2015-07-30 05:46:31 -04:00
|
|
|
next = de->next;
|
2012-07-09 04:00:26 -04:00
|
|
|
fn(privdata, de);
|
2015-07-30 05:46:31 -04:00
|
|
|
de = next;
|
2012-07-09 04:00:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Increment bits not covered by the smaller mask */
|
|
|
|
v = (((v | m0) + 1) & ~m0) | (v & m0);
|
|
|
|
|
|
|
|
/* Continue while bits covered by mask difference is non-zero */
|
|
|
|
} while (v & (m0 ^ m1));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set unmasked bits so incrementing the reversed cursor
|
|
|
|
* operates on the masked bits of the smaller table */
|
|
|
|
v |= ~m0;
|
|
|
|
|
|
|
|
/* Increment the reverse cursor */
|
|
|
|
v = rev(v);
|
|
|
|
v++;
|
|
|
|
v = rev(v);
|
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
/* ------------------------- private functions ------------------------------ */
|
|
|
|
|
|
|
|
/* Expand the hash table if needed */
|
2010-04-15 05:59:13 -04:00
|
|
|
static int _dictExpandIfNeeded(dict *d)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2010-09-15 08:09:41 -04:00
|
|
|
/* Incremental rehashing already in progress. Return. */
|
2010-04-15 05:59:13 -04:00
|
|
|
if (dictIsRehashing(d)) return DICT_OK;
|
2010-09-15 08:09:41 -04:00
|
|
|
|
2013-01-16 12:00:20 -05:00
|
|
|
/* If the hash table is empty expand it to the initial size. */
|
2010-09-15 08:09:41 -04:00
|
|
|
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))
|
|
|
|
{
|
2012-11-25 18:36:39 -05:00
|
|
|
return dictExpand(d, d->ht[0].used*2);
|
2010-09-15 08:09:41 -04:00
|
|
|
}
|
2009-03-22 05:30:00 -04:00
|
|
|
return DICT_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Our hash table capability is a power of two */
|
2009-04-30 08:48:02 -04:00
|
|
|
static unsigned long _dictNextPower(unsigned long size)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2009-04-30 08:48:02 -04:00
|
|
|
unsigned long i = DICT_HT_INITIAL_SIZE;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2009-04-30 08:48:02 -04:00
|
|
|
if (size >= LONG_MAX) return LONG_MAX;
|
2009-03-22 05:30:00 -04:00
|
|
|
while(1) {
|
|
|
|
if (i >= size)
|
|
|
|
return i;
|
|
|
|
i *= 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns the index of a free slot that can be populated with
|
2013-12-05 10:35:32 -05:00
|
|
|
* a hash entry for the given 'key'.
|
2010-04-15 05:59:13 -04:00
|
|
|
* 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)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2010-04-15 12:07:57 -04:00
|
|
|
unsigned int h, idx, table;
|
2009-03-22 05:30:00 -04:00
|
|
|
dictEntry *he;
|
|
|
|
|
2012-04-07 08:40:29 -04:00
|
|
|
/* Expand the hash table if needed */
|
2010-04-15 05:59:13 -04:00
|
|
|
if (_dictExpandIfNeeded(d) == DICT_ERR)
|
2009-03-22 05:30:00 -04:00
|
|
|
return -1;
|
|
|
|
/* Compute the key hash value */
|
2010-04-15 05:59:13 -04:00
|
|
|
h = dictHashKey(d, key);
|
2010-04-15 12:07:57 -04:00
|
|
|
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) {
|
2016-04-25 09:48:25 -04:00
|
|
|
if (key==he->key || dictCompareKeys(d, key, he->key))
|
2010-04-15 12:07:57 -04:00
|
|
|
return -1;
|
|
|
|
he = he->next;
|
|
|
|
}
|
|
|
|
if (!dictIsRehashing(d)) break;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
2010-04-15 12:07:57 -04:00
|
|
|
return idx;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2013-12-10 12:18:24 -05:00
|
|
|
void dictEmpty(dict *d, void(callback)(void*)) {
|
|
|
|
_dictClear(d,&d->ht[0],callback);
|
|
|
|
_dictClear(d,&d->ht[1],callback);
|
2010-04-15 05:59:13 -04:00
|
|
|
d->rehashidx = -1;
|
|
|
|
d->iterators = 0;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2012-04-18 17:56:07 -04:00
|
|
|
void dictEnableResize(void) {
|
|
|
|
dict_can_resize = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dictDisableResize(void) {
|
|
|
|
dict_can_resize = 0;
|
|
|
|
}
|
|
|
|
|
2015-07-14 11:15:37 -04:00
|
|
|
/* ------------------------------- Debugging ---------------------------------*/
|
2012-04-18 17:56:07 -04:00
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
#define DICT_STATS_VECTLEN 50
|
2015-07-14 11:15:37 -04:00
|
|
|
size_t _dictGetStatsHt(char *buf, size_t bufsize, dictht *ht, int tableid) {
|
2009-04-30 08:48:02 -04:00
|
|
|
unsigned long i, slots = 0, chainlen, maxchainlen = 0;
|
|
|
|
unsigned long totchainlen = 0;
|
|
|
|
unsigned long clvector[DICT_STATS_VECTLEN];
|
2015-07-14 11:15:37 -04:00
|
|
|
size_t l = 0;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
|
|
|
if (ht->used == 0) {
|
2015-07-14 11:15:37 -04:00
|
|
|
return snprintf(buf,bufsize,
|
|
|
|
"No stats available for empty dictionaries\n");
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2015-07-14 11:15:37 -04:00
|
|
|
/* Compute stats. */
|
2009-03-22 05:30:00 -04:00
|
|
|
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;
|
|
|
|
}
|
2015-07-14 11:15:37 -04:00
|
|
|
|
|
|
|
/* Generate human readable stats. */
|
|
|
|
l += snprintf(buf+l,bufsize-l,
|
|
|
|
"Hash table %d stats (%s):\n"
|
|
|
|
" table size: %ld\n"
|
|
|
|
" number of elements: %ld\n"
|
|
|
|
" different slots: %ld\n"
|
|
|
|
" max chain length: %ld\n"
|
|
|
|
" avg chain length (counted): %.02f\n"
|
|
|
|
" avg chain length (computed): %.02f\n"
|
|
|
|
" Chain length distribution:\n",
|
|
|
|
tableid, (tableid == 0) ? "main hash table" : "rehashing target",
|
|
|
|
ht->size, ht->used, slots, maxchainlen,
|
|
|
|
(float)totchainlen/slots, (float)ht->used/slots);
|
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
for (i = 0; i < DICT_STATS_VECTLEN-1; i++) {
|
|
|
|
if (clvector[i] == 0) continue;
|
2015-07-14 11:15:37 -04:00
|
|
|
if (l >= bufsize) break;
|
|
|
|
l += snprintf(buf+l,bufsize-l,
|
|
|
|
" %s%ld: %ld (%.02f%%)\n",
|
|
|
|
(i == DICT_STATS_VECTLEN-1)?">= ":"",
|
|
|
|
i, clvector[i], ((float)clvector[i]/ht->size)*100);
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2015-07-14 11:15:37 -04:00
|
|
|
/* Unlike snprintf(), teturn the number of characters actually written. */
|
|
|
|
if (bufsize) buf[bufsize-1] = '\0';
|
|
|
|
return strlen(buf);
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2015-07-14 11:15:37 -04:00
|
|
|
void dictGetStats(char *buf, size_t bufsize, dict *d) {
|
|
|
|
size_t l;
|
|
|
|
char *orig_buf = buf;
|
|
|
|
size_t orig_bufsize = bufsize;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2015-07-14 11:15:37 -04:00
|
|
|
l = _dictGetStatsHt(buf,bufsize,&d->ht[0],0);
|
|
|
|
buf += l;
|
|
|
|
bufsize -= l;
|
|
|
|
if (dictIsRehashing(d) && bufsize > 0) {
|
|
|
|
_dictGetStatsHt(buf,bufsize,&d->ht[1],1);
|
|
|
|
}
|
|
|
|
/* Make sure there is a NULL term at the end. */
|
|
|
|
if (orig_bufsize) orig_buf[orig_bufsize-1] = '\0';
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
2016-09-07 04:32:57 -04:00
|
|
|
|
|
|
|
/* ------------------------------- Benchmark ---------------------------------*/
|
|
|
|
|
|
|
|
#ifdef DICT_BENCHMARK_MAIN
|
|
|
|
|
|
|
|
#include "sds.h"
|
|
|
|
|
|
|
|
unsigned int hashCallback(const void *key) {
|
|
|
|
return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));
|
|
|
|
}
|
|
|
|
|
|
|
|
int compareCallback(void *privdata, const void *key1, const void *key2) {
|
|
|
|
int l1,l2;
|
|
|
|
DICT_NOTUSED(privdata);
|
|
|
|
|
|
|
|
l1 = sdslen((sds)key1);
|
|
|
|
l2 = sdslen((sds)key2);
|
|
|
|
if (l1 != l2) return 0;
|
|
|
|
return memcmp(key1, key2, l1) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void freeCallback(void *privdata, void *val) {
|
|
|
|
DICT_NOTUSED(privdata);
|
|
|
|
|
|
|
|
sdsfree(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
dictType BenchmarkDictType = {
|
|
|
|
hashCallback,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
compareCallback,
|
|
|
|
freeCallback,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2016-09-07 04:53:24 -04:00
|
|
|
#define start_benchmark() start = timeInMilliseconds()
|
|
|
|
#define end_benchmark(msg) do { \
|
|
|
|
elapsed = timeInMilliseconds()-start; \
|
|
|
|
printf(msg ": %ld items in %lld ms\n", count, elapsed); \
|
|
|
|
} while(0);
|
|
|
|
|
2016-09-07 04:44:19 -04:00
|
|
|
/* dict-benchmark [count] */
|
|
|
|
int main(int argc, char **argv) {
|
2016-09-07 04:32:57 -04:00
|
|
|
long j;
|
|
|
|
long long start, elapsed;
|
|
|
|
dict *dict = dictCreate(&BenchmarkDictType,NULL);
|
2016-09-07 04:44:19 -04:00
|
|
|
long count = 0;
|
|
|
|
|
|
|
|
if (argc == 2) {
|
|
|
|
count = strtol(argv[1],NULL,10);
|
|
|
|
} else {
|
|
|
|
count = 5000000;
|
|
|
|
}
|
2016-09-07 04:32:57 -04:00
|
|
|
|
2016-09-07 04:53:24 -04:00
|
|
|
start_benchmark();
|
2016-09-07 04:44:19 -04:00
|
|
|
for (j = 0; j < count; j++) {
|
2016-09-07 04:32:57 -04:00
|
|
|
int retval = dictAdd(dict,sdsfromlonglong(j),(void*)j);
|
|
|
|
assert(retval == DICT_OK);
|
|
|
|
}
|
2016-09-07 04:53:24 -04:00
|
|
|
end_benchmark("Inserting");
|
2016-09-07 04:44:19 -04:00
|
|
|
assert((long)dictSize(dict) == count);
|
2016-09-07 04:53:24 -04:00
|
|
|
|
2016-09-07 05:06:03 -04:00
|
|
|
/* Wait for rehashing. */
|
|
|
|
while (dictIsRehashing(dict)) {
|
|
|
|
dictRehashMilliseconds(dict,100);
|
|
|
|
}
|
|
|
|
|
2016-09-07 04:53:24 -04:00
|
|
|
start_benchmark();
|
|
|
|
for (j = 0; j < count; j++) {
|
|
|
|
sds key = sdsfromlonglong(rand() % count);
|
|
|
|
dictEntry *de = dictFind(dict,key);
|
|
|
|
assert(de != NULL);
|
|
|
|
sdsfree(key);
|
|
|
|
}
|
|
|
|
end_benchmark("Accessing existing");
|
|
|
|
|
|
|
|
start_benchmark();
|
|
|
|
for (j = 0; j < count; j++) {
|
|
|
|
sds key = sdsfromlonglong(rand() % count);
|
|
|
|
key[0] = 'X';
|
|
|
|
dictEntry *de = dictFind(dict,key);
|
|
|
|
assert(de == NULL);
|
|
|
|
sdsfree(key);
|
|
|
|
}
|
|
|
|
end_benchmark("Accessing missing");
|
2016-09-07 06:34:37 -04:00
|
|
|
|
|
|
|
start_benchmark();
|
|
|
|
for (j = 0; j < count; j++) {
|
|
|
|
sds key = sdsfromlonglong(j);
|
|
|
|
int retval = dictDelete(dict,key);
|
|
|
|
assert(retval == DICT_OK);
|
|
|
|
key[0] += 17; /* Change first number to letter. */
|
|
|
|
retval = dictAdd(dict,key,(void*)j);
|
|
|
|
assert(retval == DICT_OK);
|
|
|
|
}
|
|
|
|
end_benchmark("Removing and adding");
|
2016-09-07 04:32:57 -04:00
|
|
|
}
|
|
|
|
#endif
|