| ... | ... | @@ -0,0 +1,99 @@ |
| 1 | const std = @import("std"); |
| 2 | const mem = std.mem; |
| 3 | |
| 4 | const primes = [_]u64{ |
| 5 | 0xa0761d6478bd642f, |
| 6 | 0xe7037ed1a0b428db, |
| 7 | 0x8ebc6af09c88c6e3, |
| 8 | 0x589965cc75374cc3, |
| 9 | 0x1d8e4e27c47d124f, |
| 10 | }; |
| 11 | |
| 12 | fn read_bytes(comptime bytes: u8, data: []const u8) u64 { |
| 13 | return mem.readVarInt(u64, data[0..bytes], @import("builtin").endian); |
| 14 | } |
| 15 | |
| 16 | fn read_8bytes_swapped(data: []const u8) u64 { |
| 17 | return (read_bytes(4, data) << 32 | read_bytes(4, data[4..])); |
| 18 | } |
| 19 | |
| 20 | fn mum(a: u64, b: u64) u64 { |
| 21 | var r: u128 = @intCast(u128, a) * @intCast(u128, b); |
| 22 | r = (r >> 64) ^ r; |
| 23 | return @truncate(u64, r); |
| 24 | } |
| 25 | |
| 26 | fn mix0(a: u64, b: u64, seed: u64) u64 { |
| 27 | return mum(a ^ seed ^ primes[0], b ^ seed ^ primes[1]); |
| 28 | } |
| 29 | |
| 30 | fn mix1(a: u64, b: u64, seed: u64) u64 { |
| 31 | return mum(a ^ seed ^ primes[2], b ^ seed ^ primes[3]); |
| 32 | } |
| 33 | |
| 34 | pub fn hash(key: []const u8, initial_seed: u64) u64 { |
| 35 | var seed = initial_seed; |
| 36 | |
| 37 | var i: usize = 0; |
| 38 | while (i + 32 <= key.len) : (i += 32) { |
| 39 | seed = mix0( |
| 40 | read_bytes(8, key[i..]), |
| 41 | read_bytes(8, key[i + 8 ..]), |
| 42 | seed, |
| 43 | ) ^ mix1( |
| 44 | read_bytes(8, key[i + 16 ..]), |
| 45 | read_bytes(8, key[i + 24 ..]), |
| 46 | seed, |
| 47 | ); |
| 48 | } |
| 49 | |
| 50 | const rem_len = @truncate(u5, key.len); |
| 51 | const rem_key = key[i..]; |
| 52 | seed = switch (rem_len) { |
| 53 | 0 => seed, |
| 54 | 1 => mix0(read_bytes(1, rem_key), primes[4], seed), |
| 55 | 2 => mix0(read_bytes(2, rem_key), primes[4], seed), |
| 56 | 3 => mix0((read_bytes(2, rem_key) << 8) | read_bytes(1, rem_key[2..]), primes[4], seed), |
| 57 | 4 => mix0(read_bytes(4, rem_key), primes[4], seed), |
| 58 | 5 => mix0((read_bytes(4, rem_key) << 8) | read_bytes(1, rem_key[4..]), primes[4], seed), |
| 59 | 6 => mix0((read_bytes(4, rem_key) << 16) | read_bytes(2, rem_key[4..]), primes[4], seed), |
| 60 | 7 => mix0((read_bytes(4, rem_key) << 24) | (read_bytes(2, rem_key[4..]) << 8) | read_bytes(1, rem_key[6..]), primes[4], seed), |
| 61 | 8 => mix0(read_8bytes_swapped(rem_key), primes[4], seed), |
| 62 | 9 => mix0(read_8bytes_swapped(rem_key), read_bytes(1, rem_key[8..]), seed), |
| 63 | 10 => mix0(read_8bytes_swapped(rem_key), read_bytes(2, rem_key[8..]), seed), |
| 64 | 11 => mix0(read_8bytes_swapped(rem_key), (read_bytes(2, rem_key[8..]) << 8) | read_bytes(1, rem_key[10..]), seed), |
| 65 | 12 => mix0(read_8bytes_swapped(rem_key), read_bytes(4, rem_key[8..]), seed), |
| 66 | 13 => mix0(read_8bytes_swapped(rem_key), (read_bytes(4, rem_key[8..]) << 8) | read_bytes(1, rem_key[12..]), seed), |
| 67 | 14 => mix0(read_8bytes_swapped(rem_key), (read_bytes(4, rem_key[8..]) << 16) | read_bytes(2, rem_key[12..]), seed), |
| 68 | 15 => mix0(read_8bytes_swapped(rem_key), (read_bytes(4, rem_key[8..]) << 24) | (read_bytes(2, rem_key[12..]) << 8) | read_bytes(1, rem_key[14..]), seed), |
| 69 | 16 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed), |
| 70 | 17 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_bytes(1, rem_key[16..]), primes[4], seed), |
| 71 | 18 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_bytes(2, rem_key[16..]), primes[4], seed), |
| 72 | 19 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1((read_bytes(2, rem_key[16..]) << 8) | read_bytes(1, rem_key[18..]), primes[4], seed), |
| 73 | 20 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_bytes(4, rem_key[16..]), primes[4], seed), |
| 74 | 21 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1((read_bytes(4, rem_key[16..]) << 8) | read_bytes(1, rem_key[20..]), primes[4], seed), |
| 75 | 22 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1((read_bytes(4, rem_key[16..]) << 16) | read_bytes(2, rem_key[20..]), primes[4], seed), |
| 76 | 23 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1((read_bytes(4, rem_key[16..]) << 24) | (read_bytes(2, rem_key[20..]) << 8) | read_bytes(1, rem_key[22..]), primes[4], seed), |
| 77 | 24 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), primes[4], seed), |
| 78 | 25 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), read_bytes(1, rem_key[24..]), seed), |
| 79 | 26 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), read_bytes(2, rem_key[24..]), seed), |
| 80 | 27 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), (read_bytes(2, rem_key[24..]) << 8) | read_bytes(1, rem_key[26..]), seed), |
| 81 | 28 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), read_bytes(4, rem_key[24..]), seed), |
| 82 | 29 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), (read_bytes(4, rem_key[24..]) << 8) | read_bytes(1, rem_key[28..]), seed), |
| 83 | 30 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), (read_bytes(4, rem_key[24..]) << 16) | read_bytes(2, rem_key[28..]), seed), |
| 84 | 31 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), (read_bytes(4, rem_key[24..]) << 24) | (read_bytes(2, rem_key[28..]) << 8) | read_bytes(1, rem_key[30..]), seed), |
| 85 | }; |
| 86 | |
| 87 | return mum(seed ^ key.len, primes[4]); |
| 88 | } |
| 89 | |
| 90 | test "test vectors" { |
| 91 | const expectEqual = std.testing.expectEqual; |
| 92 | expectEqual(hash("", 0), 0x0); |
| 93 | expectEqual(hash("a", 1), 0xbed235177f41d328); |
| 94 | expectEqual(hash("abc", 2), 0xbe348debe59b27c3); |
| 95 | expectEqual(hash("message digest", 3), 0x37320f657213a290); |
| 96 | expectEqual(hash("abcdefghijklmnopqrstuvwxyz", 4), 0xd0b270e1d8a7019c); |
| 97 | expectEqual(hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 5), 0x602a1894d3bbfe7f); |
| 98 | expectEqual(hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890", 6), 0x829e9c148b75970e); |
| 99 | } |