This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Restore "improve and update hash algorithm configuration docs in INSTALL"
[perl5.git] / zaphod32_hash.h
CommitLineData
9d5e3f1a
YO
1#ifndef DEBUG_ZAPHOD32_HASH
2#define DEBUG_ZAPHOD32_HASH 0
3
4#if DEBUG_ZAPHOD32_HASH == 1
5#include <stdio.h>
6#define ZAPHOD32_WARN6(pat,v0,v1,v2,v3,v4,v5) printf(pat, v0, v1, v2, v3, v4, v5)
7#define ZAPHOD32_WARN5(pat,v0,v1,v2,v3,v4) printf(pat, v0, v1, v2, v3, v4)
8#define ZAPHOD32_WARN4(pat,v0,v1,v2,v3) printf(pat, v0, v1, v2, v3)
9#define ZAPHOD32_WARN3(pat,v0,v1,v2) printf(pat, v0, v1, v2)
10#define ZAPHOD32_WARN2(pat,v0,v1) printf(pat, v0, v1)
11#define NOTE3(pat,v0,v1,v2) printf(pat, v0, v1, v2)
12#elif DEBUG_ZAPHOD32_HASH == 2
13#define ZAPHOD32_WARN6(pat,v0,v1,v2,v3,v4,v5)
14#define ZAPHOD32_WARN5(pat,v0,v1,v2,v3,v4)
15#define ZAPHOD32_WARN4(pat,v0,v1,v2,v3)
16#define ZAPHOD32_WARN3(pat,v0,v1,v2)
17#define ZAPHOD32_WARN2(pat,v0,v1)
18#define NOTE3(pat,v0,v1,v2) printf(pat, v0, v1, v2)
19#else
20#define ZAPHOD32_WARN6(pat,v0,v1,v2,v3,v4,v5)
21#define ZAPHOD32_WARN5(pat,v0,v1,v2,v3,v4)
22#define ZAPHOD32_WARN4(pat,v0,v1,v2,v3)
23#define ZAPHOD32_WARN3(pat,v0,v1,v2)
24#define NOTE3(pat,v0,v1,v2)
25#define ZAPHOD32_WARN2(pat,v0,v1)
26#endif
27
28#ifndef ROTL32
29#define _ROTL_SIZED(x,r,s) ( ((x) << (r)) | ((x) >> ((s) - (r))) )
30#define _ROTR_SIZED(x,r,s) ( ((x) << ((s) - (r))) | ((x) >> (r)) )
31#define ROTL32(x,r) _ROTL_SIZED(x,r,32)
32#define ROTR32(x,r) _ROTR_SIZED(x,r,32)
33#endif
34
35#ifndef PERL_SEEN_HV_FUNC_H
36#if !defined(U64)
37 #include <stdint.h>
38 #define U64 uint64_t
39#endif
40
41#if !defined(U32)
42 #define U32 uint32_t
43#endif
44
45#if !defined(U8)
46 #define U8 unsigned char
47#endif
48
49#if !defined(U16)
50 #define U16 uint16_t
51#endif
52
53#ifndef STRLEN
54#define STRLEN int
55#endif
56#endif
57
58#ifndef ZAPHOD32_STATIC_INLINE
59#ifdef PERL_STATIC_INLINE
60#define ZAPHOD32_STATIC_INLINE PERL_STATIC_INLINE
61#else
62#define ZAPHOD32_STATIC_INLINE static inline
63#endif
64#endif
65
66#ifndef STMT_START
67#define STMT_START do
68#define STMT_END while(0)
69#endif
70
71#ifndef U8TO64_LE
72#define U8TO64_LE(ptr) (*((const U64 *)(ptr)))
73#endif
74#ifndef U8TO32_LE
75#define U8TO32_LE(ptr) (*((const U32 *)(ptr)))
76#endif
77#ifndef U8TO16_LE
78#define U8TO16_LE(ptr) (*((const U16 *)(ptr)))
79#endif
80
81/* This is two marsaglia xor-shift permutes, with a prime-multiple
82 * sandwiched inside. The end result of doing this twice with different
83 * primes is a completely avalanched v. */
84#define ZAPHOD32_SCRAMBLE32(v,prime) STMT_START { \
85 v ^= (v>>9); \
86 v ^= (v<<21); \
87 v ^= (v>>16); \
88 v *= prime; \
89 v ^= (v>>17); \
90 v ^= (v<<15); \
91 v ^= (v>>23); \
92} STMT_END
93
94#define ZAPHOD32_FINALIZE(v0,v1,v2) STMT_START { \
95 ZAPHOD32_WARN3("v0=%08x v1=%08x v2=%08x - ZAPHOD32 FINALIZE\n", \
96 (unsigned int)v0, (unsigned int)v1, (unsigned int)v2); \
97 v2 += v0; \
98 v1 -= v2; \
99 v1 = ROTL32(v1, 6); \
100 v2 ^= v1; \
101 v2 = ROTL32(v2, 28); \
102 v1 ^= v2; \
103 v0 += v1; \
104 v1 = ROTL32(v1, 24); \
105 v2 += v1; \
106 v2 = ROTL32(v2, 18) + v1; \
107 v0 ^= v2; \
108 v0 = ROTL32(v0, 20); \
109 v2 += v0; \
110 v1 ^= v2; \
111 v0 += v1; \
112 v0 = ROTL32(v0, 5); \
113 v2 += v0; \
114 v2 = ROTL32(v2, 22); \
115 v0 -= v1; \
116 v1 -= v2; \
117 v1 = ROTL32(v1, 17); \
118} STMT_END
119
120#define ZAPHOD32_MIX(v0,v1,v2,text) STMT_START { \
121 ZAPHOD32_WARN4("v0=%08x v1=%08x v2=%08x - ZAPHOD32 %s MIX\n", \
122 (unsigned int)v0,(unsigned int)v1,(unsigned int)v2, text ); \
123 v0 = ROTL32(v0,16) - v2; \
124 v1 = ROTR32(v1,13) ^ v2; \
125 v2 = ROTL32(v2,17) + v1; \
126 v0 = ROTR32(v0, 2) + v1; \
127 v1 = ROTR32(v1,17) - v0; \
128 v2 = ROTR32(v2, 7) ^ v0; \
129} STMT_END
130
131
132ZAPHOD32_STATIC_INLINE
133void zaphod32_seed_state (
134 const U8 *seed_ch,
135 U8 *state_ch
136) {
137 U32 *seed= (U32 *)seed_ch;
138 U32 *state= (U32 *)state_ch;
139
140 /* hex expansion of pi, skipping first two digits. pi= 3.2[43f6...]*/
141 /* pi value in hex from here:
142 * http://turner.faculty.swau.edu/mathematics/materialslibrary/pi/pibases.html*/
143 /* Ensure that the three state vectors are nonzero regardless of the seed. */
144 /* The idea of these two steps is to ensure that the 0 state comes from a seed
145 * utterly unlike that of the value we replace it with.*/
146 state[0]= seed[0] ^ 0x43f6a888;
147 state[1]= seed[1] ^ 0x5a308d31;
148 state[2]= seed[2] ^ 0x3198a2e0;
149 if (!state[0]) state[0] = 1;
150 if (!state[1]) state[1] = 2;
151 if (!state[2]) state[2] = 4;
152 /* these are pseduo-randomly selected primes between 2**31 and 2**32
153 * (I generated a big list and then randomly chose some from the list) */
154 ZAPHOD32_SCRAMBLE32(state[0],0x9fade23b);
155 ZAPHOD32_SCRAMBLE32(state[1],0xaa6f908d);
156 ZAPHOD32_SCRAMBLE32(state[2],0xcdf6b72d);
157 /* now that we have scrambled we do some mixing to avalanche the
158 * state bits to gether */
159 ZAPHOD32_MIX(state[0],state[1],state[2],"ZAPHOD32 SEED-STATE A 1/4");
160 ZAPHOD32_MIX(state[0],state[1],state[2],"ZAPHOD32 SEED-STATE A 2/4");
161 ZAPHOD32_MIX(state[0],state[1],state[2],"ZAPHOD32 SEED-STATE A 3/4");
162 ZAPHOD32_MIX(state[0],state[1],state[2],"ZAPHOD32 SEED-STATE A 4/4");
163
164 /* and then scramble them again with different primes */
165 ZAPHOD32_SCRAMBLE32(state[0],0xc95d22a9);
166 ZAPHOD32_SCRAMBLE32(state[1],0x8497242b);
167 ZAPHOD32_SCRAMBLE32(state[2],0x9c5cc4e9);
168 /* and one final mix */
169 ZAPHOD32_MIX(state[0],state[1],state[2],"ZAPHOD32 SEED-STATE 3/3");
170 ZAPHOD32_MIX(state[0],state[1],state[2],"ZAPHOD32 SEED-STATE 3/3");
171 ZAPHOD32_MIX(state[0],state[1],state[2],"ZAPHOD32 SEED-STATE 3/3");
172 ZAPHOD32_MIX(state[0],state[1],state[2],"ZAPHOD32 SEED-STATE 3/3");
173 ZAPHOD32_MIX(state[0],state[1],state[2],"ZAPHOD32 SEED-STATE 3/3");
174
9d5e3f1a
YO
175}
176
177ZAPHOD32_STATIC_INLINE
178U32 zaphod32_hash_with_state(
179 const U8 *state_ch,
180 const U8 *key,
181 const STRLEN key_len
182) {
183 U32 *state= (U32 *)state_ch;
184 const U8 *end;
185 U32 len = key_len;
186 U32 v0= state[0];
187 U32 v1= state[1];
188 U32 v2= state[2] ^ (0xC41A7AB1 * (key_len + 1));
189
190 ZAPHOD32_WARN4("v0=%08x v1=%08x v2=%08x ln=%08x HASH START\n",
191 (unsigned int)state[0], (unsigned int)state[1],
192 (unsigned int)state[2], (unsigned int)key_len);
193 {
194 switch (len) {
195 default: goto zaphod32_read8;
196 case 12: v2 += (U32)key[11] << 24;
197 case 11: v2 += (U32)key[10] << 16;
198 case 10: v2 += (U32)U8TO16_LE(key+8);
199 v1 -= U8TO32_LE(key+4);
200 v0 += U8TO32_LE(key+0);
201 goto zaphod32_finalize;
202 case 9: v2 += (U32)key[8];
203 case 8: v1 -= U8TO32_LE(key+4);
204 v0 += U8TO32_LE(key+0);
205 goto zaphod32_finalize;
206 case 7: v2 += (U32)key[6];
207 case 6: v0 += (U32)U8TO16_LE(key+4);
208 v1 -= U8TO32_LE(key+0);
209 goto zaphod32_finalize;
210 case 5: v0 += (U32)key[4];
211 case 4: v1 -= U8TO32_LE(key+0);
212 goto zaphod32_finalize;
213 case 3: v2 += (U32)key[2];
214 case 2: v0 += (U32)U8TO16_LE(key);
215 break;
216 case 1: v0 += (U32)key[0];
217 break;
218 case 0: v2 ^= 0xFF;
219 break;
220
221 }
222 v0 -= v2;
223 v2 = ROTL32(v2, 8) ^ v0;
224 v0 = ROTR32(v0,16) + v2;
225 v2 += v0;
226 v0 += v0 >> 9;
227 v0 += v2;
228 v2 ^= v0;
229 v2 += v2 << 4;
230 v0 -= v2;
231 v2 = ROTR32(v2, 8) ^ v0;
232 v0 = ROTL32(v0,16) ^ v2;
233 v2 = ROTL32(v2,10) + v0;
234 v0 = ROTR32(v0,30) + v2;
235 v2 = ROTR32(v2,12);
236 return v0 ^ v2;
237 }
238
239 if (len >= 8) {
240zaphod32_read8:
241 len = key_len & 0x7;
242 end = key + key_len - len;
243 do {
244 v1 -= U8TO32_LE(key+0);
245 v0 += U8TO32_LE(key+4);
246 ZAPHOD32_MIX(v0,v1,v2,"MIX 2-WORDS A");
247 key += 8;
248 } while ( key < end );
249 }
250
251 if ( len >= 4 ) {
252 v1 -= U8TO32_LE(key);
253 key += 4;
254 }
255
256 v0 += (U32)(key_len) << 24;
257 switch (len & 0x3) {
258 case 3: v2 += (U32)key[2];
259 case 2: v0 += (U32)U8TO16_LE(key);
260 break;
261 case 1: v0 += (U32)key[0];
262 break;
263 case 0: v2 ^= 0xFF;
264 }
265zaphod32_finalize:
266 ZAPHOD32_FINALIZE(v0,v1,v2);
267
268 ZAPHOD32_WARN4("v0=%08x v1=%08x v2=%08x hh=%08x - FINAL\n\n",
269 (unsigned int)v0, (unsigned int)v1, (unsigned int)v2,
270 (unsigned int)v0 ^ v1 ^ v2);
271
272 return v0 ^ v1 ^ v2;
273}
274
275ZAPHOD32_STATIC_INLINE U32 zaphod32_hash(
276 const U8 *seed_ch,
277 const U8 *key,
278 const STRLEN key_len
279) {
280 U32 state[1796];
281 zaphod32_seed_state(seed_ch,(U8*)state);
282 return zaphod32_hash_with_state((U8*)state,key,key_len);
283}
284
285#endif