package farm import ( "encoding/binary" "math/bits" ) func uoH(x, y, mul uint64, r uint) uint64 { a := (x ^ y) * mul a ^= (a >> 47) b := (y ^ a) * mul return bits.RotateLeft64(b, -int(r)) * mul } // Hash64WithSeeds hashes a byte slice and two uint64 seeds and returns a uint64 hash value func Hash64WithSeeds(s []byte, seed0, seed1 uint64) uint64 { slen := len(s) if slen <= 64 { return naHash64WithSeeds(s, seed0, seed1) } // For strings over 64 bytes we loop. // Internal state consists of 64 bytes: u, v, w, x, y, and z. x := seed0 y := seed1*k2 + 113 z := shiftMix(y*k2) * k2 v := uint128{seed0, seed1} var w uint128 u := x - z x *= k2 mul := k2 + (u & 0x82) // Set end so that after the loop we have 1 to 64 bytes left to process. endIdx := ((slen - 1) / 64) * 64 last64Idx := endIdx + ((slen - 1) & 63) - 63 last64 := s[last64Idx:] for len(s) > 64 { a0 := binary.LittleEndian.Uint64(s[0 : 0+8]) a1 := binary.LittleEndian.Uint64(s[8 : 8+8]) a2 := binary.LittleEndian.Uint64(s[16 : 16+8]) a3 := binary.LittleEndian.Uint64(s[24 : 24+8]) a4 := binary.LittleEndian.Uint64(s[32 : 32+8]) a5 := binary.LittleEndian.Uint64(s[40 : 40+8]) a6 := binary.LittleEndian.Uint64(s[48 : 48+8]) a7 := binary.LittleEndian.Uint64(s[56 : 56+8]) x += a0 + a1 y += a2 z += a3 v.lo += a4 v.hi += a5 + a1 w.lo += a6 w.hi += a7 x = bits.RotateLeft64(x, -26) x *= 9 y = bits.RotateLeft64(y, -29) z *= mul v.lo = bits.RotateLeft64(v.lo, -33) v.hi = bits.RotateLeft64(v.hi, -30) w.lo ^= x w.lo *= 9 z = bits.RotateLeft64(z, -32) z += w.hi w.hi += z z *= 9 u, y = y, u z += a0 + a6 v.lo += a2 v.hi += a3 w.lo += a4 w.hi += a5 + a6 x += a1 y += a7 y += v.lo v.lo += x - y v.hi += w.lo w.lo += v.hi w.hi += x - y x += w.hi w.hi = bits.RotateLeft64(w.hi, -34) u, z = z, u s = s[64:] } // Make s point to the last 64 bytes of input. s = last64 u *= 9 v.hi = bits.RotateLeft64(v.hi, -28) v.lo = bits.RotateLeft64(v.lo, -20) w.lo += (uint64(slen-1) & 63) u += y y += u x = bits.RotateLeft64(y-x+v.lo+binary.LittleEndian.Uint64(s[8:8+8]), -37) * mul y = bits.RotateLeft64(y^v.hi^binary.LittleEndian.Uint64(s[48:48+8]), -42) * mul x ^= w.hi * 9 y += v.lo + binary.LittleEndian.Uint64(s[40:40+8]) z = bits.RotateLeft64(z+w.lo, -33) * mul v.lo, v.hi = weakHashLen32WithSeeds(s, v.hi*mul, x+w.lo) w.lo, w.hi = weakHashLen32WithSeeds(s[32:], z+w.hi, y+binary.LittleEndian.Uint64(s[16:16+8])) return uoH(hashLen16Mul(v.lo+x, w.lo^y, mul)+z-u, uoH(v.hi+y, w.hi+z, k2, 30)^x, k2, 31) } // Hash64WithSeed hashes a byte slice and a uint64 seed and returns a uint64 hash value func Hash64WithSeed(s []byte, seed uint64) uint64 { if len(s) <= 64 { return naHash64WithSeed(s, seed) } return Hash64WithSeeds(s, 0, seed) } // Hash64 hashes a byte slice and returns a uint64 hash value func Hash64(s []byte) uint64 { if len(s) <= 64 { return naHash64(s) } return Hash64WithSeeds(s, 81, 0) }