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 }