| // Copyright 2023 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package zstd |
| |
| import ( |
| "encoding/binary" |
| "math/bits" |
| ) |
| |
| const ( |
| xxhPrime64c1 = 0x9e3779b185ebca87 |
| xxhPrime64c2 = 0xc2b2ae3d27d4eb4f |
| xxhPrime64c3 = 0x165667b19e3779f9 |
| xxhPrime64c4 = 0x85ebca77c2b2ae63 |
| xxhPrime64c5 = 0x27d4eb2f165667c5 |
| ) |
| |
| // xxhash64 is the state of a xxHash-64 checksum. |
| type xxhash64 struct { |
| len uint64 // total length hashed |
| v [4]uint64 // accumulators |
| buf [32]byte // buffer |
| cnt int // number of bytes in buffer |
| } |
| |
| // reset discards the current state and prepares to compute a new hash. |
| // We assume a seed of 0 since that is what zstd uses. |
| func (xh *xxhash64) reset() { |
| xh.len = 0 |
| |
| // Separate addition for awkward constant overflow. |
| xh.v[0] = xxhPrime64c1 |
| xh.v[0] += xxhPrime64c2 |
| |
| xh.v[1] = xxhPrime64c2 |
| xh.v[2] = 0 |
| |
| // Separate negation for awkward constant overflow. |
| xh.v[3] = xxhPrime64c1 |
| xh.v[3] = -xh.v[3] |
| |
| for i := range xh.buf { |
| xh.buf[i] = 0 |
| } |
| xh.cnt = 0 |
| } |
| |
| // update adds a buffer to the has. |
| func (xh *xxhash64) update(b []byte) { |
| xh.len += uint64(len(b)) |
| |
| if xh.cnt+len(b) < len(xh.buf) { |
| copy(xh.buf[xh.cnt:], b) |
| xh.cnt += len(b) |
| return |
| } |
| |
| if xh.cnt > 0 { |
| n := copy(xh.buf[xh.cnt:], b) |
| b = b[n:] |
| xh.v[0] = xh.round(xh.v[0], binary.LittleEndian.Uint64(xh.buf[:])) |
| xh.v[1] = xh.round(xh.v[1], binary.LittleEndian.Uint64(xh.buf[8:])) |
| xh.v[2] = xh.round(xh.v[2], binary.LittleEndian.Uint64(xh.buf[16:])) |
| xh.v[3] = xh.round(xh.v[3], binary.LittleEndian.Uint64(xh.buf[24:])) |
| xh.cnt = 0 |
| } |
| |
| for len(b) >= 32 { |
| xh.v[0] = xh.round(xh.v[0], binary.LittleEndian.Uint64(b)) |
| xh.v[1] = xh.round(xh.v[1], binary.LittleEndian.Uint64(b[8:])) |
| xh.v[2] = xh.round(xh.v[2], binary.LittleEndian.Uint64(b[16:])) |
| xh.v[3] = xh.round(xh.v[3], binary.LittleEndian.Uint64(b[24:])) |
| b = b[32:] |
| } |
| |
| if len(b) > 0 { |
| copy(xh.buf[:], b) |
| xh.cnt = len(b) |
| } |
| } |
| |
| // digest returns the final hash value. |
| func (xh *xxhash64) digest() uint64 { |
| var h64 uint64 |
| if xh.len < 32 { |
| h64 = xh.v[2] + xxhPrime64c5 |
| } else { |
| h64 = bits.RotateLeft64(xh.v[0], 1) + |
| bits.RotateLeft64(xh.v[1], 7) + |
| bits.RotateLeft64(xh.v[2], 12) + |
| bits.RotateLeft64(xh.v[3], 18) |
| h64 = xh.mergeRound(h64, xh.v[0]) |
| h64 = xh.mergeRound(h64, xh.v[1]) |
| h64 = xh.mergeRound(h64, xh.v[2]) |
| h64 = xh.mergeRound(h64, xh.v[3]) |
| } |
| |
| h64 += xh.len |
| |
| len := xh.len |
| len &= 31 |
| buf := xh.buf[:] |
| for len >= 8 { |
| k1 := xh.round(0, binary.LittleEndian.Uint64(buf)) |
| buf = buf[8:] |
| h64 ^= k1 |
| h64 = bits.RotateLeft64(h64, 27)*xxhPrime64c1 + xxhPrime64c4 |
| len -= 8 |
| } |
| if len >= 4 { |
| h64 ^= uint64(binary.LittleEndian.Uint32(buf)) * xxhPrime64c1 |
| buf = buf[4:] |
| h64 = bits.RotateLeft64(h64, 23)*xxhPrime64c2 + xxhPrime64c3 |
| len -= 4 |
| } |
| for len > 0 { |
| h64 ^= uint64(buf[0]) * xxhPrime64c5 |
| buf = buf[1:] |
| h64 = bits.RotateLeft64(h64, 11) * xxhPrime64c1 |
| len-- |
| } |
| |
| h64 ^= h64 >> 33 |
| h64 *= xxhPrime64c2 |
| h64 ^= h64 >> 29 |
| h64 *= xxhPrime64c3 |
| h64 ^= h64 >> 32 |
| |
| return h64 |
| } |
| |
| // round updates a value. |
| func (xh *xxhash64) round(v, n uint64) uint64 { |
| v += n * xxhPrime64c2 |
| v = bits.RotateLeft64(v, 31) |
| v *= xxhPrime64c1 |
| return v |
| } |
| |
| // mergeRound updates a value in the final round. |
| func (xh *xxhash64) mergeRound(v, n uint64) uint64 { |
| n = xh.round(0, n) |
| v ^= n |
| v = v*xxhPrime64c1 + xxhPrime64c4 |
| return v |
| } |