diff options
| author | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-01-21 22:40:55 +0100 |
|---|---|---|
| committer | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-01-21 22:40:55 +0100 |
| commit | 5d8dfe892a2ea89f706ee140c3bdcfd89fe03fda (patch) | |
| tree | 1acdfa5220cd13b7be43a2a01368e80d306473ca /examples/redis-unstable/src/dict.h | |
| parent | c7ab12bba64d9c20ccd79b132dac475f7bc3923e (diff) | |
| download | crep-5d8dfe892a2ea89f706ee140c3bdcfd89fe03fda.tar.gz | |
Add Redis source code for testing
Diffstat (limited to 'examples/redis-unstable/src/dict.h')
| -rw-r--r-- | examples/redis-unstable/src/dict.h | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/examples/redis-unstable/src/dict.h b/examples/redis-unstable/src/dict.h new file mode 100644 index 0000000..25e4cf1 --- /dev/null +++ b/examples/redis-unstable/src/dict.h @@ -0,0 +1,319 @@ +/* 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-Present, Redis Ltd. + * All rights reserved. + * + * Licensed under your choice of (a) the Redis Source Available License 2.0 + * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the + * GNU Affero General Public License v3 (AGPLv3). + * + * Dict usage of pointer tagging + * ----------------------------- + * In the "normal" case (no_value=0), a dict slot contains only a pointer to a + * dictEntry, and dictEntry holds untagged pointers to key and value. But when a + * dict is used as a set (no_value=1), we optimize by storing direct key pointers + * when possible, avoiding dictEntry allocation. This happens when A bucket contains + * only one key, or at the tail of a collision chain. Redis dicts uses pointer + * tagging, to identify direct key pointers from dictEntry pointers, i.e embedding + * metadata in the lowest three bits of pointers. This requires 8-byte alignment, + * which zmalloc() guarantees on both 32-bit and 64-bit systems (via jemalloc/tcmalloc, + * or standard malloc with explicit PREFIX_SIZE=8). + * + * Besides of distinguishing direct key pointers from dictEntry pointers, we also + * need to distinguish between even and odd key pointers that being stored in the + * dict. Therefore, we use the following tagging scheme: + * - dictEntry pointer: Points to a dictEntry structure (8-byte aligned). Left intact: + * ENTRY_PTR_NORMAL=000 + * - Odd-address key (keys_are_odd=1): Direct pointer to a + * key with odd address (e.g., all SDS strings), Left intact: + * ENTRY_PTR_IS_ODD_KEY=XX1 + * - Even-address key (keys_are_odd=0): Direct pointer to a key with + * even address. Since 8-byte alignment yields bits = 000, same as dictEntry, + * we tag it by setting bit 1 which results with: + * ENTRY_PTR_IS_EVEN_KEY=010. + */ + +#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 + +/* Hash table parameters */ +#define HASHTABLE_MIN_FILL 8 /* Minimal hash table fill 12.5%(100/8) */ + +/* stored-key vs. key + * ------------------ + * If dictType.keyFromStoredKey is non-NULL, then dict distinguishes between the + * lookup key and the actual stored-key object. In this case, "key" is used to + * locate entries, while "storedKey" is the actual element stored in the dict. + * If dictType.keyFromStoredKey is NULL, the lookup "key" and the stored-key are the + * same. This API is primarily relevant for no_value=1 dicts, where the key and value + * might be packed together. When values are stored separately, this identity + * distinction does not arise. The marker __stored_key is used to indicate that + * the pointer refers to the stored-key rather than the lookup key. + */ +#define __stored_key + +typedef struct dictEntry dictEntry; /* opaque */ +typedef struct dict dict; +typedef dictEntry **dictEntryLink; /* See description of dictFindLink() */ + +/* Searching for a key in a dict may involve few comparisons. + * If extracting the looked-up key is expensive (e.g., sdslen(), kvobjGetKey()), + * caching can be used to reduce those repetitive computations. + * + * This struct, passed to the comparison function as temporary caching, if + * needed by the function across comparison of a given lookup. + * for the looked-up key and resets before each new lookup. */ +typedef struct dictCmpCache { + int useCache; + + union { + uint64_t u64; + int64_t i64; + int i; + size_t sz; + void *p; + } data[2]; +} dictCmpCache; + +typedef struct dictType { + /* Callbacks */ + uint64_t (*hashFunction)(const void *key); + void *(*keyDup)(dict *d, const void *key __stored_key); + void *(*valDup)(dict *d, const void *obj); + int (*keyCompare)(dictCmpCache *cache, const void *key1, const void *key2); + void (*keyDestructor)(dict *d, void *key __stored_key); + void (*valDestructor)(dict *d, void *obj); + int (*resizeAllowed)(size_t moreMem, double usedRatio); + /* Invoked at the start of dict initialization/rehashing (old and new ht are already created) */ + void (*rehashingStarted)(dict *d); + /* Invoked at the end of dict initialization/rehashing of all the entries from old to new ht. Both ht still exists + * and are cleaned up after this callback. */ + void (*rehashingCompleted)(dict *d); + /* Invoked when the size of the dictionary changes. + * The `delta` parameter can be positive (size increase) or negative (size decrease). */ + void (*bucketChanged)(dict *d, long long delta); + /* Allow a dict to carry extra caller-defined metadata. The + * extra memory is initialized to 0 when a dict is allocated. */ + size_t (*dictMetadataBytes)(dict *d); + + /* Data */ + void *userdata; + + /* Flags */ + /* The 'no_value' flag, if set, indicates that values are not used, i.e. the + * dict is a set. When this flag is set, it's not possible to access the + * value of a dictEntry and it's also impossible to use dictSetKey(). It + * enables an optimization to store a key directly without an allocating + * dictEntry in between, if it is the only key in the bucket. */ + unsigned int no_value:1; + /* This flag is required for `no_value` optimization since the optimization + * reuses LSB bits as metadata */ + unsigned int keys_are_odd:1; + + /* Ensures that the entire hash table is rehashed at once if set. */ + unsigned int force_full_rehash:1; + + /* Callback to extract key from stored-key object. When set, the dict can + * store keys in one format (e.g., a structure) but look them up using a + * different format, extracted from the stored-key. (e.g., sds or integer). + * Set to NULL if key and stored-key object are the same. Relevant only for + * no_value=1 dicts. */ + const void *(*keyFromStoredKey)(const void *key __stored_key); + + /* Optional callback called when the dict is destroyed. */ + void (*onDictRelease)(dict *d); +} dictType; + +#define DICTHT_SIZE(exp) ((exp) == -1 ? 0 : (unsigned long)1<<(exp)) +#define DICTHT_SIZE_MASK(exp) ((exp) == -1 ? 0 : (DICTHT_SIZE(exp))-1) + +struct dict { + dictType *type; + + dictEntry **ht_table[2]; + unsigned long ht_used[2]; + + long rehashidx; /* rehashing not in progress if rehashidx == -1 */ + + /* Note: pauserehash is a full unsigned so iterator increments + * don't perform RMW on the same storage unit as other bitfields. */ + unsigned pauserehash; /* If >0 rehashing is paused */ + + /* Keep small vars at end for optimal (minimal) struct padding */ + signed char ht_size_exp[2]; /* exponent of size. (size = 1<<exp) */ + int16_t pauseAutoResize; /* If >0 automatic resizing is disallowed (<0 indicates coding error) */ + void *metadata[]; +}; + +/* 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 struct dictStats { + int htidx; + unsigned long buckets; + unsigned long maxChainLen; + unsigned long totalChainLen; + unsigned long htSize; + unsigned long htUsed; + unsigned long *clvector; +} dictStats; + +typedef void (dictScanFunction)(void *privdata, const dictEntry *de, dictEntry **plink); +typedef void *(dictDefragAllocFunction)(void *ptr); +typedef struct { + dictDefragAllocFunction *defragAlloc; /* Used for entries etc. */ + dictDefragAllocFunction *defragKey; /* Defrag-realloc keys (optional) */ + dictDefragAllocFunction *defragVal; /* Defrag-realloc values (optional) */ +} dictDefragFunctions; + +/* This is the initial size of every hash table */ +#define DICT_HT_INITIAL_EXP 2 +#define DICT_HT_INITIAL_SIZE (1<<(DICT_HT_INITIAL_EXP)) + +/* ------------------------------- Macros ------------------------------------*/ +#define dictFreeVal(d, entry) do { \ + if ((d)->type->valDestructor) \ + (d)->type->valDestructor((d), dictGetVal(entry)); \ + } while(0) + +#define dictFreeKey(d, entry) \ + if ((d)->type->keyDestructor) \ + (d)->type->keyDestructor((d), dictGetKey(entry)) + +#define dictMetadata(d) (&(d)->metadata) +#define dictMetadataSize(d) ((d)->type->dictMetadataBytes \ + ? (d)->type->dictMetadataBytes(d) : 0) + +#define dictBuckets(d) (DICTHT_SIZE((d)->ht_size_exp[0])+DICTHT_SIZE((d)->ht_size_exp[1])) +#define dictSize(d) ((d)->ht_used[0]+(d)->ht_used[1]) +#define dictIsEmpty(d) ((d)->ht_used[0] == 0 && (d)->ht_used[1] == 0) +#define dictIsRehashing(d) ((d)->rehashidx != -1) +#define dictPauseRehashing(d) ((d)->pauserehash++) +#define dictResumeRehashing(d) ((d)->pauserehash--) +#define dictIsRehashingPaused(d) ((d)->pauserehash > 0) +#define dictPauseAutoResize(d) ((d)->pauseAutoResize++) +#define dictResumeAutoResize(d) ((d)->pauseAutoResize--) + +/* 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() +#endif + +typedef enum { + DICT_RESIZE_ENABLE, + DICT_RESIZE_AVOID, + DICT_RESIZE_FORBID, +} dictResizeEnable; + +/* API */ +dict *dictCreate(dictType *type); +void dictTypeAddMeta(dict **d, dictType *typeWithMeta); +int dictExpand(dict *d, unsigned long size); +int dictTryExpand(dict *d, unsigned long size); +int dictShrink(dict *d, unsigned long size); +int dictAdd(dict *d, void *key __stored_key, void *val); +dictEntry *dictAddRaw(dict *d, void *key __stored_key, dictEntry **existing); +dictEntry *dictAddOrFind(dict *d, void *key __stored_key); +int dictReplace(dict *d, void *key __stored_key, void *val); +int dictDelete(dict *d, const void *key); +dictEntry *dictUnlink(dict *d, const void *key); +void dictFreeUnlinkedEntry(dict *d, dictEntry *he); +dictEntryLink dictTwoPhaseUnlinkFind(dict *d, const void *key, int *table_index); +void dictTwoPhaseUnlinkFree(dict *d, dictEntryLink llink, int table_index); +void dictRelease(dict *d); +dictEntry * dictFind(dict *d, const void *key); +dictEntry *dictFindByHashAndPtr(dict *d, const void *oldptr, const uint64_t hash); +int dictShrinkIfNeeded(dict *d); +int dictExpandIfNeeded(dict *d); +void *dictGetKey(const dictEntry *de); +int dictEntryIsKey(const dictEntry *de); +int dictCompareKeys(dict *d, const void *key1, const void *key2); +size_t dictMemUsage(const dict *d); +size_t dictEntryMemUsage(int noValueDict); +dictIterator *dictGetIterator(dict *d); +dictIterator *dictGetSafeIterator(dict *d); +void dictInitIterator(dictIterator *iter, dict *d); +void dictInitSafeIterator(dictIterator *iter, dict *d); +void dictResetIterator(dictIterator *iter); +dictEntry *dictNext(dictIterator *iter); +dictEntry *dictGetNext(const dictEntry *de); +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, int full); +uint64_t dictGenHashFunction(const void *key, size_t len); +uint64_t dictGenCaseHashFunction(const unsigned char *buf, size_t len); +void dictEmpty(dict *d, void(callback)(dict*)); +void dictSetResizeEnabled(dictResizeEnable enable); +int dictRehash(dict *d, int n); +int dictRehashMicroseconds(dict *d, uint64_t us); +void dictSetHashFunctionSeed(uint8_t *seed); +unsigned long dictScan(dict *d, unsigned long v, dictScanFunction *fn, void *privdata); +unsigned long dictScanDefrag(dict *d, unsigned long v, dictScanFunction *fn, dictDefragFunctions *defragfns, void *privdata); +uint64_t dictGetHash(dict *d, const void *key); +void dictRehashingInfo(dict *d, unsigned long long *from_size, unsigned long long *to_size); + +size_t dictGetStatsMsg(char *buf, size_t bufsize, dictStats *stats, int full); +dictStats* dictGetStatsHt(dict *d, int htidx, int full); +void dictCombineStats(dictStats *from, dictStats *into); +void dictFreeStats(dictStats *stats); + +dictEntryLink dictFindLink(dict *d, const void *key, dictEntryLink *bucket); +void dictSetKeyAtLink(dict *d, void *key __stored_key, dictEntryLink *link, int newItem); + +/* API relevant only when dict is used as a hash-map (no_value=0) */ +void dictSetKey(dict *d, dictEntry* de, void *key __stored_key); +void dictSetVal(dict *d, dictEntry *de, void *val); +void *dictGetVal(const dictEntry *de); +void dictSetDoubleVal(dictEntry *de, double val); +double dictGetDoubleVal(const dictEntry *de); +double *dictGetDoubleValPtr(dictEntry *de); +void *dictFetchValue(dict *d, const void *key); +void dictSetUnsignedIntegerVal(dictEntry *de, uint64_t val); +uint64_t dictIncrUnsignedIntegerVal(dictEntry *de, uint64_t val); +uint64_t dictGetUnsignedIntegerVal(const dictEntry *de); + +#define dictForEach(d, ty, m, ...) do { \ + dictIterator di; \ + dictEntry *de; \ + dictInitIterator(&di, d); \ + while ((de = dictNext(&di)) != NULL) { \ + ty *m = dictGetVal(de); \ + do { \ + __VA_ARGS__ \ + } while(0); \ + } \ + dictResetIterator(&di); \ +} while(0); + +#ifdef REDIS_TEST +int dictTest(int argc, char *argv[], int flags); +#endif + +#endif /* __DICT_H */ |
