75 lines
1.3 KiB
Go
75 lines
1.3 KiB
Go
package halfsiphash
|
|
|
|
import "encoding/binary"
|
|
|
|
func rotl(x uint32, b uint) uint32 {
|
|
return (x << b) | (x >> (32 - b))
|
|
}
|
|
|
|
func sipround(v0, v1, v2, v3 *uint32) {
|
|
*v0 += *v1
|
|
*v1 = rotl(*v1, 5)
|
|
*v1 ^= *v0
|
|
*v0 = rotl(*v0, 16)
|
|
*v2 += *v3
|
|
*v3 = rotl(*v3, 8)
|
|
*v3 ^= *v2
|
|
*v0 += *v3
|
|
*v3 = rotl(*v3, 7)
|
|
*v3 ^= *v0
|
|
*v2 += *v1
|
|
*v1 = rotl(*v1, 13)
|
|
*v1 ^= *v2
|
|
*v2 = rotl(*v2, 16)
|
|
}
|
|
|
|
// Sum32 computes HalfSipHash-2-4 with an 8-byte key and returns a 4-byte hash.
|
|
func Sum32(data []byte, key [8]byte) uint32 {
|
|
k0 := binary.LittleEndian.Uint32(key[0:4])
|
|
k1 := binary.LittleEndian.Uint32(key[4:8])
|
|
|
|
v0 := uint32(0) ^ k0
|
|
v1 := uint32(0) ^ k1
|
|
v2 := uint32(0x6c796765) ^ k0
|
|
v3 := uint32(0x74656462) ^ k1
|
|
|
|
// Process full 4-byte blocks.
|
|
nblocks := len(data) / 4
|
|
for i := 0; i < nblocks; i++ {
|
|
m := binary.LittleEndian.Uint32(data[i*4:])
|
|
v3 ^= m
|
|
for j := 0; j < 2; j++ {
|
|
sipround(&v0, &v1, &v2, &v3)
|
|
}
|
|
v0 ^= m
|
|
}
|
|
|
|
// Process remaining bytes.
|
|
b := uint32(len(data)) << 24
|
|
tail := data[nblocks*4:]
|
|
switch len(tail) {
|
|
case 3:
|
|
b |= uint32(tail[2]) << 16
|
|
fallthrough
|
|
case 2:
|
|
b |= uint32(tail[1]) << 8
|
|
fallthrough
|
|
case 1:
|
|
b |= uint32(tail[0])
|
|
}
|
|
|
|
v3 ^= b
|
|
for i := 0; i < 2; i++ {
|
|
sipround(&v0, &v1, &v2, &v3)
|
|
}
|
|
v0 ^= b
|
|
|
|
v2 ^= 0xff
|
|
|
|
for i := 0; i < 4; i++ {
|
|
sipround(&v0, &v1, &v2, &v3)
|
|
}
|
|
|
|
return v1 ^ v3
|
|
}
|