diff --git a/src/Makefile b/src/Makefile index 83f5c6d79513ae211bbe08f80578067aa346c937..8cf6ba6f82e7eb868f2c54d36d3f4ae3a0c1aab0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -132,7 +132,7 @@ endif REDIS_SERVER_NAME=redis-server REDIS_SENTINEL_NAME=redis-sentinel -REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o +REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o REDIS_CLI_NAME=redis-cli REDIS_CLI_OBJ=anet.o adlist.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o REDIS_BENCHMARK_NAME=redis-benchmark @@ -208,7 +208,7 @@ $(REDIS_CHECK_AOF_NAME): $(REDIS_CHECK_AOF_OBJ) $(REDIS_LD) -o $@ $^ $(FINAL_LIBS) dict-benchmark: dict.c zmalloc.c sds.c - $(REDIS_CC) $(FINAL_CFLAGS) dict.c zmalloc.c sds.c -D DICT_BENCHMARK_MAIN -o dict-benchmark + $(REDIS_CC) $(FINAL_CFLAGS) dict.c zmalloc.c sds.c siphash.c -D DICT_BENCHMARK_MAIN -o dict-benchmark # Because the jemalloc.h header is generated as a part of the jemalloc build, # building it should complete before building any other object. Instead of diff --git a/src/config.c b/src/config.c index 83651877c320870518c0f407fd771531936b4436..900274f65e731acb3f7cb0480c11ad5326d7b5c7 100644 --- a/src/config.c +++ b/src/config.c @@ -1423,7 +1423,7 @@ void configGetCommand(client *c) { /* We use the following dictionary type to store where a configuration * option is mentioned in the old configuration file, so it's * like "maxmemory" -> list of line numbers (first line is zero). */ -unsigned int dictSdsCaseHash(const void *key); +uint64_t dictSdsCaseHash(const void *key); int dictSdsKeyCaseCompare(void *privdata, const void *key1, const void *key2); void dictSdsDestructor(void *privdata, void *val); void dictListDestructor(void *privdata, void *val); diff --git a/src/debug.c b/src/debug.c index a6bc62dc8598eeefe722ab69fa9355e68c4dab1d..a4caa49f285c77c10bbf60497ef36309a2f0a1d9 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1029,8 +1029,6 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) { /* Log INFO and CLIENT LIST */ serverLogRaw(LL_WARNING|LL_RAW, "\n------ INFO OUTPUT ------\n"); infostring = genRedisInfoString("all"); - infostring = sdscatprintf(infostring, "hash_init_value: %u\n", - dictGetHashFunctionSeed()); serverLogRaw(LL_WARNING|LL_RAW, infostring); serverLogRaw(LL_WARNING|LL_RAW, "\n------ CLIENT LIST OUTPUT ------\n"); clients = getAllClientsInfoString(); diff --git a/src/dict.c b/src/dict.c index 59aef77247a540d6e6289ecc2260604b2d16d7ab..69fb3b8f88bc4695760f004d5a01c59562269304 100644 --- a/src/dict.c +++ b/src/dict.c @@ -37,11 +37,11 @@ #include #include +#include #include #include #include #include -#include #include "dict.h" #include "zmalloc.h" @@ -71,77 +71,28 @@ static int _dictInit(dict *ht, dictType *type, void *privDataPtr); /* -------------------------- hash functions -------------------------------- */ -static uint32_t dict_hash_function_seed = 5381; +static uint8_t dict_hash_function_seed[16]; -void dictSetHashFunctionSeed(uint32_t seed) { - dict_hash_function_seed = seed; +void dictSetHashFunctionSeed(uint8_t *seed) { + memcpy(dict_hash_function_seed,seed,sizeof(dict_hash_function_seed)); } -uint32_t dictGetHashFunctionSeed(void) { +uint8_t *dictGetHashFunctionSeed(void) { return dict_hash_function_seed; } -/* 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; +/* The default hashing function uses SipHash implementation + * in siphash.c. */ - h *= m; - h ^= k; +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); - 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; +uint64_t dictGenHashFunction(const void *key, int len) { + return siphash(key,len,dict_hash_function_seed); } -/* And a case insensitive hash function (based on djb hash) */ -unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) { - unsigned int hash = (unsigned int)dict_hash_function_seed; - - while (len--) - hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */ - return hash; +uint64_t dictGenCaseHashFunction(const unsigned char *buf, int len) { + return siphash_nocase(buf,len,dict_hash_function_seed); } /* ----------------------------- API implementation ------------------------- */ @@ -1158,7 +1109,7 @@ void dictGetStats(char *buf, size_t bufsize, dict *d) { #include "sds.h" -unsigned int hashCallback(const void *key) { +uint64_t hashCallback(const void *key) { return dictGenHashFunction((unsigned char*)key, sdslen((char*)key)); } diff --git a/src/dict.h b/src/dict.h index 60a423a2c12f497adbdf3d6066a135a5c4bc187b..bf316a00f927441dfac3b7279df7cf79c7b4ece8 100644 --- a/src/dict.h +++ b/src/dict.h @@ -56,7 +56,7 @@ typedef struct dictEntry { } dictEntry; typedef struct dictType { - unsigned int (*hashFunction)(const void *key); + 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); @@ -168,15 +168,15 @@ void dictReleaseIterator(dictIterator *iter); dictEntry *dictGetRandomKey(dict *d); unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count); void dictGetStats(char *buf, size_t bufsize, dict *d); -unsigned int dictGenHashFunction(const void *key, int len); -unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len); +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 dictEnableResize(void); void dictDisableResize(void); int dictRehash(dict *d, int n); int dictRehashMilliseconds(dict *d, int ms); -void dictSetHashFunctionSeed(unsigned int initval); -unsigned int dictGetHashFunctionSeed(void); +void dictSetHashFunctionSeed(uint8_t *seed); +uint8_t *dictGetHashFunctionSeed(void); unsigned long dictScan(dict *d, unsigned long v, dictScanFunction *fn, dictScanBucketFunction *bucketfn, void *privdata); unsigned int dictGetHash(dict *d, const void *key); dictEntry **dictFindEntryRefByPtrAndHash(dict *d, const void *oldptr, unsigned int hash); diff --git a/src/latency.c b/src/latency.c index 53e0ec7becb33ce2a7a5318956c0b7e8430afb16..9e9f1f13a973f883a7e648aa08056688637c9275 100644 --- a/src/latency.c +++ b/src/latency.c @@ -41,7 +41,7 @@ int dictStringKeyCompare(void *privdata, const void *key1, const void *key2) { return strcmp(key1,key2) == 0; } -unsigned int dictStringHash(const void *key) { +uint64_t dictStringHash(const void *key) { return dictGenHashFunction(key, strlen(key)); } diff --git a/src/module.c b/src/module.c index 1fbc5094f4b31c4a950aa4b87b5c99e2fa644511..3b90eae4a419e2318f779789d0fbe08132258a56 100644 --- a/src/module.c +++ b/src/module.c @@ -3264,7 +3264,7 @@ void *RM_GetBlockedClientPrivateData(RedisModuleCtx *ctx) { /* server.moduleapi dictionary type. Only uses plain C strings since * this gets queries from modules. */ -unsigned int dictCStringKeyHash(const void *key) { +uint64_t dictCStringKeyHash(const void *key) { return dictGenHashFunction((unsigned char*)key, strlen((char*)key)); } diff --git a/src/sentinel.c b/src/sentinel.c index 1f47dd337ec7217932a60a16cea74d71b9c25758..6c6a3a0cd484402386039c8fca0a42452eb0b76b 100644 --- a/src/sentinel.c +++ b/src/sentinel.c @@ -379,7 +379,7 @@ void sentinelSimFailureCrash(void); /* ========================= Dictionary types =============================== */ -unsigned int dictSdsHash(const void *key); +uint64_t dictSdsHash(const void *key); int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2); void releaseSentinelRedisInstance(sentinelRedisInstance *ri); diff --git a/src/server.c b/src/server.c index 8bf6510deb62d26d441efdc9607dce888cc387cf..0494a4e7561cee86674014b540c7193f021e7b05 100644 --- a/src/server.c +++ b/src/server.c @@ -482,16 +482,16 @@ int dictObjKeyCompare(void *privdata, const void *key1, return dictSdsKeyCompare(privdata,o1->ptr,o2->ptr); } -unsigned int dictObjHash(const void *key) { +uint64_t dictObjHash(const void *key) { const robj *o = key; return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr)); } -unsigned int dictSdsHash(const void *key) { +uint64_t dictSdsHash(const void *key) { return dictGenHashFunction((unsigned char*)key, sdslen((char*)key)); } -unsigned int dictSdsCaseHash(const void *key) { +uint64_t dictSdsCaseHash(const void *key) { return dictGenCaseHashFunction((unsigned char*)key, sdslen((char*)key)); } @@ -513,7 +513,7 @@ int dictEncObjKeyCompare(void *privdata, const void *key1, return cmp; } -unsigned int dictEncObjHash(const void *key) { +uint64_t dictEncObjHash(const void *key) { robj *o = (robj*) key; if (sdsEncodedObject(o)) { @@ -526,7 +526,7 @@ unsigned int dictEncObjHash(const void *key) { len = ll2string(buf,32,(long)o->ptr); return dictGenHashFunction((unsigned char*)buf, len); } else { - unsigned int hash; + uint64_t hash; o = getDecodedObject(o); hash = dictGenHashFunction(o->ptr, sdslen((sds)o->ptr)); @@ -3639,7 +3639,9 @@ int main(int argc, char **argv) { zmalloc_set_oom_handler(redisOutOfMemoryHandler); srand(time(NULL)^getpid()); gettimeofday(&tv,NULL); - dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid()); + char hashseed[16]; + getRandomHexChars(hashseed,sizeof(hashseed)); + dictSetHashFunctionSeed((uint8_t*)hashseed); server.sentinel_mode = checkForSentinelMode(argc,argv); initServerConfig(); moduleInitModulesSystem(); diff --git a/src/server.h b/src/server.h index 30d8be8497ea01840f247ae1bf478aee857f9832..75ff384cdb4b799ea3e2cbec61ac65d55effba6f 100644 --- a/src/server.h +++ b/src/server.h @@ -1765,7 +1765,7 @@ unsigned long LFUGetTimeInMinutes(void); uint8_t LFULogIncr(uint8_t value); /* Keys hashing / comparison functions for dict.c hash tables. */ -unsigned int dictSdsHash(const void *key); +uint64_t dictSdsHash(const void *key); int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2); void dictSdsDestructor(void *privdata, void *val); diff --git a/src/siphash.c b/src/siphash.c new file mode 100644 index 0000000000000000000000000000000000000000..6c41fe6b678d185269dd946f0515bd95c7678f69 --- /dev/null +++ b/src/siphash.c @@ -0,0 +1,360 @@ +/* + SipHash reference C implementation + + Copyright (c) 2012-2016 Jean-Philippe Aumasson + + Copyright (c) 2012-2014 Daniel J. Bernstein + Copyright (c) 2017 Salvatore Sanfilippo + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along + with this software. If not, see + . + + ---------------------------------------------------------------------------- + + This version was modified by Salvatore Sanfilippo + in the following ways: + + 1. We use SipHash 1-2. This is not believed to be as strong as the + suggested 2-4 variant, but AFAIK there are not trivial attacks + against this reduced-rounds version, and it runs at the same speed + as Murmurhash2 that we used previously, why the 2-4 variant slowed + down Redis by a 4% figure more or less. + 2. Hard-code rounds in the hope the compiler can optimize it more + in this raw from. Anyway we always want the standard 2-4 variant. + 3. Modify the prototype and implementation so that the function directly + returns an uint64_t value, the hash itself, instead of receiving an + output buffer. This also means that the output size is set to 8 bytes + and the 16 bytes output code handling was removed. + 4. Provide a case insensitive variant to be used when hashing strings that + must be considered identical by the hash table regardless of the case. + If we don't have directly a case insensitive hash function, we need to + perform a text transformation in some temporary buffer, which is costly. + 5. Remove debugging code. + 6. Modified the original test.c file to be a stand-alone function testing + the function in the new form (returing an uint64_t) using just the + relevant test vector. + */ +#include +#include +#include +#include +#include + +/* Fast tolower() alike function that does not care about locale + * but just returns a-z insetad of A-Z. */ +int siptlw(int c) { + if (c >= 'A' && c <= 'Z') { + return c+('a'-'A'); + } else { + return c; + } +} + +/* Test of the CPU is Little Endian and supports not aligned accesses. + * Two interesting conditions to speedup the function that happen to be + * in most of x86 servers. */ +#if defined(__X86_64__) || defined(__x86_64__) || defined (__i386__) +#define UNALIGNED_LE_CPU +#endif + +#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) + +#define U32TO8_LE(p, v) \ + (p)[0] = (uint8_t)((v)); \ + (p)[1] = (uint8_t)((v) >> 8); \ + (p)[2] = (uint8_t)((v) >> 16); \ + (p)[3] = (uint8_t)((v) >> 24); + +#define U64TO8_LE(p, v) \ + U32TO8_LE((p), (uint32_t)((v))); \ + U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); + +#ifdef UNALIGNED_LE_CPU +#define U8TO64_LE(p) (*((uint64_t*)(p))) +#else +#define U8TO64_LE(p) \ + (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \ + ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \ + ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \ + ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56)) +#endif + +#define U8TO64_LE_NOCASE(p) \ + (((uint64_t)(siptlw((p)[0]))) | \ + ((uint64_t)(siptlw((p)[1])) << 8) | \ + ((uint64_t)(siptlw((p)[2])) << 16) | \ + ((uint64_t)(siptlw((p)[3])) << 24) | \ + ((uint64_t)(siptlw((p)[4])) << 32) | \ + ((uint64_t)(siptlw((p)[5])) << 40) | \ + ((uint64_t)(siptlw((p)[6])) << 48) | \ + ((uint64_t)(siptlw((p)[7])) << 56)) + +#define SIPROUND \ + do { \ + v0 += v1; \ + v1 = ROTL(v1, 13); \ + v1 ^= v0; \ + v0 = ROTL(v0, 32); \ + v2 += v3; \ + v3 = ROTL(v3, 16); \ + v3 ^= v2; \ + v0 += v3; \ + v3 = ROTL(v3, 21); \ + v3 ^= v0; \ + v2 += v1; \ + v1 = ROTL(v1, 17); \ + v1 ^= v2; \ + v2 = ROTL(v2, 32); \ + } while (0) + +uint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k) { +#ifndef UNALIGNED_LE_CPU + uint64_t hash; + uint8_t *out = (uint8_t*) &hash; +#endif + uint64_t v0 = 0x736f6d6570736575ULL; + uint64_t v1 = 0x646f72616e646f6dULL; + uint64_t v2 = 0x6c7967656e657261ULL; + uint64_t v3 = 0x7465646279746573ULL; + uint64_t k0 = U8TO64_LE(k); + uint64_t k1 = U8TO64_LE(k + 8); + uint64_t m; + const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t)); + const int left = inlen & 7; + uint64_t b = ((uint64_t)inlen) << 56; + v3 ^= k1; + v2 ^= k0; + v1 ^= k1; + v0 ^= k0; + + for (; in != end; in += 8) { + m = U8TO64_LE(in); + v3 ^= m; + + SIPROUND; + + v0 ^= m; + } + + switch (left) { + case 7: b |= ((uint64_t)in[6]) << 48; + case 6: b |= ((uint64_t)in[5]) << 40; + case 5: b |= ((uint64_t)in[4]) << 32; + case 4: b |= ((uint64_t)in[3]) << 24; + case 3: b |= ((uint64_t)in[2]) << 16; + case 2: b |= ((uint64_t)in[1]) << 8; + case 1: b |= ((uint64_t)in[0]); break; + case 0: break; + } + + v3 ^= b; + + SIPROUND; + + v0 ^= b; + v2 ^= 0xff; + + SIPROUND; + SIPROUND; + + b = v0 ^ v1 ^ v2 ^ v3; +#ifndef UNALIGNED_LE_CPU + U64TO8_LE(out, b); + return hash; +#else + return b; +#endif +} + +uint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k) +{ +#ifndef UNALIGNED_LE_CPU + uint64_t hash; + uint8_t *out = (uint8_t*) &hash; +#endif + uint64_t v0 = 0x736f6d6570736575ULL; + uint64_t v1 = 0x646f72616e646f6dULL; + uint64_t v2 = 0x6c7967656e657261ULL; + uint64_t v3 = 0x7465646279746573ULL; + uint64_t k0 = U8TO64_LE(k); + uint64_t k1 = U8TO64_LE(k + 8); + uint64_t m; + const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t)); + const int left = inlen & 7; + uint64_t b = ((uint64_t)inlen) << 56; + v3 ^= k1; + v2 ^= k0; + v1 ^= k1; + v0 ^= k0; + + for (; in != end; in += 8) { + m = U8TO64_LE_NOCASE(in); + v3 ^= m; + + SIPROUND; + + v0 ^= m; + } + + switch (left) { + case 7: b |= ((uint64_t)siptlw(in[6])) << 48; + case 6: b |= ((uint64_t)siptlw(in[5])) << 40; + case 5: b |= ((uint64_t)siptlw(in[4])) << 32; + case 4: b |= ((uint64_t)siptlw(in[3])) << 24; + case 3: b |= ((uint64_t)siptlw(in[2])) << 16; + case 2: b |= ((uint64_t)siptlw(in[1])) << 8; + case 1: b |= ((uint64_t)siptlw(in[0])); break; + case 0: break; + } + + v3 ^= b; + + SIPROUND; + + v0 ^= b; + v2 ^= 0xff; + + SIPROUND; + SIPROUND; + + b = v0 ^ v1 ^ v2 ^ v3; +#ifndef UNALIGNED_LE_CPU + U64TO8_LE(out, b); + return hash; +#else + return b; +#endif +} + + +/* --------------------------------- TEST ------------------------------------ */ + +#ifdef SIPHASH_TEST + +const uint8_t vectors_sip64[64][8] = { + { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, }, + { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, }, + { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, }, + { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, }, + { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, }, + { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, }, + { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, }, + { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, }, + { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, }, + { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, }, + { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, }, + { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, }, + { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, }, + { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, }, + { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, }, + { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, }, + { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, }, + { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, }, + { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, }, + { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, }, + { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, }, + { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, }, + { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, }, + { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, }, + { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, }, + { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, }, + { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, }, + { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, }, + { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, }, + { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, }, + { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, }, + { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, }, + { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, }, + { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, }, + { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, }, + { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, }, + { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, }, + { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, }, + { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, }, + { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, }, + { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, }, + { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, }, + { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, }, + { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, }, + { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, }, + { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, }, + { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, }, + { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, }, + { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, }, + { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, }, + { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, }, + { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, }, + { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, }, + { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, }, + { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, }, + { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, }, + { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, }, + { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, }, + { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, }, + { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, }, + { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, }, + { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, }, + { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, }, + { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, }, +}; + + +/* Test siphash using a test vector. Returns 0 if the function passed + * all the tests, otherwise 1 is returned. + * + * IMPORTANT: The test vector is for SipHash 2-4. Before running + * the test revert back the siphash() function to 2-4 rounds since + * now it uses 1-2 rounds. */ +int siphash_test(void) { + uint8_t in[64], k[16]; + int i; + int fails = 0; + + for (i = 0; i < 16; ++i) + k[i] = i; + + for (i = 0; i < 64; ++i) { + in[i] = i; + uint64_t hash = siphash(in, i, k); + const uint8_t *v = NULL; + v = (uint8_t *)vectors_sip64; + if (memcmp(&hash, v + (i * 8), 8)) { + /* printf("fail for %d bytes\n", i); */ + fails++; + } + } + + /* Run a few basic tests with the case insensitive version. */ + uint64_t h1, h2; + h1 = siphash((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678"); + h2 = siphash_nocase((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678"); + if (h1 != h2) fails++; + + h1 = siphash((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678"); + h2 = siphash_nocase((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678"); + if (h1 != h2) fails++; + + h1 = siphash((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678"); + h2 = siphash_nocase((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678"); + if (h1 == h2) fails++; + + if (!fails) return 0; + return 1; +} + +int main(void) { + if (siphash_test() == 0) { + printf("SipHash test: OK\n"); + return 0; + } else { + printf("SipHash test: FAILED\n"); + return 1; + } +} + +#endif diff --git a/src/t_zset.c b/src/t_zset.c index d36fa30ae1053782ea6266a1df0982c843e09b58..f7f4c6eb2863609fc7cfbe78454a85a525d0fed2 100644 --- a/src/t_zset.c +++ b/src/t_zset.c @@ -2110,7 +2110,7 @@ inline static void zunionInterAggregate(double *target, double val, int aggregat } } -unsigned int dictSdsHash(const void *key); +uint64_t dictSdsHash(const void *key); int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2); dictType setAccumulatorDictType = {