mirror of
https://github.com/Luzifer/scs-extract.git
synced 2024-11-08 20:10:01 +00:00
190 lines
4.7 KiB
Go
190 lines
4.7 KiB
Go
package b0rkhash
|
|
|
|
import (
|
|
"encoding/binary"
|
|
)
|
|
|
|
// Some primes between 2^63 and 2^64 for various uses.
|
|
const (
|
|
k0 uint64 = 0xc3a5c85c97cb3127
|
|
k1 uint64 = 0xb492b66fbe98f273
|
|
k2 uint64 = 0x9ae16a3b2f90404f
|
|
k3 uint64 = 0xc949d7c7509e6557
|
|
)
|
|
|
|
func fetch32(p []byte) uint32 {
|
|
return binary.LittleEndian.Uint32(p)
|
|
}
|
|
|
|
func fetch64(p []byte) uint64 {
|
|
r := binary.LittleEndian.Uint64(p)
|
|
return r
|
|
}
|
|
|
|
// Bitwise right rotate
|
|
func rotate(val uint64, shift uint) uint64 {
|
|
// Avoid shifting by 64: doing so yields an undefined result.
|
|
if shift == 0 {
|
|
return val
|
|
}
|
|
return (val >> shift) | val<<(64-shift)
|
|
}
|
|
|
|
func rotateByAtleast1(val uint64, shift uint) uint64 {
|
|
return (val >> shift) | (val << (64 - shift))
|
|
}
|
|
|
|
func shiftMix(val uint64) uint64 {
|
|
return val ^ (val >> 47)
|
|
}
|
|
|
|
func hash128to64(x Uint128) uint64 {
|
|
const mul = uint64(0x9ddfea08eb382d69)
|
|
a := (x.Low64() ^ x.High64()) * mul
|
|
a ^= (a >> 47)
|
|
b := (x.High64() ^ a) * mul
|
|
b ^= (b >> 47)
|
|
b *= mul
|
|
return b
|
|
}
|
|
|
|
func hashLen16(u, v uint64) uint64 {
|
|
return hash128to64(Uint128{u, v})
|
|
}
|
|
|
|
func hashLen0to16(s []byte, length int) uint64 {
|
|
if length > 8 {
|
|
a := fetch64(s)
|
|
b := fetch64(s[length-8:])
|
|
return hashLen16(a, rotateByAtleast1(b+uint64(length), uint(length))) ^ b
|
|
}
|
|
|
|
if length >= 4 {
|
|
a := uint64(fetch32(s))
|
|
return hashLen16(uint64(length)+(a<<3), uint64(fetch32(s[length-4:])))
|
|
}
|
|
|
|
if length > 0 {
|
|
a := uint8(s[0])
|
|
b := uint8(s[length>>1])
|
|
c := uint8(s[length-1])
|
|
y := uint32(a) + (uint32(b) << 8)
|
|
z := uint32(length) + (uint32(c) << 2)
|
|
return shiftMix(uint64(y)*k2^uint64(z)*k3) * k2
|
|
}
|
|
|
|
return k2
|
|
}
|
|
|
|
// This probably works well for 16-byte strings as well, but is may be overkill
|
|
// in that case
|
|
func hashLen17to32(s []byte, length int) uint64 {
|
|
a := fetch64(s) * k1
|
|
b := fetch64(s[8:])
|
|
c := fetch64(s[length-8:]) * k2
|
|
d := fetch64(s[length-16:]) * k0
|
|
return hashLen16(rotate(a-b, 43)+rotate(c, 30)+d,
|
|
a+rotate(b^k3, 20)-c+uint64(length))
|
|
}
|
|
|
|
// Return a 16-byte hash for 48 bytes. Quick and dirty.
|
|
// callers do best to use "random-looking" values for a and b.
|
|
func weakHashLen32WithSeeds(w, x, y, z, a, b uint64) Uint128 {
|
|
a += w
|
|
b = rotate(b+a+z, 21)
|
|
c := a
|
|
a += x
|
|
a += y
|
|
b += rotate(a, 44)
|
|
return Uint128{a + z, b + c}
|
|
}
|
|
|
|
// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
|
|
func weakHashLen32WithSeedsByte(s []byte, a, b uint64) Uint128 {
|
|
return weakHashLen32WithSeeds(
|
|
fetch64(s),
|
|
fetch64(s[8:]),
|
|
fetch64(s[16:]),
|
|
fetch64(s[24:]),
|
|
a,
|
|
b)
|
|
}
|
|
|
|
// Return an 8-byte hash for 33 to 64 bytes.
|
|
func hashLen33to64(s []byte, length int) uint64 {
|
|
z := fetch64(s[24:])
|
|
a := fetch64(s) + (uint64(length)+fetch64(s[length-16:]))*k0
|
|
b := rotate(a+z, 52)
|
|
c := rotate(a, 37)
|
|
a += fetch64(s[8:])
|
|
c += rotate(a, 7)
|
|
a += fetch64(s[16:])
|
|
vf := a + z
|
|
vs := b + rotate(a, 31) + c
|
|
a = fetch64(s[16:]) + fetch64(s[length-32:])
|
|
z = fetch64(s[length-8:])
|
|
b = rotate(a+z, 52)
|
|
c = rotate(a, 37)
|
|
a += fetch64(s[length-24:])
|
|
c += rotate(a, 7)
|
|
a += fetch64(s[length-16:])
|
|
wf := a + z
|
|
ws := b + rotate(a, 31) + c
|
|
r := shiftMix((vf+ws)*k2 + (wf+vs)*k0)
|
|
return shiftMix(r*k0+vs) * k2
|
|
}
|
|
|
|
// CityHash64 return a 64-bit hash.
|
|
func CityHash64(s []byte) uint64 {
|
|
length := len(s)
|
|
if length <= 32 {
|
|
if length <= 16 {
|
|
return hashLen0to16(s, length)
|
|
}
|
|
return hashLen17to32(s, length)
|
|
} else if length <= 64 {
|
|
return hashLen33to64(s, length)
|
|
}
|
|
|
|
// For string over 64 bytes we hash the end first, and then as we
|
|
// loop we keep 56 bytes of state: v, w, x, y and z.
|
|
x := fetch64(s[length-40:])
|
|
y := fetch64(s[length-16:]) + fetch64(s[length-56:])
|
|
z := hashLen16(fetch64(s[length-48:])+uint64(length), fetch64(s[length-24:]))
|
|
v := weakHashLen32WithSeedsByte(s[length-64:], uint64(length), z)
|
|
w := weakHashLen32WithSeedsByte(s[length-32:], y+k1, x)
|
|
x = x*k1 + fetch64(s)
|
|
|
|
// Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
|
|
tmpLength := uint32(length)
|
|
tmpLength = uint32(tmpLength-1) & ^uint32(63)
|
|
for {
|
|
x = rotate(x+y+v.Low64()+fetch64(s[8:]), 37) * k1
|
|
y = rotate(y+v.High64()+fetch64(s[48:]), 42) * k1
|
|
x ^= w.High64()
|
|
y += v.Low64() + fetch64(s[40:])
|
|
z = rotate(z+w.Low64(), 33) * k1
|
|
v = weakHashLen32WithSeedsByte(s, v.High64()*k1, x+w.Low64())
|
|
w = weakHashLen32WithSeedsByte(s[32:], z+w.High64(), y+fetch64(s[16:]))
|
|
z, x = x, z
|
|
s = s[64:]
|
|
tmpLength -= 64
|
|
if tmpLength == 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
return hashLen16(
|
|
hashLen16(v.Low64(), w.Low64())+shiftMix(y)*k1+z,
|
|
hashLen16(v.High64(), w.High64())+x)
|
|
}
|
|
|
|
// CityHash64WithSeed return a 64-bit hash with a seed.
|
|
func CityHash64WithSeed(s []byte, seed uint64) uint64 {
|
|
return CityHash64WithSeeds(s, k2, seed)
|
|
}
|
|
|
|
// CityHash64WithSeeds return a 64-bit hash with two seeds.
|
|
func CityHash64WithSeeds(s []byte, seed0, seed1 uint64) uint64 {
|
|
return hashLen16(CityHash64(s)-seed0, seed1)
|
|
}
|