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 }