| ... | ... | @@ -1,125 +0,0 @@ |
| 1 | | const std = @import("std"); |
| 2 | | |
| 3 | | const readInt = std.mem.readInt; |
| 4 | | const assert = std.debug.assert; |
| 5 | | const expect = std.testing.expect; |
| 6 | | const expectEqual = std.testing.expectEqual; |
| 7 | | |
| 8 | | const RAPID_SEED: u64 = 0xbdd89aa982704029; |
| 9 | | const RAPID_SECRET: [3]u64 = .{ 0x2d358dccaa6c78a5, 0x8bb84b93962eacc9, 0x4b33a62ed433d4a3 }; |
| 10 | | |
| 11 | | pub fn hash(seed: u64, input: []const u8) u64 { |
| 12 | | const sc = RAPID_SECRET; |
| 13 | | const len = input.len; |
| 14 | | var a: u64 = 0; |
| 15 | | var b: u64 = 0; |
| 16 | | var k = input; |
| 17 | | var is: [3]u64 = .{ seed, 0, 0 }; |
| 18 | | |
| 19 | | is[0] ^= mix(seed ^ sc[0], sc[1]) ^ len; |
| 20 | | |
| 21 | | if (len <= 16) { |
| 22 | | if (len >= 4) { |
| 23 | | const d: usize = ((len & 24) >> @intCast(len >> 3)); |
| 24 | | const e = len - 4; |
| 25 | | a = (r32(k) << 32) | r32(k[e..]); |
| 26 | | b = ((r32(k[d..]) << 32) | r32(k[(e - d)..])); |
| 27 | | } else if (len > 0) |
| 28 | | a = (@as(u64, k[0]) << 56) | (@as(u64, k[len >> 1]) << 32) | @as(u64, k[len - 1]); |
| 29 | | } else { |
| 30 | | var remain = len; |
| 31 | | if (len > 48) { |
| 32 | | is[1] = is[0]; |
| 33 | | is[2] = is[0]; |
| 34 | | while (remain >= 96) { |
| 35 | | inline for (0..6) |i| { |
| 36 | | const m1 = r64(k[8 * i * 2 ..]); |
| 37 | | const m2 = r64(k[8 * (i * 2 + 1) ..]); |
| 38 | | is[i % 3] = mix(m1 ^ sc[i % 3], m2 ^ is[i % 3]); |
| 39 | | } |
| 40 | | k = k[96..]; |
| 41 | | remain -= 96; |
| 42 | | } |
| 43 | | if (remain >= 48) { |
| 44 | | inline for (0..3) |i| { |
| 45 | | const m1 = r64(k[8 * i * 2 ..]); |
| 46 | | const m2 = r64(k[8 * (i * 2 + 1) ..]); |
| 47 | | is[i] = mix(m1 ^ sc[i], m2 ^ is[i]); |
| 48 | | } |
| 49 | | k = k[48..]; |
| 50 | | remain -= 48; |
| 51 | | } |
| 52 | | |
| 53 | | is[0] ^= is[1] ^ is[2]; |
| 54 | | } |
| 55 | | |
| 56 | | if (remain > 16) { |
| 57 | | is[0] = mix(r64(k) ^ sc[2], r64(k[8..]) ^ is[0] ^ sc[1]); |
| 58 | | if (remain > 32) { |
| 59 | | is[0] = mix(r64(k[16..]) ^ sc[2], r64(k[24..]) ^ is[0]); |
| 60 | | } |
| 61 | | } |
| 62 | | |
| 63 | | a = r64(input[len - 16 ..]); |
| 64 | | b = r64(input[len - 8 ..]); |
| 65 | | } |
| 66 | | |
| 67 | | a ^= sc[1]; |
| 68 | | b ^= is[0]; |
| 69 | | mum(&a, &b); |
| 70 | | return mix(a ^ sc[0] ^ len, b ^ sc[1]); |
| 71 | | } |
| 72 | | |
| 73 | | test "RapidHash.hash" { |
| 74 | | const bytes: []const u8 = "abcdefgh" ** 128; |
| 75 | | |
| 76 | | const sizes: [13]u64 = .{ 0, 1, 2, 3, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }; |
| 77 | | |
| 78 | | const outcomes: [13]u64 = .{ |
| 79 | | 0x5a6ef77074ebc84b, |
| 80 | | 0xc11328477bc0f5d1, |
| 81 | | 0x5644ac035e40d569, |
| 82 | | 0x347080fbf5fcd81, |
| 83 | | 0x56b66b8dc802bcc, |
| 84 | | 0xb6bf9055973aac7c, |
| 85 | | 0xed56d62eead1e402, |
| 86 | | 0xc19072d767da8ffb, |
| 87 | | 0x89bb40a9928a4f0d, |
| 88 | | 0xe0af7c5e7b6e29fd, |
| 89 | | 0x9a3ed35fbedfa11a, |
| 90 | | 0x4c684b2119ca19fb, |
| 91 | | 0x4b575f5bf25600d6, |
| 92 | | }; |
| 93 | | |
| 94 | | var success: bool = true; |
| 95 | | for (sizes, outcomes) |s, e| { |
| 96 | | const r = hash(RAPID_SEED, bytes[0..s]); |
| 97 | | |
| 98 | | expectEqual(e, r) catch |err| { |
| 99 | | std.debug.print("Failed on {d}: {!}\n", .{ s, err }); |
| 100 | | success = false; |
| 101 | | }; |
| 102 | | } |
| 103 | | try expect(success); |
| 104 | | } |
| 105 | | |
| 106 | | inline fn mum(a: *u64, b: *u64) void { |
| 107 | | const r = @as(u128, a.*) * b.*; |
| 108 | | a.* = @truncate(r); |
| 109 | | b.* = @truncate(r >> 64); |
| 110 | | } |
| 111 | | |
| 112 | | inline fn mix(a: u64, b: u64) u64 { |
| 113 | | var copy_a = a; |
| 114 | | var copy_b = b; |
| 115 | | mum(&copy_a, &copy_b); |
| 116 | | return copy_a ^ copy_b; |
| 117 | | } |
| 118 | | |
| 119 | | inline fn r64(p: []const u8) u64 { |
| 120 | | return readInt(u64, p[0..8], .little); |
| 121 | | } |
| 122 | | |
| 123 | | inline fn r32(p: []const u8) u64 { |
| 124 | | return readInt(u32, p[0..4], .little); |
| 125 | | } |