【C Hash Map from Redis】

  • 将Redis源码中的哈希表底层逻辑提取,并进行最小demo级测试
  • 将对应文件抽出,通过宏替换等方式保证源码编译通过
  • main.c编写测试demo ,注册哈希函数和值比较函数(必选项)
/* 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... :)** Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>* 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.*/#ifndef __DICT_H
#define __DICT_H#include "mt19937-64.h"
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>#define DICT_OK 0
#define DICT_ERR 1/* Unused arguments generate annoying warnings... */
#define DICT_NOTUSED(V) ((void) V)typedef struct dictEntry {void *key;union {void *val;uint64_t u64;int64_t s64;double d;} v;struct dictEntry *next;
} dictEntry;typedef struct dictType {uint64_t (*hashFunction)(const void *key);void *(*keyDup)(void *privdata, const void *key);void *(*valDup)(void *privdata, const void *obj);int (*keyCompare)(void *privdata, const void *key1, const void *key2);void (*keyDestructor)(void *privdata, void *key);void (*valDestructor)(void *privdata, void *obj);int (*expandAllowed)(size_t moreMem, double usedRatio);
} dictType;/* This is our hash table structure. Every dictionary has two of this as we* implement incremental rehashing, for the old to the new table. */
typedef struct dictht {dictEntry **table;unsigned long size;unsigned long sizemask;unsigned long used;
} dictht;typedef struct dict {dictType *type;void *privdata;dictht ht[2];long rehashidx; /* rehashing not in progress if rehashidx == -1 */int16_t pauserehash; /* If >0 rehashing is paused (<0 indicates coding error) */
} dict;/* If safe is set to 1 this is a safe iterator, that means, you can call* dictAdd, dictFind, and other functions against the dictionary even while* iterating. Otherwise it is a non safe iterator, and only dictNext()* should be called while iterating. */
typedef struct dictIterator {dict *d;long index;int table, safe;dictEntry *entry, *nextEntry;/* unsafe iterator fingerprint for misuse detection. */unsigned long long fingerprint;
} dictIterator;typedef void (dictScanFunction)(void *privdata, const dictEntry *de);
typedef void (dictScanBucketFunction)(void *privdata, dictEntry **bucketref);/* This is the initial size of every hash table */
#define DICT_HT_INITIAL_SIZE     4/* ------------------------------- Macros ------------------------------------*/
#define dictFreeVal(d, entry) \if ((d)->type->valDestructor) \(d)->type->valDestructor((d)->privdata, (entry)->v.val)#define dictSetVal(d, entry, _val_) do { \if ((d)->type->valDup) \(entry)->v.val = (d)->type->valDup((d)->privdata, _val_); \else \(entry)->v.val = (_val_); \
} while(0)#define dictSetSignedIntegerVal(entry, _val_) \do { (entry)->v.s64 = _val_; } while(0)#define dictSetUnsignedIntegerVal(entry, _val_) \do { (entry)->v.u64 = _val_; } while(0)#define dictSetDoubleVal(entry, _val_) \do { (entry)->v.d = _val_; } while(0)#define dictFreeKey(d, entry) \if ((d)->type->keyDestructor) \(d)->type->keyDestructor((d)->privdata, (entry)->key)#define dictSetKey(d, entry, _key_) do { \if ((d)->type->keyDup) \(entry)->key = (d)->type->keyDup((d)->privdata, _key_); \else \(entry)->key = (_key_); \
} while(0)#define dictCompareKeys(d, key1, key2) \(((d)->type->keyCompare) ? \(d)->type->keyCompare((d)->privdata, key1, key2) : \(key1) == (key2))#define dictHashKey(d, key) (d)->type->hashFunction(key)
#define dictGetKey(he) ((he)->key)
#define dictGetVal(he) ((he)->v.val)
#define dictGetSignedIntegerVal(he) ((he)->v.s64)
#define dictGetUnsignedIntegerVal(he) ((he)->v.u64)
#define dictGetDoubleVal(he) ((he)->v.d)
#define dictSlots(d) ((d)->ht[0].size+(d)->ht[1].size)
#define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used)
#define dictIsRehashing(d) ((d)->rehashidx != -1)
#define dictPauseRehashing(d) (d)->pauserehash++
#define dictResumeRehashing(d) (d)->pauserehash--/* If our unsigned long type can store a 64 bit number, use a 64 bit PRNG. */
#if ULONG_MAX >= 0xffffffffffffffff
#define randomULong() ((unsigned long) genrand64_int64())
#else
#define randomULong() random()
#endiftypedef enum {DICT_RESIZE_ENABLE,DICT_RESIZE_AVOID,DICT_RESIZE_FORBID,
} dictResizeEnable;/* API */
dict *dictCreate(dictType *type, void *privDataPtr);
int dictExpand(dict *d, unsigned long size);
int dictTryExpand(dict *d, unsigned long size);
int dictAdd(dict *d, void *key, void *val);
dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing);
dictEntry *dictAddOrFind(dict *d, void *key);
int dictReplace(dict *d, void *key, void *val);
int dictDelete(dict *d, const void *key);
dictEntry *dictUnlink(dict *ht, const void *key);
void dictFreeUnlinkedEntry(dict *d, dictEntry *he);
void dictRelease(dict *d);
dictEntry * dictFind(dict *d, const void *key);
void *dictFetchValue(dict *d, const void *key);
int dictResize(dict *d);
dictIterator *dictGetIterator(dict *d);
dictIterator *dictGetSafeIterator(dict *d);
dictEntry *dictNext(dictIterator *iter);
void dictReleaseIterator(dictIterator *iter);
dictEntry *dictGetRandomKey(dict *d);
dictEntry *dictGetFairRandomKey(dict *d);
unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count);
void dictGetStats(char *buf, size_t bufsize, dict *d);
uint64_t dictGenHashFunction(const void *key, int len);
uint64_t dictGenCaseHashFunction(const unsigned char *buf, int len);
void dictEmpty(dict *d, void(callback)(void*));
void dictSetResizeEnabled(dictResizeEnable enable);
int dictRehash(dict *d, int n);
int dictRehashMilliseconds(dict *d, int ms);
void dictSetHashFunctionSeed(uint8_t *seed);
uint8_t *dictGetHashFunctionSeed(void);
unsigned long dictScan(dict *d, unsigned long v, dictScanFunction *fn, dictScanBucketFunction *bucketfn, void *privdata);
uint64_t dictGetHash(dict *d, const void *key);
dictEntry **dictFindEntryRefByPtrAndHash(dict *d, const void *oldptr, uint64_t hash);/* Hash table types */
extern dictType dictTypeHeapStringCopyKey;
extern dictType dictTypeHeapStrings;
extern dictType dictTypeHeapStringCopyKeyValue;#ifdef REDIS_TEST
int dictTest(int argc, char *argv[], int accurate);
#endif/*  defined by blogger  */
#define zcalloc(n) calloc(n, sizeof(char))
#define zmalloc    malloc
#define zfree      free
#define ztrycalloc zcalloc#endif /* __DICT_H */
/* 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... :)** Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>* 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.*///#include "fmacros.h"#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <limits.h>
#include <sys/time.h>
#include <assert.h>
#include "dict.h"
//#include "zmalloc.h"
//#include "redisassert.h"/* Using dictEnableResize() / dictDisableResize() we make possible to disable* resizing and rehashing 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* 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: a hash table is still allowed to grow if the ratio between* the number of elements and the buckets > dict_force_resize_ratio. */
static dictResizeEnable dict_can_resize = DICT_RESIZE_ENABLE;
static unsigned int dict_force_resize_ratio = 5;/* -------------------------- private prototypes ---------------------------- */static int _dictExpandIfNeeded(dict *ht);
static unsigned long _dictNextPower(unsigned long size);
static long _dictKeyIndex(dict *ht, const void *key, uint64_t hash, dictEntry **existing);
static int _dictInit(dict *ht, dictType *type, void *privDataPtr);/* -------------------------- hash functions -------------------------------- */static uint8_t dict_hash_function_seed[16];void dictSetHashFunctionSeed(uint8_t *seed) {memcpy(dict_hash_function_seed,seed,sizeof(dict_hash_function_seed));
}uint8_t *dictGetHashFunctionSeed(void) {return dict_hash_function_seed;
}/* The default hashing function uses SipHash implementation* in siphash.c. */uint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k);
uint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k);uint64_t dictGenHashFunction(const void *key, int len) {return siphash(key,len,dict_hash_function_seed);
}uint64_t dictGenCaseHashFunction(const unsigned char *buf, int len) {return siphash_nocase(buf,len,dict_hash_function_seed);
}/* ----------------------------- API implementation ------------------------- *//* Reset a hash table already initialized with ht_init().* NOTE: This function should only be called by ht_destroy(). */
static void _dictReset(dictht *ht)
{ht->table = NULL;ht->size = 0;ht->sizemask = 0;ht->used = 0;
}/* Create a new hash table */
dict *dictCreate(dictType *type,void *privDataPtr)
{dict *d = zmalloc(sizeof(*d));_dictInit(d,type,privDataPtr);return d;
}/* Initialize the hash table */
int _dictInit(dict *d, dictType *type,void *privDataPtr)
{_dictReset(&d->ht[0]);_dictReset(&d->ht[1]);d->type = type;d->privdata = privDataPtr;d->rehashidx = -1;d->pauserehash = 0;return DICT_OK;
}/* Resize the table to the minimal size that contains all the elements,* but with the invariant of a USED/BUCKETS ratio near to <= 1 */
int dictResize(dict *d)
{unsigned long minimal;if (dict_can_resize != DICT_RESIZE_ENABLE || dictIsRehashing(d)) return DICT_ERR;minimal = d->ht[0].used;if (minimal < DICT_HT_INITIAL_SIZE)minimal = DICT_HT_INITIAL_SIZE;return dictExpand(d, minimal);
}/* Expand or create the hash table,* when malloc_failed is non-NULL, it'll avoid panic if malloc fails (in which case it'll be set to 1).* Returns DICT_OK if expand was performed, and DICT_ERR if skipped. */
int _dictExpand(dict *d, unsigned long size, int* malloc_failed)
{if (malloc_failed) *malloc_failed = 0;/* the size is invalid if it is smaller than the number of* elements already inside the hash table */if (dictIsRehashing(d) || d->ht[0].used > size)return DICT_ERR;dictht n; /* the new hash table */unsigned long realsize = _dictNextPower(size);/* Detect overflows */if (realsize < size || realsize * sizeof(dictEntry*) < realsize)return DICT_ERR;/* Rehashing to the same table size is not useful. */if (realsize == d->ht[0].size) return DICT_ERR;/* Allocate the new hash table and initialize all pointers to NULL */n.size = realsize;n.sizemask = realsize-1;if (malloc_failed) {n.table = ztrycalloc(realsize*sizeof(dictEntry*));*malloc_failed = n.table == NULL;if (*malloc_failed)return DICT_ERR;} elsen.table = zcalloc(realsize*sizeof(dictEntry*));n.used = 0;/* 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;}/* Prepare a second hash table for incremental rehashing */d->ht[1] = n;d->rehashidx = 0;return DICT_OK;
}/* return DICT_ERR if expand was not performed */
int dictExpand(dict *d, unsigned long size) {return _dictExpand(d, size, NULL);
}/* return DICT_ERR if expand failed due to memory allocation failure */
int dictTryExpand(dict *d, unsigned long size) {int malloc_failed;_dictExpand(d, size, &malloc_failed);return malloc_failed? DICT_ERR : 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* 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. */
int dictRehash(dict *d, int n) {int empty_visits = n*10; /* Max number of empty buckets to visit. */unsigned long s0 = d->ht[0].size;unsigned long s1 = d->ht[1].size;if (dict_can_resize == DICT_RESIZE_FORBID || !dictIsRehashing(d)) return 0;if (dict_can_resize == DICT_RESIZE_AVOID && ((s1 > s0 && s1 / s0 < dict_force_resize_ratio) ||(s1 < s0 && s0 / s1 < dict_force_resize_ratio))){return 0;}while(n-- && d->ht[0].used != 0) {dictEntry *de, *nextde;/* Note that rehashidx can't overflow as we are sure there are more* elements because ht[0].used != 0 */assert(d->ht[0].size > (unsigned long)d->rehashidx);while(d->ht[0].table[d->rehashidx] == NULL) {d->rehashidx++;if (--empty_visits == 0) return 1;}de = d->ht[0].table[d->rehashidx];/* Move all the keys in this bucket from the old to the new hash HT */while(de) {uint64_t h;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;}d->ht[0].table[d->rehashidx] = NULL;d->rehashidx++;}/* Check if we already rehashed the whole table... */if (d->ht[0].used == 0) {zfree(d->ht[0].table);d->ht[0] = d->ht[1];_dictReset(&d->ht[1]);d->rehashidx = -1;return 0;}/* More to rehash... */return 1;
}long long timeInMilliseconds(void) {struct timeval tv;gettimeofday(&tv,NULL);return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);
}/* Rehash in ms+"delta" milliseconds. The value of "delta" is larger * than 0, and is smaller than 1 in most cases. The exact upper bound * depends on the running time of dictRehash(d,100).*/
int dictRehashMilliseconds(dict *d, int ms) {if (d->pauserehash > 0) return 0;long long start = timeInMilliseconds();int rehashes = 0;while(dictRehash(d,100)) {rehashes += 100;if (timeInMilliseconds()-start > ms) break;}return rehashes;
}/* This function performs just a step of rehashing, and only if hashing has* not been paused for 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.** 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->pauserehash == 0) dictRehash(d,1);
}/* Add an element to the target hash table */
int dictAdd(dict *d, void *key, void *val)
{dictEntry *entry = dictAddRaw(d,key,NULL);if (!entry) return DICT_ERR;dictSetVal(d, entry, val);return DICT_OK;
}/* Low level add or find:* 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 they wish.** This function is also directly exposed to the user API to be called* mainly in order to store non-pointers inside the hash value, example:** entry = dictAddRaw(dict,mykey,NULL);* if (entry != NULL) dictSetSignedIntegerVal(entry,1000);** Return values:** If key already exists NULL is returned, and "*existing" is populated* with the existing entry if existing is not NULL.** If key was added, the hash entry is returned to be manipulated by the caller.*/
dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing)
{long index;dictEntry *entry;dictht *ht;if (dictIsRehashing(d)) _dictRehashStep(d);/* Get the index of the new element, or -1 if* the element already exists. */if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1)return NULL;/* 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. */ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];entry = zmalloc(sizeof(*entry));entry->next = ht->table[index];ht->table[index] = entry;ht->used++;/* Set the hash entry fields. */dictSetKey(d, entry, key);return entry;
}/* Add or Overwrite:* Add an element, discarding the old value 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. */
int dictReplace(dict *d, void *key, void *val)
{dictEntry *entry, *existing, auxentry;/* Try to add the element. If the key* does not exists dictAdd will succeed. */entry = dictAddRaw(d,key,&existing);if (entry) {dictSetVal(d, entry, val);return 1;}/* 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 = *existing;dictSetVal(d, existing, val);dictFreeVal(d, &auxentry);return 0;
}/* Add or Find:* dictAddOrFind() 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 *dictAddOrFind(dict *d, void *key) {dictEntry *entry, *existing;entry = dictAddRaw(d,key,&existing);return entry ? entry : existing;
}/* Search and remove an element. This is an helper function for* dictDelete() and dictUnlink(), please check the top comment* of those functions. */
static dictEntry *dictGenericDelete(dict *d, const void *key, int nofree) {uint64_t h, idx;dictEntry *he, *prevHe;int table;if (d->ht[0].used == 0 && d->ht[1].used == 0) return NULL;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];prevHe = NULL;while(he) {if (key==he->key || dictCompareKeys(d, key, he->key)) {/* Unlink the element from the list */if (prevHe)prevHe->next = he->next;elsed->ht[table].table[idx] = he->next;if (!nofree) {dictFreeKey(d, he);dictFreeVal(d, he);zfree(he);}d->ht[table].used--;return he;}prevHe = he;he = he->next;}if (!dictIsRehashing(d)) break;}return NULL; /* not found */
}/* Remove an element, returning DICT_OK on success or DICT_ERR if the* element was not found. */
int dictDelete(dict *ht, const void *key) {return dictGenericDelete(ht,key,0) ? DICT_OK : DICT_ERR;
}/* Remove an element from the table, but without actually releasing* the key, value and dictionary entry. The dictionary entry is returned* if the element was found (and unlinked from the table), and the user* should later call `dictFreeUnlinkedEntry()` with it in order to release it.* Otherwise if the key is not found, NULL is returned.** This function is useful when we want to remove something from the hash* table but want to use its value before actually deleting the entry.* Without this function the pattern would require two lookups:**  entry = dictFind(...);*  // Do something with entry*  dictDelete(dictionary,entry);** Thanks to this function it is possible to avoid this, and use* instead:** entry = dictUnlink(dictionary,entry);* // Do something with entry* dictFreeUnlinkedEntry(entry); // <- This does not need to lookup again.*/
dictEntry *dictUnlink(dict *ht, const void *key) {return dictGenericDelete(ht,key,1);
}/* You need to call this function to really free the entry after a call* to dictUnlink(). It's safe to call this function with 'he' = NULL. */
void dictFreeUnlinkedEntry(dict *d, dictEntry *he) {if (he == NULL) return;dictFreeKey(d, he);dictFreeVal(d, he);zfree(he);
}/* Destroy an entire dictionary */
int _dictClear(dict *d, dictht *ht, void(callback)(void *)) {unsigned long i;/* Free all the elements */for (i = 0; i < ht->size && ht->used > 0; i++) {dictEntry *he, *nextHe;if (callback && (i & 65535) == 0) callback(d->privdata);if ((he = ht->table[i]) == NULL) continue;while(he) {nextHe = he->next;dictFreeKey(d, he);dictFreeVal(d, he);zfree(he);ht->used--;he = nextHe;}}/* Free the table and the allocated cache structure */zfree(ht->table);/* Re-initialize the table */_dictReset(ht);return DICT_OK; /* never fails */
}/* Clear & Release the hash table */
void dictRelease(dict *d)
{_dictClear(d,&d->ht[0],NULL);_dictClear(d,&d->ht[1],NULL);zfree(d);
}dictEntry *dictFind(dict *d, const void *key)
{dictEntry *he;uint64_t h, idx, table;if (dictSize(d) == 0) return NULL; /* dict is empty */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) {if (key==he->key || dictCompareKeys(d, key, he->key))return he;he = he->next;}if (!dictIsRehashing(d)) return NULL;}return NULL;
}void *dictFetchValue(dict *d, const void *key) {dictEntry *he;he = dictFind(d,key);return he ? dictGetVal(he) : NULL;
}/* 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. */
unsigned long long dictFingerprint(dict *d) {unsigned long long integers[6], hash = 0;int j;integers[0] = (long) d->ht[0].table;integers[1] = d->ht[0].size;integers[2] = d->ht[0].used;integers[3] = (long) d->ht[1].table;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);hash = (hash + (hash << 3)) + (hash << 8); // hash * 265hash = hash ^ (hash >> 14);hash = (hash + (hash << 2)) + (hash << 4); // hash * 21hash = hash ^ (hash >> 28);hash = hash + (hash << 31);}return hash;
}dictIterator *dictGetIterator(dict *d)
{dictIterator *iter = zmalloc(sizeof(*iter));iter->d = d;iter->table = 0;iter->index = -1;iter->safe = 0;iter->entry = NULL;iter->nextEntry = NULL;return iter;
}dictIterator *dictGetSafeIterator(dict *d) {dictIterator *i = dictGetIterator(d);i->safe = 1;return i;
}dictEntry *dictNext(dictIterator *iter)
{while (1) {if (iter->entry == NULL) {dictht *ht = &iter->d->ht[iter->table];if (iter->index == -1 && iter->table == 0) {if (iter->safe)dictPauseRehashing(iter->d);elseiter->fingerprint = dictFingerprint(iter->d);}iter->index++;if (iter->index >= (long) 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];} 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)
{if (!(iter->index == -1 && iter->table == 0)) {if (iter->safe)dictResumeRehashing(iter->d);elseassert(iter->fingerprint == dictFingerprint(iter->d));}zfree(iter);
}/* Return a random entry from the hash table. Useful to* implement randomized algorithms */
dictEntry *dictGetRandomKey(dict *d)
{dictEntry *he, *orighe;unsigned long h;int listlen, listele;if (dictSize(d) == 0) return NULL;if (dictIsRehashing(d)) _dictRehashStep(d);if (dictIsRehashing(d)) {do {/* We are sure there are no elements in indexes from 0* to rehashidx-1 */h = d->rehashidx + (randomULong() % (dictSlots(d) - d->rehashidx));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 = randomULong() & d->ht[0].sizemask;he = d->ht[0].table[h];} while(he == NULL);}/* Now we found a non empty bucket, but it is a linked* list and we need to get a random element from the list.* The only sane way to do so is counting the elements and* select a random index. */listlen = 0;orighe = he;while(he) {he = he->next;listlen++;}listele = random() % listlen;he = orighe;while(listele--) he = he->next;return he;
}/* 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) {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;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);elsebreak;}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. */unsigned long i = randomULong() & maxsizemask;unsigned long emptylen = 0; /* Continuous empty entries so far. */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. */if (tables == 2 && j == 0 && i < (unsigned long) d->rehashidx) {/* 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;elsecontinue;}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 = randomULong() & maxsizemask;emptylen = 0;}} else {emptylen = 0;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;
}/* This is like dictGetRandomKey() from the POV of the API, but will do more* work to ensure a better distribution of the returned element.** This function improves the distribution because the dictGetRandomKey()* problem is that it selects a random bucket, then it selects a random* element from the chain in the bucket. However elements being in different* chain lengths will have different probabilities of being reported. With* this function instead what we do is to consider a "linear" range of the table* that may be constituted of N buckets with chains of different lengths* appearing one after the other. Then we report a random element in the range.* In this way we smooth away the problem of different chain lengths. */
#define GETFAIR_NUM_ENTRIES 15
dictEntry *dictGetFairRandomKey(dict *d) {dictEntry *entries[GETFAIR_NUM_ENTRIES];unsigned int count = dictGetSomeKeys(d,entries,GETFAIR_NUM_ENTRIES);/* Note that dictGetSomeKeys() may return zero elements in an unlucky* run() even if there are actually elements inside the hash table. So* when we get zero, we call the true dictGetRandomKey() that will always* yield the element if the hash table has at least one. */if (count == 0) return dictGetRandomKey(d);unsigned int idx = rand() % count;return entries[idx];
}/* Function to reverse bits. Algorithm from:* http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel */
static unsigned long rev(unsigned long v) {unsigned long s = CHAR_BIT * sizeof(v); // bit size; must be power of 2unsigned long mask = ~0UL;while ((s >>= 1) > 0) {mask ^= (mask << s);v = ((v >> s) & mask) | ((v << s) & ~mask);}return v;
}/* dictScan() is used to iterate over the elements of a dictionary.** Iterating works the following way:** 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*    new cursor value you must use in the next call.* 3) When the returned cursor is 0, the iteration is complete.** 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.** For every element returned, the callback argument 'fn' is* called with 'privdata' as first argument and the dictionary entry* 'de' as second argument.** HOW IT WORKS.** The iteration algorithm was designed by Pieter Noordhuis.* The main idea is to increment a cursor starting from the higher order* bits. That is, instead of incrementing the cursor normally, the bits* of the cursor are reversed, then the cursor is incremented, and finally* the bits are reversed again.** This strategy is needed because the hash table may be resized between* iteration calls.** 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* by computing the bitwise AND between Hash(key) and SIZE-1* (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).** For example if the current hash table size is 16, the mask is* (in binary) 1111. The position of a key in the hash table will always be* the last four bits of the hash output, and so forth.** WHAT HAPPENS IF THE TABLE CHANGES IN SIZE?** 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).** 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* when scanning the bucket 1100 in the smaller hash table.** By iterating the higher bits first, because of the inverted counter, the* 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.** Similarly when the table size shrinks over time, for example going from* 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* variations of the higher bit) so we don't need to test it again.** WAIT... YOU HAVE *TWO* TABLES DURING REHASHING!** 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* 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* the larger one, if it exists, is just an expansion of the smaller one.** LIMITATIONS** This iterator is completely stateless, and this is a huge advantage,* including no additional memory used.** The disadvantages resulting from this design are:** 1) It is possible we return elements more than once. However this is usually*    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*    we are sure we don't miss keys moving during rehashing.* 3) The reverse cursor is somewhat hard to understand at first, but this*    comment is supposed to help.*/
unsigned long dictScan(dict *d,unsigned long v,dictScanFunction *fn,dictScanBucketFunction* bucketfn,void *privdata)
{dictht *t0, *t1;const dictEntry *de, *next;unsigned long m0, m1;if (dictSize(d) == 0) return 0;/* This is needed in case the scan callback tries to do dictFind or alike. */dictPauseRehashing(d);if (!dictIsRehashing(d)) {t0 = &(d->ht[0]);m0 = t0->sizemask;/* Emit entries at cursor */if (bucketfn) bucketfn(privdata, &t0->table[v & m0]);de = t0->table[v & m0];while (de) {next = de->next;fn(privdata, de);de = next;}/* Set unmasked bits so incrementing the reversed cursor* operates on the masked bits */v |= ~m0;/* Increment the reverse cursor */v = rev(v);v++;v = rev(v);} 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 */if (bucketfn) bucketfn(privdata, &t0->table[v & m0]);de = t0->table[v & m0];while (de) {next = de->next;fn(privdata, de);de = next;}/* 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 */if (bucketfn) bucketfn(privdata, &t1->table[v & m1]);de = t1->table[v & m1];while (de) {next = de->next;fn(privdata, de);de = next;}/* Increment the reverse cursor not covered by the smaller mask.*/v |= ~m1;v = rev(v);v++;v = rev(v);/* Continue while bits covered by mask difference is non-zero */} while (v & (m0 ^ m1));}dictResumeRehashing(d);return v;
}/* ------------------------- private functions ------------------------------ *//* Because we may need to allocate huge memory chunk at once when dict* expands, we will check this allocation is allowed or not if the dict* type has expandAllowed member function. */
static int dictTypeExpandAllowed(dict *d) {if (d->type->expandAllowed == NULL) return 1;return d->type->expandAllowed(_dictNextPower(d->ht[0].used + 1) * sizeof(dictEntry*),(double)d->ht[0].used / d->ht[0].size);
}/* Expand the hash table if needed */
static int _dictExpandIfNeeded(dict *d)
{/* Incremental rehashing already in progress. Return. */if (dictIsRehashing(d)) return DICT_OK;/* If the hash table is empty expand it to the initial 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 (!dictTypeExpandAllowed(d))return DICT_OK;if ((dict_can_resize == DICT_RESIZE_ENABLE &&d->ht[0].used >= d->ht[0].size) ||(dict_can_resize != DICT_RESIZE_FORBID &&d->ht[0].used / d->ht[0].size > dict_force_resize_ratio)){return dictExpand(d, d->ht[0].used + 1);}return DICT_OK;
}/* Our hash table capability is a power of two */
static unsigned long _dictNextPower(unsigned long size)
{unsigned long i = DICT_HT_INITIAL_SIZE;if (size >= LONG_MAX) return LONG_MAX + 1LU;while(1) {if (i >= size)return i;i *= 2;}
}/* Returns the index of a free slot that can be populated with* a hash entry for the given 'key'.* If the key already exists, -1 is returned* and the optional output parameter may be filled.** 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 long _dictKeyIndex(dict *d, const void *key, uint64_t hash, dictEntry **existing)
{unsigned long idx, table;dictEntry *he;if (existing) *existing = NULL;/* Expand the hash table if needed */if (_dictExpandIfNeeded(d) == DICT_ERR)return -1;for (table = 0; table <= 1; table++) {idx = hash & d->ht[table].sizemask;/* Search if this slot does not already contain the given key */he = d->ht[table].table[idx];while(he) {if (key==he->key || dictCompareKeys(d, key, he->key)) {if (existing) *existing = he;return -1;}he = he->next;}if (!dictIsRehashing(d)) break;}return idx;
}void dictEmpty(dict *d, void(callback)(void*)) {_dictClear(d,&d->ht[0],callback);_dictClear(d,&d->ht[1],callback);d->rehashidx = -1;d->pauserehash = 0;
}void dictSetResizeEnabled(dictResizeEnable enable) {dict_can_resize = enable;
}uint64_t dictGetHash(dict *d, const void *key) {return dictHashKey(d, key);
}/* Finds the dictEntry reference by using pointer and pre-calculated hash.* oldkey is a dead pointer and should not be accessed.* the hash value should be provided using dictGetHash.* no string / key comparison is performed.* return value is the reference to the dictEntry if found, or NULL if not found. */
dictEntry **dictFindEntryRefByPtrAndHash(dict *d, const void *oldptr, uint64_t hash) {dictEntry *he, **heref;unsigned long idx, table;if (dictSize(d) == 0) return NULL; /* dict is empty */for (table = 0; table <= 1; table++) {idx = hash & d->ht[table].sizemask;heref = &d->ht[table].table[idx];he = *heref;while(he) {if (oldptr==he->key)return heref;heref = &he->next;he = *heref;}if (!dictIsRehashing(d)) return NULL;}return NULL;
}/* ------------------------------- Debugging ---------------------------------*/#define DICT_STATS_VECTLEN 50
size_t _dictGetStatsHt(char *buf, size_t bufsize, dictht *ht, int tableid) {unsigned long i, slots = 0, chainlen, maxchainlen = 0;unsigned long totchainlen = 0;unsigned long clvector[DICT_STATS_VECTLEN];size_t l = 0;if (ht->used == 0) {return snprintf(buf,bufsize,"Hash table %d stats (%s):\n""No stats available for empty dictionaries\n",tableid, (tableid == 0) ? "main hash table" : "rehashing target");}/* Compute stats. */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;}/* Generate human readable stats. */l += snprintf(buf+l,bufsize-l,"Hash table %d stats (%s):\n"" table size: %lu\n"" number of elements: %lu\n"" different slots: %lu\n"" max chain length: %lu\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);for (i = 0; i < DICT_STATS_VECTLEN-1; i++) {if (clvector[i] == 0) continue;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);}/* Unlike snprintf(), return the number of characters actually written. */if (bufsize) buf[bufsize-1] = '\0';return strlen(buf);
}void dictGetStats(char *buf, size_t bufsize, dict *d) {size_t l;char *orig_buf = buf;size_t orig_bufsize = bufsize;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';
}/* ------------------------------- Benchmark ---------------------------------*/#ifdef REDIS_TESTuint64_t hashCallback(const void *key) {return dictGenHashFunction((unsigned char*)key, strlen((char*)key));
}int compareCallback(void *privdata, const void *key1, const void *key2) {int l1,l2;DICT_NOTUSED(privdata);l1 = strlen((char*)key1);l2 = strlen((char*)key2);if (l1 != l2) return 0;return memcmp(key1, key2, l1) == 0;
}void freeCallback(void *privdata, void *val) {DICT_NOTUSED(privdata);zfree(val);
}char *stringFromLongLong(long long value) {char buf[32];int len;char *s;len = sprintf(buf,"%lld",value);s = zmalloc(len+1);memcpy(s, buf, len);s[len] = '\0';return s;
}dictType BenchmarkDictType = {hashCallback,NULL,NULL,compareCallback,freeCallback,NULL,NULL
};#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)/* ./redis-server test dict [<count> | --accurate] */
int dictTest(int argc, char **argv, int accurate) {long j;long long start, elapsed;dict *dict = dictCreate(&BenchmarkDictType,NULL);long count = 0;if (argc == 4) {if (accurate) {count = 5000000;} else {count = strtol(argv[3],NULL,10);}} else {count = 5000;}start_benchmark();for (j = 0; j < count; j++) {int retval = dictAdd(dict,stringFromLongLong(j),(void*)j);assert(retval == DICT_OK);}end_benchmark("Inserting");assert((long)dictSize(dict) == count);/* Wait for rehashing. */while (dictIsRehashing(dict)) {dictRehashMilliseconds(dict,100);}start_benchmark();for (j = 0; j < count; j++) {char *key = stringFromLongLong(j);dictEntry *de = dictFind(dict,key);assert(de != NULL);zfree(key);}end_benchmark("Linear access of existing elements");start_benchmark();for (j = 0; j < count; j++) {char *key = stringFromLongLong(j);dictEntry *de = dictFind(dict,key);assert(de != NULL);zfree(key);}end_benchmark("Linear access of existing elements (2nd round)");start_benchmark();for (j = 0; j < count; j++) {char *key = stringFromLongLong(rand() % count);dictEntry *de = dictFind(dict,key);assert(de != NULL);zfree(key);}end_benchmark("Random access of existing elements");start_benchmark();for (j = 0; j < count; j++) {dictEntry *de = dictGetRandomKey(dict);assert(de != NULL);}end_benchmark("Accessing random keys");start_benchmark();for (j = 0; j < count; j++) {char *key = stringFromLongLong(rand() % count);key[0] = 'X';dictEntry *de = dictFind(dict,key);assert(de == NULL);zfree(key);}end_benchmark("Accessing missing");start_benchmark();for (j = 0; j < count; j++) {char *key = stringFromLongLong(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");dictRelease(dict);return 0;
}
#endif
#include <stdio.h>
#include <stdint.h>
#include "dict.h"#define sdslen strlen/* -------------------------- hash functions -------------------------------- *//* Generic hash function (a popular one from Bernstein).* I tested a few and this was the best. */
// static unsigned int dictGenHashFunction(const unsigned char *buf, int len) {
//     unsigned int hash = 5381;//     while (len--)
//         hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */
//     return hash;
// }uint64_t dictSdsHash(const void *key) {return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));
}int dictSdsKeyCompare(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;
}/* Dict type without destructor */
dictType sdsReplyDictType = {dictSdsHash,                /* hash function */NULL,                       /* key dup */NULL,                       /* val dup */dictSdsKeyCompare,          /* key compare */NULL,                       /* key destructor */NULL,                       /* val destructor */NULL                        /* allow to expand */
};#define serverAssert(_e) (_e)?(void)0 : (fprintf(stderr, "==> '%s' is not true [%s:%d]\n", #_e,__FILE__,__LINE__))typedef struct _MapEntry 
{char *key;char *value;
}MapEntry_t;int main()
{int index = 0;dict *dictHd = dictCreate(&sdsReplyDictType, NULL);if(NULL == dictHd){printf("NULL == entry\n");return -1;}dictExpand(dictHd, 1024);MapEntry_t mapEntry[] = {[0] = {"key1", "1"},[1] = {"key2", "2"},[2] = {"key3", "3"},};int numcommands = sizeof(mapEntry)/sizeof(MapEntry_t);printf("numcommands %d \n", numcommands);for(index = 0; index < numcommands; index++){printf("add:  key: %s, value: %s\n", mapEntry[index].key, mapEntry[index].value);int retVal  = dictAdd(dictHd, (void *)mapEntry[index].key, (void *)mapEntry[index].value);serverAssert(retVal == DICT_OK);}dictEntry *find = NULL;find =  dictFind(dictHd, mapEntry[0].key);serverAssert(find != NULL); printf("key %s, value %s\n", mapEntry[0].key, find->v.val);if (find != NULL)dictReplace(dictHd, mapEntry[0].key, "11");find =  dictFind(dictHd, mapEntry[0].key);serverAssert(find != NULL);if (find != NULL) printf("key %s, value %s\n", mapEntry[0].key, find->v.val);dictDelete(dictHd, mapEntry[0].key);find =  dictFind(dictHd, mapEntry[0].key);serverAssert(find != NULL);if (find != NULL) printf("key %s, value %s\n", mapEntry[0].key, find->v.val);find =  dictFind(dictHd, mapEntry[2].key);serverAssert(find != NULL);if (find != NULL) printf("key %s %s, value %s\n", mapEntry[2].key, find->key,find->v.val);return 0;
}
  • result

在这里插入图片描述

  • 完整代码
    https://github.com/AntigravityCC/hash_map_from_redis

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/616727.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024/4/11 直流电机调速/PWM

一、直流电机简介和PWM原理 直流电机是一种将电能转换为机械能的装置。一般的直流电机有两个电极&#xff0c;当电极正接时&#xff0c;电机正转&#xff0c;当电极反接时&#xff0c;电机反转 直流电机主要由永磁体&#xff08;定子&#xff09;、线圈&#xff08;转子&…

VulNyx - Ready

目录 信息收集 arp nmap nikto Redis未授权访问漏洞 漏洞扫描 redis-cli 写入公钥 ssh连接 get root.txt 信息收集 arp ┌─[rootparrot]─[~/vulnyx] └──╼ #arp-scan -l Interface: enp0s3, type: EN10MB, MAC: 08:00:27:16:3d:f8, IPv4: 192.168.9.102 Starti…

Docker快速上手及常用命令速查

Docker快速上手 安装 在ubuntu上安装docker: sudo apt-get install docker docker -v #查看版本在centos7上安装docker&#xff1a;(docker在YUM源的Extras仓库中) yum install docker systemctl start dockerdocker常用命令速查 #查看docker信息 docker info #查看本地镜…

JavaScript中的Blob、Buffer、ArrayBuffer和TypedArray详解

文章的更新路线&#xff1a;JavaScript基础知识-Vue2基础知识-Vue3基础知识-TypeScript基础知识-网络基础知识-浏览器基础知识-项目优化知识-项目实战经验-前端温习题&#xff08;HTML基础知识和CSS基础知识已经更新完毕&#xff09; 正文 摘要&#xff1a;本文详细介绍了JavaS…

Linux安装和管理程序

1. Linux 软件包封装类型&#xff1a; &#xff08;1&#xff09;RPM/DEB软件包&#xff1a; REHL、CentOS、OpenSUSE等系列系统支持 Ubuntu、Debian等系列系统支持 软件包名称格式XXX.rpmXXX.deb管理命令rpm yumdpkg apt-get &#xff08;2&#xff09;源代码软件包&…

【MYSQL管理工具】数据库备份和恢复

&#x1f525;作者主页&#xff1a;小林同学的学习笔录 &#x1f525;mysql专栏&#xff1a;小林同学的专栏 目录 1.MYSQL管理 1.1 系统数据库 1.2 常用工具 1.2.1 mysql 1.2.2 mysqladmin 1.2.3 mysqlbinlog 1.2.4 mysqlshow 1.2.5 mysqldump 1.2.6 mysqlimport/sour…

分类预测 | Matlab实现RIME-LSSVM霜冰算法优化最小二乘支持向量机数据分类预测

分类预测 | Matlab实现RIME-LSSVM霜冰算法优化最小二乘支持向量机数据分类预测 目录 分类预测 | Matlab实现RIME-LSSVM霜冰算法优化最小二乘支持向量机数据分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现RIME-LSSVM霜冰算法优化最小二乘支持向量机数…

Vue 3 项目中如何使用 TypeScript 类型来优化 Vuex 的状态管理?

在 Vue 3 项目中&#xff0c;使用 TypeScript 可以极大地优化 Vuex 的状态管理&#xff0c;提供更强的类型检查和更好的开发体验。以下是一些使用 TypeScript 来优化 Vuex 状态管理的方法&#xff1a; 定义状态类型&#xff1a; 使用 TypeScript 的接口&#xff08;Interfaces&…

redis的主从复制(docker方式快速入门和实战)

目录 一、主从复制简介 二、配置主从服务器 2.1使用配置文件的形式来主从复制 2.2使用纯代码的方式来进行主从复制&#xff1b; 2.3脱离主服务器 三、一些注意事项 一、主从复制简介 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器…

第16天:信息打点-CDN绕过业务部署漏洞回链接口探针全网扫描反向邮件

第十六天 本课意义 1.CDN服务对安全影响 2.CDN服务绕过识别手法 一、CDN服务-解释差异识别 1.前置知识&#xff1a; 传统访问&#xff1a;用户访问域名–>解析服务器IP–>访问目标主机普通CDN&#xff1a;用户访问域名–>CDN节点–>真实服务器IP–>访问目标…

苹果电脑启动磁盘是什么意思 苹果电脑磁盘清理软件 mac找不到启动磁盘 启动磁盘没有足够的空间来进行分区

当你一早打开苹果电脑&#xff0c;结果系统突然提示&#xff1a; “启动磁盘已满&#xff0c;需要删除部分文件”。你会怎么办&#xff1f;如果你认为单纯靠清理废纸篓或者删除大型文件就能释放你的启动磁盘上的空间&#xff0c;那就大错特错了。其实苹果启动磁盘的清理技巧有很…

修改Catsxp暗蓝色背景

Catsxp浏览器自从123内核后&#xff0c;背景就是暗蓝色了&#xff0c;太辣眼睛了&#xff0c;开发者说是原生的。 今天我点击主题背景-恢复默认修复了&#xff01; 所以是安装了一个主题引起的。