xref: /freebsd/sys/contrib/ck/src/ck_ht_hash.h (revision ab00ac327a66a53edaac95b536b209db3ae2cd9f)
1 /*
2  * Copyright 2012-2015 Samy Al Bahra
3  * Copyright 2011-2014 AppNexus, Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #ifndef CK_HT_HASH_H
28 #define CK_HT_HASH_H
29 
30 /*
31  * This is the Murmur hash written by Austin Appleby.
32  */
33 
34 #include <ck_stdint.h>
35 #include <ck_string.h>
36 
37 //-----------------------------------------------------------------------------
38 // MurmurHash3 was written by Austin Appleby, and is placed in the public
39 // domain. The author hereby disclaims copyright to this source code.
40 
41 // Note - The x86 and x64 versions do _not_ produce the same results, as the
42 // algorithms are optimized for their respective platforms. You can still
43 // compile and run any of them on any platform, but your performance with the
44 // non-native version will be less than optimal.
45 
46 //-----------------------------------------------------------------------------
47 // Platform-specific functions and macros
48 
49 // Microsoft Visual Studio
50 
51 #if defined(_MSC_VER)
52 
53 #define FORCE_INLINE    __forceinline
54 
55 #include <stdlib.h>
56 
57 #define ROTL32(x,y)     _rotl(x,y)
58 #define ROTL64(x,y)     _rotl64(x,y)
59 
60 #define BIG_CONSTANT(x) (x)
61 
62 // Other compilers
63 
64 #else   // defined(_MSC_VER)
65 
66 #define FORCE_INLINE inline __attribute__((always_inline))
67 
68 static inline uint32_t rotl32 ( uint32_t x, int8_t r )
69 {
70   return (x << r) | (x >> (32 - r));
71 }
72 
73 static inline uint64_t rotl64 ( uint64_t x, int8_t r )
74 {
75   return (x << r) | (x >> (64 - r));
76 }
77 
78 #define ROTL32(x,y)     rotl32(x,y)
79 #define ROTL64(x,y)     rotl64(x,y)
80 
81 #define BIG_CONSTANT(x) (x##LLU)
82 
83 #endif // !defined(_MSC_VER)
84 
85 //-----------------------------------------------------------------------------
86 // Block read - if your platform needs to do endian-swapping or can only
87 // handle aligned reads, do the conversion here
88 
89 FORCE_INLINE static uint32_t getblock ( const uint32_t * p, int i )
90 {
91   return p[i];
92 }
93 
94 //-----------------------------------------------------------------------------
95 // Finalization mix - force all bits of a hash block to avalanche
96 
97 FORCE_INLINE static uint32_t fmix ( uint32_t h )
98 {
99   h ^= h >> 16;
100   h *= 0x85ebca6b;
101   h ^= h >> 13;
102   h *= 0xc2b2ae35;
103   h ^= h >> 16;
104 
105   return h;
106 }
107 
108 //-----------------------------------------------------------------------------
109 
110 static inline void MurmurHash3_x86_32 ( const void * key, int len,
111                           uint32_t seed, uint32_t * out )
112 {
113   const uint8_t * data = (const uint8_t*)key;
114   const int nblocks = len / 4;
115   int i;
116 
117   uint32_t h1 = seed;
118 
119   uint32_t c1 = 0xcc9e2d51;
120   uint32_t c2 = 0x1b873593;
121 
122   //----------
123   // body
124 
125   const uint32_t * blocks = (const uint32_t *)(const void *)(data + nblocks*4);
126 
127   for(i = -nblocks; i; i++)
128   {
129     uint32_t k1 = getblock(blocks,i);
130 
131     k1 *= c1;
132     k1 = ROTL32(k1,15);
133     k1 *= c2;
134 
135     h1 ^= k1;
136     h1 = ROTL32(h1,13);
137     h1 = h1*5+0xe6546b64;
138   }
139 
140   //----------
141   // tail
142 
143   const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
144 
145   uint32_t k1 = 0;
146 
147   switch(len & 3)
148   {
149   case 3: k1 ^= tail[2] << 16;
150   case 2: k1 ^= tail[1] << 8;
151   case 1: k1 ^= tail[0];
152           k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
153   };
154 
155   //----------
156   // finalization
157 
158   h1 ^= len;
159 
160   h1 = fmix(h1);
161 
162   *(uint32_t *)out = h1;
163 }
164 
165 static inline uint64_t MurmurHash64A ( const void * key, int len, uint64_t seed )
166 {
167   const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995);
168   const int r = 47;
169 
170   uint64_t h = seed ^ (len * m);
171 
172   const uint64_t * data = (const uint64_t *)key;
173   const uint64_t * end = data + (len/8);
174 
175   while(data != end)
176   {
177     uint64_t k;
178 
179     if (!((uintptr_t)data & 0x7))
180 	    k = *data++;
181     else {
182 	    memcpy(&k, data, sizeof(k));
183 	    data++;
184     }
185 
186     k *= m;
187     k ^= k >> r;
188     k *= m;
189 
190     h ^= k;
191     h *= m;
192   }
193 
194   const unsigned char * data2 = (const unsigned char*)data;
195 
196   switch(len & 7)
197   {
198   case 7: h ^= (uint64_t)(data2[6]) << 48;
199   case 6: h ^= (uint64_t)(data2[5]) << 40;
200   case 5: h ^= (uint64_t)(data2[4]) << 32;
201   case 4: h ^= (uint64_t)(data2[3]) << 24;
202   case 3: h ^= (uint64_t)(data2[2]) << 16;
203   case 2: h ^= (uint64_t)(data2[1]) << 8;
204   case 1: h ^= (uint64_t)(data2[0]);
205           h *= m;
206   };
207 
208   h ^= h >> r;
209   h *= m;
210   h ^= h >> r;
211 
212   return h;
213 }
214 
215 
216 // 64-bit hash for 32-bit platforms
217 
218 static inline uint64_t MurmurHash64B ( const void * key, int len, uint64_t seed )
219 {
220   const uint32_t m = 0x5bd1e995;
221   const int r = 24;
222 
223   uint32_t h1 = (uint32_t)(seed) ^ len;
224   uint32_t h2 = (uint32_t)(seed >> 32);
225 
226   const uint32_t * data = (const uint32_t *)key;
227 
228   while(len >= 8)
229   {
230     uint32_t k1 = *data++;
231     k1 *= m; k1 ^= k1 >> r; k1 *= m;
232     h1 *= m; h1 ^= k1;
233     len -= 4;
234 
235     uint32_t k2 = *data++;
236     k2 *= m; k2 ^= k2 >> r; k2 *= m;
237     h2 *= m; h2 ^= k2;
238     len -= 4;
239   }
240 
241   if(len >= 4)
242   {
243     uint32_t k1 = *data++;
244     k1 *= m; k1 ^= k1 >> r; k1 *= m;
245     h1 *= m; h1 ^= k1;
246     len -= 4;
247   }
248 
249   switch(len)
250   {
251   case 3: h2 ^= ((const unsigned char*)data)[2] << 16;
252   case 2: h2 ^= ((const unsigned char*)data)[1] << 8;
253   case 1: h2 ^= ((const unsigned char*)data)[0];
254       h2 *= m;
255   };
256 
257   h1 ^= h2 >> 18; h1 *= m;
258   h2 ^= h1 >> 22; h2 *= m;
259   h1 ^= h2 >> 17; h1 *= m;
260   h2 ^= h1 >> 19; h2 *= m;
261 
262   uint64_t h = h1;
263 
264   h = (h << 32) | h2;
265 
266   return h;
267 }
268 
269 #endif /* CK_HT_HASH_H */
270