1 /**
2 Computes $(LINK2 https://en.wikipedia.org/wiki/MurmurHash, MurmurHash) hashes
3 of arbitrary data. MurmurHash is a non-cryptographic hash function suitable
4 for general hash-based lookup. It is optimized for x86 but can be used on
5 all architectures.
6 The current version is MurmurHash3, which yields a 32-bit or 128-bit hash value.
7 The older MurmurHash 1 and 2 are currently not supported.
8 MurmurHash3 comes in three flavors, listed in increasing order of throughput:
9 $(UL
10 $(LI $(D MurmurHash3!32) produces a 32-bit value and is optimized for 32-bit architectures)
11 $(LI $(D MurmurHash3!(128, 32)) produces a 128-bit value and is optimized for 32-bit architectures)
12 $(LI $(D MurmurHash3!(128, 64)) produces a 128-bit value and is optimized for 64-bit architectures)
13 )
14 Note:
15 $(UL
16 $(LI $(D MurmurHash3!(128, 32)) and $(D MurmurHash3!(128, 64)) produce different values.)
17 $(LI The current implementation is optimized for little endian architectures.
18   It will exhibit different results on big endian architectures and a slightly
19   less uniform distribution.)
20 )
21 This module conforms to the APIs defined in $(D std.digest.digest).
22 This module publicly imports $(D std.digest.digest) and can be used as a stand-alone module.
23 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
24 Authors: Guillaume Chatelet
25 References: $(LINK2 https://github.com/aappleby/smhasher, Reference implementation)
26 $(BR) $(LINK2 https://en.wikipedia.org/wiki/MurmurHash, Wikipedia)
27 */
28 /* Copyright Guillaume Chatelet 2016.
29  * Distributed under the Boost Software License, Version 1.0.
30  * (See LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
31  */
32 module murmurhash;
33 
34 ///
35 @safe unittest
36 {
37     // MurmurHash3!32, MurmurHash3!(128, 32) and MurmurHash3!(128, 64) implement
38     // the std.digest.digest Template API.
39     static assert(isDigest!(MurmurHash3!32));
40     // The convenient digest template allows for quick hashing of any data.
41     ubyte[4] hashed = digest!(MurmurHash3!32)([1, 2, 3, 4]);
42 }
43 
44 ///
45 @safe unittest
46 {
47     // One can also hash ubyte data piecewise by instanciating a hasher and call
48     // the 'put' method.
49     const(ubyte)[] data1 = [1, 2, 3];
50     const(ubyte)[] data2 = [4, 5, 6, 7];
51     // The incoming data will be buffered and hashed element by element.
52     MurmurHash3!32 hasher;
53     hasher.put(data1);
54     hasher.put(data2);
55     // The call to 'finish' ensures:
56     // - the remaining bits are processed
57     // - the hash gets finalized
58     auto hashed = hasher.finish();
59 }
60 
61 ///
62 @safe unittest
63 {
64     // Using `putElements`, `putRemainder` and `finalize` you gain full
65     // control over which part of the algorithm to run.
66     // This allows for maximum throughput but needs extra care.
67 
68     // Data type must be the same as the hasher's element type:
69     // - uint for MurmurHash3!32
70     // - uint[4] for MurmurHash3!(128, 32)
71     // - ulong[2] for MurmurHash3!(128, 64)
72     const(uint)[] data = [1, 2, 3, 4];
73     // Note the hasher starts with 'Fast'.
74     MurmurHash3!32 hasher;
75     // Push as many array of elements as you need. The less calls the better.
76     hasher.putElements(data);
77     // Put remainder bytes if needed. This method can be called only once.
78     hasher.putRemainder(ubyte(1), ubyte(1), ubyte(1));
79     // Call finalize to incorporate data length in the hash.
80     hasher.finalize();
81     // Finally get the hashed value.
82     auto hashed = hasher.getBytes();
83 }
84 
85 public import std.digest.digest;
86 
87 @safe:
88 
89 /*
90 Performance notes:
91  - To help a bit with the performance when compiling with DMD some
92    functions have been rewritten to pass by value instead of by reference.
93  - GDC and LDC are on par with their C++ counterpart.
94  - DMD is typically between 20% to 50% of the GCC version.
95 */
96 
97 /++
98  + Implements the MurmurHash3 functions. You can specify the `size` of the
99  + hash in bit. For 128 bit hashes you can specify whether to optimize for 32
100  + or 64 bit architectures. If you don't specify the `opt` value it will select
101  + the fastest version of the host platform.
102  +
103  + This hasher is compatible with the `Digest` API:
104  + $(UL
105  + $(LI `void start()`)
106  + $(LI `void put(scope const(ubyte)[] data...)`)
107  + $(LI `ubyte[Element.sizeof] finish()`)
108  + )
109  +
110  + It also provides a faster, low level API working with data of size
111  + `Element.sizeof`:
112  + $(UL
113  + $(LI `void putElements(scope const(Element[]) elements...)`)
114  + $(LI `void putRemainder(scope const(ubyte[]) data...)`)
115  + $(LI `void finalize()`)
116  + $(LI `Element get()`)
117  + $(LI `ubyte[Element.sizeof] getBytes()`)
118  + )
119  +/
120 struct MurmurHash3(uint size /* 32 or 128 */ , uint opt = size_t.sizeof == 8 ? 64 : 32)
121 {
122     enum blockSize = size; // Number of bits of the hashed value.
123     size_t element_count; // The number of full elements pushed, this is used for finalization.
124 
125     static if (size == 32)
126     {
127         private enum uint c1 = 0xcc9e2d51;
128         private enum uint c2 = 0x1b873593;
129         private uint h1;
130         alias Element = uint; /// The element type for 32-bit implementation.
131 
132         this(uint seed)
133         {
134             h1 = seed;
135         }
136         /++
137         Adds a single Element of data without increasing `element_count`.
138         Make sure to increase `element_count` by `Element.sizeof` for each call to `putElement`.
139         +/
140         void putElement(uint block) pure nothrow @nogc
141         {
142             h1 = update(h1, block, 0, c1, c2, 15, 13, 0xe6546b64U);
143         }
144 
145         /// Put remainder bytes. This must be called only once after `putElement` and before `finalize`.
146         void putRemainder(scope const(ubyte[]) data...) pure nothrow @nogc
147         {
148             assert(data.length < Element.sizeof);
149             assert(data.length >= 0);
150             element_count += data.length;
151             uint k1 = 0;
152             final switch (data.length & 3)
153             {
154             case 3:
155                 k1 ^= data[2] << 16;
156                 goto case;
157             case 2:
158                 k1 ^= data[1] << 8;
159                 goto case;
160             case 1:
161                 k1 ^= data[0];
162                 h1 ^= shuffle(k1, c1, c2, 15);
163                 goto case;
164             case 0:
165             }
166         }
167 
168         /// Incorporate `element_count` and finalizes the hash.
169         void finalize() pure nothrow @nogc
170         {
171             h1 ^= element_count;
172             h1 = fmix(h1);
173         }
174 
175         /// Returns the hash as an uint value.
176         Element get() pure nothrow @nogc
177         {
178             return h1;
179         }
180 
181         /// Returns the current hashed value as an ubyte array.
182         ubyte[4] getBytes() pure nothrow @nogc
183         {
184             return cast(typeof(return)) cast(uint[1])[get()];
185         }
186     }
187     else static if (size == 128 && opt == 32)
188     {
189         private enum uint c1 = 0x239b961b;
190         private enum uint c2 = 0xab0e9789;
191         private enum uint c3 = 0x38b34ae5;
192         private enum uint c4 = 0xa1e38b93;
193         private uint h4, h3, h2, h1;
194 
195         alias Element = uint[4]; /// The element type for 128-bit implementation.
196 
197         this(uint seed4, uint seed3, uint seed2, uint seed1) pure nothrow @nogc
198         {
199             h4 = seed4;
200             h3 = seed3;
201             h2 = seed2;
202             h1 = seed1;
203         }
204 
205         this(uint seed) pure nothrow @nogc
206         {
207             h4 = h3 = h2 = h1 = seed;
208         }
209 
210         /++
211         Adds a single Element of data without increasing element_count.
212         Make sure to increase `element_count` by `Element.sizeof` for each call to `putElement`.
213         +/
214         void putElement(Element block) pure nothrow @nogc
215         {
216             h1 = update(h1, block[0], h2, c1, c2, 15, 19, 0x561ccd1bU);
217             h2 = update(h2, block[1], h3, c2, c3, 16, 17, 0x0bcaa747U);
218             h3 = update(h3, block[2], h4, c3, c4, 17, 15, 0x96cd1c35U);
219             h4 = update(h4, block[3], h1, c4, c1, 18, 13, 0x32ac3b17U);
220         }
221 
222         /// Put remainder bytes. This must be called only once after `putElement` and before `finalize`.
223         void putRemainder(scope const(ubyte[]) data...) pure nothrow @nogc
224         {
225             assert(data.length < Element.sizeof);
226             assert(data.length >= 0);
227             element_count += data.length;
228             uint k1 = 0;
229             uint k2 = 0;
230             uint k3 = 0;
231             uint k4 = 0;
232 
233             final switch (data.length & 15)
234             {
235             case 15:
236                 k4 ^= data[14] << 16;
237                 goto case;
238             case 14:
239                 k4 ^= data[13] << 8;
240                 goto case;
241             case 13:
242                 k4 ^= data[12] << 0;
243                 h4 ^= shuffle(k4, c4, c1, 18);
244                 goto case;
245             case 12:
246                 k3 ^= data[11] << 24;
247                 goto case;
248             case 11:
249                 k3 ^= data[10] << 16;
250                 goto case;
251             case 10:
252                 k3 ^= data[9] << 8;
253                 goto case;
254             case 9:
255                 k3 ^= data[8] << 0;
256                 h3 ^= shuffle(k3, c3, c4, 17);
257                 goto case;
258             case 8:
259                 k2 ^= data[7] << 24;
260                 goto case;
261             case 7:
262                 k2 ^= data[6] << 16;
263                 goto case;
264             case 6:
265                 k2 ^= data[5] << 8;
266                 goto case;
267             case 5:
268                 k2 ^= data[4] << 0;
269                 h2 ^= shuffle(k2, c2, c3, 16);
270                 goto case;
271             case 4:
272                 k1 ^= data[3] << 24;
273                 goto case;
274             case 3:
275                 k1 ^= data[2] << 16;
276                 goto case;
277             case 2:
278                 k1 ^= data[1] << 8;
279                 goto case;
280             case 1:
281                 k1 ^= data[0] << 0;
282                 h1 ^= shuffle(k1, c1, c2, 15);
283                 goto case;
284             case 0:
285             }
286         }
287 
288         /// Incorporate `element_count` and finalizes the hash.
289         void finalize() pure nothrow @nogc
290         {
291             h1 ^= element_count;
292             h2 ^= element_count;
293             h3 ^= element_count;
294             h4 ^= element_count;
295 
296             h1 += h2;
297             h1 += h3;
298             h1 += h4;
299             h2 += h1;
300             h3 += h1;
301             h4 += h1;
302 
303             h1 = fmix(h1);
304             h2 = fmix(h2);
305             h3 = fmix(h3);
306             h4 = fmix(h4);
307 
308             h1 += h2;
309             h1 += h3;
310             h1 += h4;
311             h2 += h1;
312             h3 += h1;
313             h4 += h1;
314         }
315 
316         /// Returns the hash as an uint[4] value.
317         Element get() pure nothrow @nogc
318         {
319             return [h1, h2, h3, h4];
320         }
321 
322         /// Returns the current hashed value as an ubyte array.
323         ubyte[16] getBytes() pure nothrow @nogc
324         {
325             return cast(typeof(return)) get();
326         }
327     }
328     else static if (size == 128 && opt == 64)
329     {
330         private enum ulong c1 = 0x87c37b91114253d5;
331         private enum ulong c2 = 0x4cf5ad432745937f;
332         private ulong h2, h1;
333 
334         alias Element = ulong[2]; /// The element type for 128-bit implementation.
335 
336         this(ulong seed) pure nothrow @nogc
337         {
338             h2 = h1 = seed;
339         }
340 
341         this(ulong seed2, ulong seed1) pure nothrow @nogc
342         {
343             h2 = seed2;
344             h1 = seed1;
345         }
346 
347         /++
348         Adds a single Element of data without increasing `element_count`.
349         Make sure to increase `element_count` by `Element.sizeof` for each call to `putElement`.
350         +/
351         void putElement(Element block) pure nothrow @nogc
352         {
353             h1 = update(h1, block[0], h2, c1, c2, 31, 27, 0x52dce729U);
354             h2 = update(h2, block[1], h1, c2, c1, 33, 31, 0x38495ab5U);
355         }
356 
357         /// Put remainder bytes. This must be called only once after `putElement` and before `finalize`.
358         void putRemainder(scope const(ubyte[]) data...) pure nothrow @nogc
359         {
360             assert(data.length < Element.sizeof);
361             assert(data.length >= 0);
362             element_count += data.length;
363             ulong k1 = 0;
364             ulong k2 = 0;
365             final switch (data.length & 15)
366             {
367             case 15:
368                 k2 ^= ulong(data[14]) << 48;
369                 goto case;
370             case 14:
371                 k2 ^= ulong(data[13]) << 40;
372                 goto case;
373             case 13:
374                 k2 ^= ulong(data[12]) << 32;
375                 goto case;
376             case 12:
377                 k2 ^= ulong(data[11]) << 24;
378                 goto case;
379             case 11:
380                 k2 ^= ulong(data[10]) << 16;
381                 goto case;
382             case 10:
383                 k2 ^= ulong(data[9]) << 8;
384                 goto case;
385             case 9:
386                 k2 ^= ulong(data[8]) << 0;
387                 h2 ^= shuffle(k2, c2, c1, 33);
388                 goto case;
389             case 8:
390                 k1 ^= ulong(data[7]) << 56;
391                 goto case;
392             case 7:
393                 k1 ^= ulong(data[6]) << 48;
394                 goto case;
395             case 6:
396                 k1 ^= ulong(data[5]) << 40;
397                 goto case;
398             case 5:
399                 k1 ^= ulong(data[4]) << 32;
400                 goto case;
401             case 4:
402                 k1 ^= ulong(data[3]) << 24;
403                 goto case;
404             case 3:
405                 k1 ^= ulong(data[2]) << 16;
406                 goto case;
407             case 2:
408                 k1 ^= ulong(data[1]) << 8;
409                 goto case;
410             case 1:
411                 k1 ^= ulong(data[0]) << 0;
412                 h1 ^= shuffle(k1, c1, c2, 31);
413                 goto case;
414             case 0:
415             }
416         }
417 
418         /// Incorporate `element_count` and finalizes the hash.
419         void finalize() pure nothrow @nogc
420         {
421             h1 ^= element_count;
422             h2 ^= element_count;
423 
424             h1 += h2;
425             h2 += h1;
426             h1 = fmix(h1);
427             h2 = fmix(h2);
428             h1 += h2;
429             h2 += h1;
430         }
431 
432         /// Returns the hash as an ulong[2] value.
433         Element get() pure nothrow @nogc
434         {
435             return [h1, h2];
436         }
437 
438         /// Returns the current hashed value as an ubyte array.
439         ubyte[16] getBytes() pure nothrow @nogc
440         {
441             return cast(typeof(return)) get();
442         }
443     }
444     else
445     {
446         alias Element = char; // This is needed to trigger the following error message.
447         static assert(false, "MurmurHash3(" ~ size.stringof ~ ", " ~ opt.stringof ~ ") is not implemented");
448     }
449 
450     /++
451     Pushes an array of elements at once. It is more efficient to push as much data as possible in a single call.
452     On platforms that do not support unaligned reads (MIPS or old ARM chips), the compiler may produce slower code to ensure correctness.
453     +/
454     void putElements(scope const(Element[]) elements...) pure nothrow @nogc
455     {
456         foreach (const block; elements)
457         {
458             putElement(block);
459         }
460         element_count += elements.length * Element.sizeof;
461     }
462 
463     //-------------------------------------------------------------------------
464     // Implementation of the Digest API.
465     //-------------------------------------------------------------------------
466 
467     private union BufferUnion
468     {
469         Element block;
470         ubyte[Element.sizeof] data;
471     }
472 
473     private BufferUnion buffer;
474     private size_t bufferSize;
475 
476     @disable this(this);
477 
478     // Initialize
479     void start()
480     {
481         this = this.init;
482     }
483 
484     /++
485     Adds data to the digester. This function can be called many times in a row
486     after start but before finish.
487     +/
488     void put(scope const(ubyte)[] data...) pure nothrow
489     {
490         // Buffer should never be full while entering this function.
491         assert(bufferSize < Element.sizeof);
492 
493         // Check if we have some leftover data in the buffer. Then fill the first block buffer.
494         if (bufferSize + data.length < Element.sizeof)
495         {
496             buffer.data[bufferSize .. bufferSize + data.length] = data[];
497             bufferSize += data.length;
498             return;
499         }
500         const bufferLeeway = Element.sizeof - bufferSize;
501         assert(bufferLeeway <= Element.sizeof);
502         buffer.data[bufferSize .. $] = data[0 .. bufferLeeway];
503         putElement(buffer.block);
504         data = data[bufferLeeway .. $];
505 
506         // Do main work: process chunks of `Element.sizeof` bytes.
507         const numElements = data.length / Element.sizeof;
508         const remainderStart = numElements * Element.sizeof;
509         foreach (ref const Element block; cast(const(Element[]))(data[0 .. remainderStart]))
510         {
511             putElement(block);
512         }
513         // +1 for bufferLeeway Element.
514         element_count += (numElements + 1) * Element.sizeof;
515         data = data[remainderStart .. $];
516 
517         // Now add remaining data to buffer.
518         assert(data.length < Element.sizeof);
519         bufferSize = data.length;
520         buffer.data[0 .. data.length] = data[];
521     }
522 
523     /++
524     Finalizes the computation of the hash and returns the computed value.
525     Note that $(D finish) can be called only once and that no subsequent calls
526     to $(D put) is allowed.
527     +/
528     ubyte[Element.sizeof] finish() pure nothrow
529     {
530         auto tail = buffer.data[0 .. bufferSize];
531         if (tail.length > 0)
532         {
533             putRemainder(tail);
534         }
535         finalize();
536         return getBytes();
537     }
538 
539     //-------------------------------------------------------------------------
540     // MurmurHash3 utils
541     //-------------------------------------------------------------------------
542 
543     private T rotl(T)(T x, uint y)
544     in
545     {
546         import std.traits : isUnsigned;
547 
548         static assert(isUnsigned!T);
549         debug assert(y >= 0 && y <= (T.sizeof * 8));
550     }
551     body
552     {
553         return ((x << y) | (x >> ((T.sizeof * 8) - y)));
554     }
555 
556     private T shuffle(T)(T k, T c1, T c2, ubyte r1)
557     {
558         import std.traits : isUnsigned;
559 
560         static assert(isUnsigned!T);
561         k *= c1;
562         k = rotl(k, r1);
563         k *= c2;
564         return k;
565     }
566 
567     private T update(T)(ref T h, T k, T mixWith, T c1, T c2, ubyte r1, ubyte r2, T n)
568     {
569         import std.traits : isUnsigned;
570 
571         static assert(isUnsigned!T);
572         h ^= shuffle(k, c1, c2, r1);
573         h = rotl(h, r2);
574         h += mixWith;
575         return h * 5 + n;
576     }
577 
578     private uint fmix(uint h) pure nothrow @nogc
579     {
580         h ^= h >> 16;
581         h *= 0x85ebca6b;
582         h ^= h >> 13;
583         h *= 0xc2b2ae35;
584         h ^= h >> 16;
585         return h;
586     }
587 
588     private ulong fmix(ulong k) pure nothrow @nogc
589     {
590         k ^= k >> 33;
591         k *= 0xff51afd7ed558ccd;
592         k ^= k >> 33;
593         k *= 0xc4ceb9fe1a85ec53;
594         k ^= k >> 33;
595         return k;
596     }
597 }
598 
599 version (unittest)
600 {
601     import std.string : representation;
602 
603     private auto hash(H, Element = H.Element)(string data)
604     {
605         H hasher;
606         immutable elements = data.length / Element.sizeof;
607         hasher.putElements(cast(const(Element)[]) data[0 .. elements * Element.sizeof]);
608         hasher.putRemainder(cast(const(ubyte)[]) data[elements * Element.sizeof .. $]);
609         hasher.finalize();
610         return hasher.getBytes();
611     }
612 
613     private void checkResult(H)(in string[string] groundtruth)
614     {
615         foreach (data, expectedHash; groundtruth)
616         {
617             assert(data.digest!H.toHexString() == expectedHash);
618             assert(data.hash!H.toHexString() == expectedHash);
619             H hasher;
620             foreach (element; data)
621             {
622                 hasher.put(element);
623             }
624             assert(hasher.finish.toHexString() == expectedHash);
625         }
626     }
627 }
628 
629 @safe unittest
630 {
631     // dfmt off
632     checkResult!(MurmurHash3!32)([
633         "" : "00000000",
634         "a" : "B269253C",
635         "ab" : "5FD7BF9B",
636         "abc" : "FA93DDB3",
637         "abcd" : "6A67ED43",
638         "abcde" : "F69A9BE8",
639         "abcdef" : "85C08161",
640         "abcdefg" : "069B3C88",
641         "abcdefgh" : "C4CCDD49",
642         "abcdefghi" : "F0061442",
643         "abcdefghij" : "91779288",
644         "abcdefghijk" : "DF253B5F",
645         "abcdefghijkl" : "273D6FA3",
646         "abcdefghijklm" : "1B1612F2",
647         "abcdefghijklmn" : "F06D52F8",
648         "abcdefghijklmno" : "D2F7099D",
649         "abcdefghijklmnop" : "ED9162E7",
650         "abcdefghijklmnopq" : "4A5E65B6",
651         "abcdefghijklmnopqr" : "94A819C2",
652         "abcdefghijklmnopqrs" : "C15BBF85",
653         "abcdefghijklmnopqrst" : "9A711CBE",
654         "abcdefghijklmnopqrstu" : "ABE7195A",
655         "abcdefghijklmnopqrstuv" : "C73CB670",
656         "abcdefghijklmnopqrstuvw" : "1C4D1EA5",
657         "abcdefghijklmnopqrstuvwx" : "3939F9B0",
658         "abcdefghijklmnopqrstuvwxy" : "1A568338",
659         "abcdefghijklmnopqrstuvwxyz" : "6D034EA3"]);
660     // dfmt on
661 }
662 
663 @safe unittest
664 {
665     // dfmt off
666     checkResult!(MurmurHash3!(128,32))([
667         "" : "00000000000000000000000000000000",
668         "a" : "3C9394A71BB056551BB056551BB05655",
669         "ab" : "DF5184151030BE251030BE251030BE25",
670         "abc" : "D1C6CD75A506B0A2A506B0A2A506B0A2",
671         "abcd" : "AACCB6962EC6AF452EC6AF452EC6AF45",
672         "abcde" : "FB2E40C5BCC5245D7701725A7701725A",
673         "abcdef" : "0AB97CE12127AFA1F9DFBEA9F9DFBEA9",
674         "abcdefg" : "D941B590DE3A86092869774A2869774A",
675         "abcdefgh" : "3611F4AE8714B1AD92806CFA92806CFA",
676         "abcdefghi" : "1C8C05AD6F590622107DD2147C4194DD",
677         "abcdefghij" : "A72ED9F50E90379A2AAA92C77FF12F69",
678         "abcdefghijk" : "DDC9C8A01E111FCA2DF1FE8257975EBD",
679         "abcdefghijkl" : "FE038573C02482F4ADDFD42753E58CD2",
680         "abcdefghijklm" : "15A23AC1ECA1AEDB66351CF470DE2CD9",
681         "abcdefghijklmn" : "8E11EC75D71F5D60F4456F944D89D4F1",
682         "abcdefghijklmno" : "691D6DEEAED51A4A5714CE84A861A7AD",
683         "abcdefghijklmnop" : "2776D29F5612B990218BCEE445BA93D1",
684         "abcdefghijklmnopq" : "D3A445046F5C51642ADC6DD99D07111D",
685         "abcdefghijklmnopqr" : "AA5493A0DA291D966A9E7128585841D9",
686         "abcdefghijklmnopqrs" : "281B6A4F9C45B9BFC3B77850930F2C20",
687         "abcdefghijklmnopqrst" : "19342546A8216DB62873B49E545DCB1F",
688         "abcdefghijklmnopqrstu" : "A6C0F30D6C738620E7B9590D2E088D99",
689         "abcdefghijklmnopqrstuv" : "A7D421D9095CDCEA393CBBA908342384",
690         "abcdefghijklmnopqrstuvw" : "C3A93D572B014949317BAD7EE809158F",
691         "abcdefghijklmnopqrstuvwx" : "802381D77956833791F87149326E4801",
692         "abcdefghijklmnopqrstuvwxy" : "0AC619A5302315755A80D74ADEFAA842",
693         "abcdefghijklmnopqrstuvwxyz" : "1306343E662F6F666E56F6172C3DE344"]);
694     // dfmt on
695 }
696 
697 @safe unittest
698 {
699     // dfmt off
700     checkResult!(MurmurHash3!(128,64))([
701         "" : "00000000000000000000000000000000",
702         "a" : "897859F6655555855A890E51483AB5E6",
703         "ab" : "2E1BED16EA118B93ADD4529B01A75EE6",
704         "abc" : "6778AD3F3F3F96B4522DCA264174A23B",
705         "abcd" : "4FCD5646D6B77BB875E87360883E00F2",
706         "abcde" : "B8BB96F491D036208CECCF4BA0EEC7C5",
707         "abcdef" : "55BFA3ACBF867DE45C842133990971B0",
708         "abcdefg" : "99E49EC09F2FCDA6B6BB55B13AA23A1C",
709         "abcdefgh" : "028CEF37B00A8ACCA14069EB600D8948",
710         "abcdefghi" : "64793CF1CFC0470533E041B7F53DB579",
711         "abcdefghij" : "998C2F770D5BC1B6C91A658CDC854DA2",
712         "abcdefghijk" : "029D78DFB8D095A871E75A45E2317CBB",
713         "abcdefghijkl" : "94E17AE6B19BF38E1C62FF7232309E1F",
714         "abcdefghijklm" : "73FAC0A78D2848167FCCE70DFF7B652E",
715         "abcdefghijklmn" : "E075C3F5A794D09124336AD2276009EE",
716         "abcdefghijklmno" : "FB2F0C895124BE8A612A969C2D8C546A",
717         "abcdefghijklmnop" : "23B74C22A33CCAC41AEB31B395D63343",
718         "abcdefghijklmnopq" : "57A6BD887F746475E40D11A19D49DAEC",
719         "abcdefghijklmnopqr" : "508A7F90EC8CF0776BC7005A29A8D471",
720         "abcdefghijklmnopqrs" : "886D9EDE23BC901574946FB62A4D8AA6",
721         "abcdefghijklmnopqrst" : "F1E237F926370B314BD016572AF40996",
722         "abcdefghijklmnopqrstu" : "3CC9FF79E268D5C9FB3C9BE9C148CCD7",
723         "abcdefghijklmnopqrstuv" : "56F8ABF430E388956DA9F4A8741FDB46",
724         "abcdefghijklmnopqrstuvw" : "8E234F9DBA0A4840FFE9541CEBB7BE83",
725         "abcdefghijklmnopqrstuvwx" : "F72CDED40F96946408F22153A3CF0F79",
726         "abcdefghijklmnopqrstuvwxy" : "0F96072FA4CBE771DBBD9E398115EEED",
727         "abcdefghijklmnopqrstuvwxyz" : "A94A6F517E9D9C7429D5A7B6899CADE9"]);
728     // dfmt on
729 }
730 
731 @safe unittest
732 {
733     // Pushing unaligned data and making sure the result is still coherent.
734     void testUnalignedHash(H)()
735     {
736         immutable ubyte[1025] data = 0xAC;
737         immutable alignedHash = digest!H(data[0 .. $ - 1]); // 0 .. 1023
738         immutable unalignedHash = digest!H(data[1 .. $]); // 1 .. 1024
739         assert(alignedHash == unalignedHash);
740     }
741 
742     testUnalignedHash!(MurmurHash3!32)();
743     testUnalignedHash!(MurmurHash3!(128, 32))();
744     testUnalignedHash!(MurmurHash3!(128, 64))();
745 }