68 lines
1.4 KiB
Go
Raw Normal View History

2023-07-22 17:02:22 +02:00
package hash
import (
"unsafe"
)
// Bytes hashes the given byte slice.
func Bytes(in []byte) uint64 {
return add(0, in)
}
// add implements the actual hashing.
func add(x uint64, in []byte) uint64 {
var i int
// Cache lines on modern processors are 64 bytes long.
// A single uint64 consumes 64 bits (8 bytes).
// That means we should read 8 uint64 at a time.
for ; i < len(in)-63; i += 64 {
words := (*[8]uint64)(unsafe.Pointer(&in[i]))
x += words[0]
x = (x << 1) | (x >> (64 - 1))
x += words[1]
x = (x << 1) | (x >> (64 - 1))
x += words[2]
x = (x << 1) | (x >> (64 - 1))
x += words[3]
x = (x << 1) | (x >> (64 - 1))
x += words[4]
x = (x << 1) | (x >> (64 - 1))
x += words[5]
x = (x << 1) | (x >> (64 - 1))
x += words[6]
x = (x << 1) | (x >> (64 - 1))
x += words[7]
x = (x << 1) | (x >> (64 - 1))
}
// While we have at least 8 bytes left, convert them to uint64.
for ; i < len(in)-7; i += 8 {
x += *(*uint64)(unsafe.Pointer(&in[i]))
x = (x << 1) | (x >> (64 - 1))
}
// Hash the remaining bytes.
// At this point we know that there are less than 8 bytes left,
// so we can shift each iteration by 8 bits to assure that hashes
// for tiny data buffers are always unique.
for ; i < len(in); i++ {
x += uint64(in[i])
x = (x << 8) | (x >> (64 - 8))
}
// This helps to avoid clashes between different lengths
// of all-zero bytes by making the data length significant.
x += uint64(len(in))
return x
}