authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2023-10-17 11:55:01-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-10-17 13:55:01-05:00
log5c8912d7a445cbafba92913bdd364f9f02d18d87
treebc32a4199bb8d93bba434956e04b9afa8cd95a35
parent3b21c15782a509d8f2939b0531573a9dfce415e1
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

XXH3 Implementation for Zig STD (#17530)

XXH3 is the faster alternative to XXH64 which utilizes SIMD when hashing large chunks of memory and similar mixing to WyHash (64x64 -> 128 mul) for smaller inputs. Co-authored-by: Reixcon226 <87927264+Rexicon226@users.noreply.github.com> --------- Co-authored-by: kprotty <kbutcher6200@gmail.com>

3 files changed, 417 insertions(+), 3 deletions(-)

lib/std/hash.zig+1
...@@ -33,6 +33,7 @@ const wyhash = @import("hash/wyhash.zig");...@@ -33,6 +33,7 @@ const wyhash = @import("hash/wyhash.zig");
33pub const Wyhash = wyhash.Wyhash;33pub const Wyhash = wyhash.Wyhash;
3434
35const xxhash = @import("hash/xxhash.zig");35const xxhash = @import("hash/xxhash.zig");
36pub const XxHash3 = xxhash.XxHash3;
36pub const XxHash64 = xxhash.XxHash64;37pub const XxHash64 = xxhash.XxHash64;
37pub const XxHash32 = xxhash.XxHash32;38pub const XxHash32 = xxhash.XxHash32;
3839
lib/std/hash/benchmark.zig+6
...@@ -24,6 +24,12 @@ const Hash = struct {...@@ -24,6 +24,12 @@ const Hash = struct {
24};24};
2525
26const hashes = [_]Hash{26const hashes = [_]Hash{
27 Hash{
28 .ty = hash.XxHash3,
29 .name = "xxh3",
30 .init_u64 = 0,
31 .has_anytype_api = @as([]const comptime_int, &[_]comptime_int{ 8, 16, 32, 48, 64, 80, 96, 112, 128 }),
32 },
27 Hash{33 Hash{
28 .ty = hash.XxHash64,34 .ty = hash.XxHash64,
29 .name = "xxhash64",35 .name = "xxhash64",
lib/std/hash/xxhash.zig+410-3
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const mem = std.mem;3const mem = std.mem;
3const expectEqual = std.testing.expectEqual;4const expectEqual = std.testing.expectEqual;
45
...@@ -426,6 +427,361 @@ pub const XxHash32 = struct {...@@ -426,6 +427,361 @@ pub const XxHash32 = struct {
426 }427 }
427};428};
428429
430pub const XxHash3 = struct {
431 const Block = @Vector(8, u64);
432 const default_secret: [192]u8 = .{
433 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c,
434 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f,
435 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,
436 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c,
437 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3,
438 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,
439 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d,
440 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64,
441 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,
442 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e,
443 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce,
444 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,
445 };
446
447 const prime_mx1 = 0x165667919E3779F9;
448 const prime_mx2 = 0x9FB21C651E98DF25;
449
450 inline fn avalanche(mode: union(enum) { h3, h64, rrmxmx: u64 }, x0: u64) u64 {
451 switch (mode) {
452 .h3 => {
453 const x1 = (x0 ^ (x0 >> 37)) *% prime_mx1;
454 return x1 ^ (x1 >> 32);
455 },
456 .h64 => {
457 const x1 = (x0 ^ (x0 >> 33)) *% XxHash64.prime_2;
458 const x2 = (x1 ^ (x1 >> 29)) *% XxHash64.prime_3;
459 return x2 ^ (x2 >> 32);
460 },
461 .rrmxmx => |len| {
462 const x1 = (x0 ^ rotl(u64, x0, 49) ^ rotl(u64, x0, 24)) *% prime_mx2;
463 const x2 = (x1 ^ ((x1 >> 35) +% len)) *% prime_mx2;
464 return x2 ^ (x2 >> 28);
465 },
466 }
467 }
468
469 inline fn fold(a: u64, b: u64) u64 {
470 const wide: [2]u64 = @bitCast(@as(u128, a) *% b);
471 return wide[0] ^ wide[1];
472 }
473
474 inline fn swap(x: anytype) @TypeOf(x) {
475 return if (builtin.cpu.arch.endian() == .Big) @byteSwap(x) else x;
476 }
477
478 inline fn disableAutoVectorization(x: anytype) void {
479 if (!@inComptime()) asm volatile (""
480 :
481 : [x] "r" (x),
482 );
483 }
484
485 inline fn mix16(seed: u64, input: []const u8, secret: []const u8) u64 {
486 const blk: [4]u64 = @bitCast([_][16]u8{ input[0..16].*, secret[0..16].* });
487 disableAutoVectorization(seed);
488
489 return fold(
490 swap(blk[0]) ^ (swap(blk[2]) +% seed),
491 swap(blk[1]) ^ (swap(blk[3]) -% seed),
492 );
493 }
494
495 const Accumulator = extern struct {
496 consumed: usize = 0,
497 seed: u64,
498 secret: [192]u8 = undefined,
499 state: Block = Block{
500 XxHash32.prime_3,
501 XxHash64.prime_1,
502 XxHash64.prime_2,
503 XxHash64.prime_3,
504 XxHash64.prime_4,
505 XxHash32.prime_2,
506 XxHash64.prime_5,
507 XxHash32.prime_1,
508 },
509
510 inline fn init(seed: u64) Accumulator {
511 var self = Accumulator{ .seed = seed };
512 for (
513 std.mem.bytesAsSlice(Block, &self.secret),
514 std.mem.bytesAsSlice(Block, &default_secret),
515 ) |*dst, src| {
516 dst.* = swap(swap(src) +% Block{
517 seed, @as(u64, 0) -% seed,
518 seed, @as(u64, 0) -% seed,
519 seed, @as(u64, 0) -% seed,
520 seed, @as(u64, 0) -% seed,
521 });
522 }
523 return self;
524 }
525
526 inline fn round(
527 noalias state: *Block,
528 noalias input_block: *align(1) const Block,
529 noalias secret_block: *align(1) const Block,
530 ) void {
531 const data = swap(input_block.*);
532 const mixed = data ^ swap(secret_block.*);
533 state.* +%= (mixed & @as(Block, @splat(0xffffffff))) *% (mixed >> @splat(32));
534 state.* +%= @shuffle(u64, data, undefined, [_]i32{ 1, 0, 3, 2, 5, 4, 7, 6 });
535 }
536
537 fn accumulate(noalias self: *Accumulator, blocks: []align(1) const Block) void {
538 const secret = std.mem.bytesAsSlice(u64, self.secret[self.consumed * 8 ..]);
539 for (blocks, secret[0..blocks.len]) |*input_block, *secret_block| {
540 @prefetch(@as([*]const u8, @ptrCast(input_block)) + 320, .{});
541 round(&self.state, input_block, @ptrCast(secret_block));
542 }
543 }
544
545 fn scramble(self: *Accumulator) void {
546 const secret_block: Block = @bitCast(self.secret[192 - @sizeOf(Block) .. 192].*);
547 self.state ^= self.state >> @splat(47);
548 self.state ^= swap(secret_block);
549 self.state *%= @as(Block, @splat(XxHash32.prime_1));
550 }
551
552 fn consume(noalias self: *Accumulator, input_blocks: []align(1) const Block) void {
553 const blocks_per_scramble = 1024 / @sizeOf(Block);
554 std.debug.assert(self.consumed <= blocks_per_scramble);
555
556 var blocks = input_blocks;
557 var blocks_until_scramble = blocks_per_scramble - self.consumed;
558 while (blocks.len >= blocks_until_scramble) {
559 self.accumulate(blocks[0..blocks_until_scramble]);
560 self.scramble();
561
562 self.consumed = 0;
563 blocks = blocks[blocks_until_scramble..];
564 blocks_until_scramble = blocks_per_scramble;
565 }
566
567 self.accumulate(blocks);
568 self.consumed += blocks.len;
569 }
570
571 fn digest(noalias self: *Accumulator, total_len: u64, noalias last_block: *align(1) const Block) u64 {
572 const secret_block = self.secret[192 - @sizeOf(Block) - 7 ..][0..@sizeOf(Block)];
573 round(&self.state, last_block, @ptrCast(secret_block));
574
575 const merge_block: Block = @bitCast(self.secret[11 .. 11 + @sizeOf(Block)].*);
576 self.state ^= swap(merge_block);
577
578 var result = XxHash64.prime_1 *% total_len;
579 inline for (0..4) |i| {
580 result +%= fold(self.state[i * 2], self.state[i * 2 + 1]);
581 }
582 return avalanche(.h3, result);
583 }
584 };
585
586 // Public API - Oneshot
587
588 pub fn hash(seed: u64, input: anytype) u64 {
589 validateType(@TypeOf(input));
590
591 const secret = &default_secret;
592 if (input.len > 240) return hashLong(seed, input);
593 if (input.len > 128) return hash240(seed, input, secret);
594 if (input.len > 16) return hash128(seed, input, secret);
595 if (input.len > 8) return hash16(seed, input, secret);
596 if (input.len > 3) return hash8(seed, input, secret);
597 if (input.len > 0) return hash3(seed, input, secret);
598
599 const flip: [2]u64 = @bitCast(secret[56..72].*);
600 const key = swap(flip[0]) ^ swap(flip[1]);
601 return avalanche(.h64, seed ^ key);
602 }
603
604 fn hash3(seed: u64, input: anytype, noalias secret: *const [192]u8) u64 {
605 @setCold(true);
606 std.debug.assert(input.len > 0 and input.len < 4);
607
608 const flip: [2]u32 = @bitCast(secret[0..8].*);
609 const blk: u32 = @bitCast([_]u8{
610 input[input.len - 1],
611 @truncate(input.len),
612 input[0],
613 input[input.len / 2],
614 });
615
616 const key = @as(u64, swap(flip[0]) ^ swap(flip[1])) +% seed;
617 return avalanche(.h64, key ^ swap(blk));
618 }
619
620 fn hash8(seed: u64, input: anytype, noalias secret: *const [192]u8) u64 {
621 @setCold(true);
622 std.debug.assert(input.len >= 4 and input.len <= 8);
623
624 const flip: [2]u64 = @bitCast(secret[8..24].*);
625 const blk: [2]u32 = @bitCast([_][4]u8{
626 input[0..4].*,
627 input[input.len - 4 ..][0..4].*,
628 });
629
630 const mixed = seed ^ (@as(u64, @byteSwap(@as(u32, @truncate(seed)))) << 32);
631 const key = (swap(flip[0]) ^ swap(flip[1])) -% mixed;
632 const combined = (@as(u64, swap(blk[0])) << 32) +% swap(blk[1]);
633 return avalanche(.{ .rrmxmx = input.len }, key ^ combined);
634 }
635
636 fn hash16(seed: u64, input: anytype, noalias secret: *const [192]u8) u64 {
637 @setCold(true);
638 std.debug.assert(input.len > 8 and input.len <= 16);
639
640 const flip: [4]u64 = @bitCast(secret[24..56].*);
641 const blk: [2]u64 = @bitCast([_][8]u8{
642 input[0..8].*,
643 input[input.len - 8 ..][0..8].*,
644 });
645
646 const lo = swap(blk[0]) ^ ((swap(flip[0]) ^ swap(flip[1])) +% seed);
647 const hi = swap(blk[1]) ^ ((swap(flip[2]) ^ swap(flip[3])) -% seed);
648 const combined = @as(u64, input.len) +% @byteSwap(lo) +% hi +% fold(lo, hi);
649 return avalanche(.h3, combined);
650 }
651
652 fn hash128(seed: u64, input: anytype, noalias secret: *const [192]u8) u64 {
653 @setCold(true);
654 std.debug.assert(input.len > 16 and input.len <= 128);
655
656 var acc = XxHash64.prime_1 *% @as(u64, input.len);
657 inline for (0..4) |i| {
658 const in_offset = 48 - (i * 16);
659 const scrt_offset = 96 - (i * 32);
660 if (input.len > scrt_offset) {
661 acc +%= mix16(seed, input[in_offset..], secret[scrt_offset..]);
662 acc +%= mix16(seed, input[input.len - (in_offset + 16) ..], secret[scrt_offset + 16 ..]);
663 }
664 }
665 return avalanche(.h3, acc);
666 }
667
668 fn hash240(seed: u64, input: anytype, noalias secret: *const [192]u8) u64 {
669 @setCold(true);
670 std.debug.assert(input.len > 128 and input.len <= 240);
671
672 var acc = XxHash64.prime_1 *% @as(u64, input.len);
673 inline for (0..8) |i| {
674 acc +%= mix16(seed, input[i * 16 ..], secret[i * 16 ..]);
675 }
676
677 var acc_end = mix16(seed, input[input.len - 16 ..], secret[136 - 17 ..]);
678 for (8..(input.len / 16)) |i| {
679 acc_end +%= mix16(seed, input[i * 16 ..], secret[((i - 8) * 16) + 3 ..]);
680 disableAutoVectorization(i);
681 }
682
683 acc = avalanche(.h3, acc) +% acc_end;
684 return avalanche(.h3, acc);
685 }
686
687 noinline fn hashLong(seed: u64, input: []const u8) u64 {
688 @setCold(true);
689 std.debug.assert(input.len >= 240);
690
691 const block_count = ((input.len - 1) / @sizeOf(Block)) * @sizeOf(Block);
692 const last_block = input[input.len - @sizeOf(Block) ..][0..@sizeOf(Block)];
693
694 var acc = Accumulator.init(seed);
695 acc.consume(std.mem.bytesAsSlice(Block, input[0..block_count]));
696 return acc.digest(input.len, @ptrCast(last_block));
697 }
698
699 // Public API - Streaming
700
701 buffered: usize = 0,
702 buffer: [256]u8 = undefined,
703 total_len: usize = 0,
704 accumulator: Accumulator,
705
706 pub fn init(seed: u64) XxHash3 {
707 return .{ .accumulator = Accumulator.init(seed) };
708 }
709
710 pub fn update(self: *XxHash3, input: anytype) void {
711 validateType(@TypeOf(input));
712
713 self.total_len += input.len;
714 std.debug.assert(self.buffered <= self.buffer.len);
715
716 // Copy the input into the buffer if we haven't filled it up yet.
717 const remaining = self.buffer.len - self.buffered;
718 if (input.len <= remaining) {
719 @memcpy(self.buffer[self.buffered..][0..input.len], input);
720 self.buffered += input.len;
721 return;
722 }
723
724 // Input will overflow the buffer. Fill up the buffer with some input and consume it.
725 var consumable: []const u8 = input;
726 if (self.buffered > 0) {
727 @memcpy(self.buffer[self.buffered..], consumable[0..remaining]);
728 consumable = consumable[remaining..];
729
730 self.accumulator.consume(std.mem.bytesAsSlice(Block, &self.buffer));
731 self.buffered = 0;
732 }
733
734 // The input isn't small enough to fit in the buffer. Consume it directly.
735 if (consumable.len > self.buffer.len) {
736 const block_count = ((consumable.len - 1) / @sizeOf(Block)) * @sizeOf(Block);
737 self.accumulator.consume(std.mem.bytesAsSlice(Block, consumable[0..block_count]));
738 consumable = consumable[block_count..];
739
740 // In case we consume all remaining input, write the last block to end of the buffer
741 // to populate the last_block_copy in final() similar to hashLong()'s last_block.
742 @memcpy(
743 self.buffer[self.buffer.len - @sizeOf(Block) .. self.buffer.len],
744 (consumable.ptr - @sizeOf(Block))[0..@sizeOf(Block)],
745 );
746 }
747
748 // Copy in any remaining input into the buffer.
749 std.debug.assert(consumable.len <= self.buffer.len);
750 @memcpy(self.buffer[0..consumable.len], consumable);
751 self.buffered = consumable.len;
752 }
753
754 pub fn final(self: *XxHash3) u64 {
755 std.debug.assert(self.buffered <= self.total_len);
756 std.debug.assert(self.buffered <= self.buffer.len);
757
758 // Use Oneshot hashing for smaller sizes as it doesn't use Accumulator like hashLong.
759 if (self.total_len <= 240) {
760 return hash(self.accumulator.seed, self.buffer[0..self.total_len]);
761 }
762
763 // Make a copy of the Accumulator state in case `self` needs to update() / be used later.
764 var accumulator_copy = self.accumulator;
765 var last_block_copy: [@sizeOf(Block)]u8 = undefined;
766
767 // Digest the last block onthe Accumulator copy.
768 return accumulator_copy.digest(self.total_len, last_block: {
769 if (self.buffered >= @sizeOf(Block)) {
770 const block_count = ((self.buffered - 1) / @sizeOf(Block)) * @sizeOf(Block);
771 accumulator_copy.consume(std.mem.bytesAsSlice(Block, self.buffer[0..block_count]));
772 break :last_block @ptrCast(self.buffer[self.buffered - @sizeOf(Block) ..][0..@sizeOf(Block)]);
773 } else {
774 const remaining = @sizeOf(Block) - self.buffered;
775 @memcpy(last_block_copy[0..remaining], self.buffer[self.buffer.len - remaining ..][0..remaining]);
776 @memcpy(last_block_copy[remaining..][0..self.buffered], self.buffer[0..self.buffered]);
777 break :last_block @ptrCast(&last_block_copy);
778 }
779 });
780 }
781};
782
783const verify = @import("verify.zig");
784
429fn validateType(comptime T: type) void {785fn validateType(comptime T: type) void {
430 comptime {786 comptime {
431 if (!((std.meta.trait.isSlice(T) or787 if (!((std.meta.trait.isSlice(T) or
...@@ -438,16 +794,67 @@ fn validateType(comptime T: type) void {...@@ -438,16 +794,67 @@ fn validateType(comptime T: type) void {
438 }794 }
439}795}
440796
441const verify = @import("verify.zig");
442
443fn testExpect(comptime H: type, seed: anytype, input: []const u8, expected: u64) !void {797fn testExpect(comptime H: type, seed: anytype, input: []const u8, expected: u64) !void {
444 try expectEqual(expected, H.hash(0, input));798 try expectEqual(expected, H.hash(seed, input));
445799
446 var hasher = H.init(seed);800 var hasher = H.init(seed);
447 hasher.update(input);801 hasher.update(input);
448 try expectEqual(expected, hasher.final());802 try expectEqual(expected, hasher.final());
449}803}
450804
805test "xxhash3" {
806 const H = XxHash3;
807 // Non-Seeded Tests
808 try testExpect(H, 0, "", 0x2d06800538d394c2);
809 try testExpect(H, 0, "a", 0xe6c632b61e964e1f);
810 try testExpect(H, 0, "abc", 0x78af5f94892f3950);
811 try testExpect(H, 0, "message", 0x0b1ca9b8977554fa);
812 try testExpect(H, 0, "message digest", 0x160d8e9329be94f9);
813 try testExpect(H, 0, "abcdefghijklmnopqrstuvwxyz", 0x810f9ca067fbb90c);
814 try testExpect(H, 0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 0x643542bb51639cb2);
815 try testExpect(H, 0, "12345678901234567890123456789012345678901234567890123456789012345678901234567890", 0x7f58aa2520c681f9);
816 try testExpect(H, 0, "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678", 0xb66ea795b5edc38c);
817 try testExpect(H, 0, "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", 0x8845e0b1b57330de);
818 try testExpect(H, 0, "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123123", 0xf031f373d63c5653);
819 try testExpect(H, 0, "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", 0xf1bf601f9d868dce);
820
821 // Seeded Tests
822 try testExpect(H, 1, "", 0x4dc5b0cc826f6703);
823 try testExpect(H, 1, "a", 0xd2f6d0996f37a720);
824 try testExpect(H, 1, "abc", 0x6b4467b443c76228);
825 try testExpect(H, 1, "message", 0x73fb1cf20d561766);
826 try testExpect(H, 1, "message digest", 0xfe71a82a70381174);
827 try testExpect(H, 1, "abcdefghijklmnopqrstuvwxyz", 0x902a2c2d016a37ba);
828 try testExpect(H, 1, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 0xbf552e540c5c6882);
829 try testExpect(H, 1, "12345678901234567890123456789012345678901234567890123456789012345678901234567890", 0xf2ca33235a6b865b);
830 try testExpect(H, 1, "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678", 0x6ef5cf958ba52c4);
831 try testExpect(H, 1, "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", 0xfbc5f9c53d21cb2f);
832 try testExpect(H, 1, "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123123", 0x48682aca3b1c5c18);
833 try testExpect(H, 1, "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", 0x3903c5437fc4e726);
834}
835
836test "xxhash3 smhasher" {
837 const Test = struct {
838 fn do() !void {
839 try expectEqual(verify.smhasher(XxHash3.hash), 0x9a636405);
840 }
841 };
842 try Test.do();
843 @setEvalBranchQuota(75000);
844 comptime try Test.do();
845}
846
847test "xxhash3 iterative api" {
848 const Test = struct {
849 fn do() !void {
850 try verify.iterativeApi(XxHash3);
851 }
852 };
853 try Test.do();
854 @setEvalBranchQuota(30000);
855 comptime try Test.do();
856}
857
451test "xxhash64" {858test "xxhash64" {
452 const H = XxHash64;859 const H = XxHash64;
453 try testExpect(H, 0, "", 0xef46db3751d8e999);860 try testExpect(H, 0, "", 0xef46db3751d8e999);