// 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 (
	
	
)

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 ( *xxhash64) () {
	.len = 0

	// Separate addition for awkward constant overflow.
	.v[0] = xxhPrime64c1
	.v[0] += xxhPrime64c2

	.v[1] = xxhPrime64c2
	.v[2] = 0

	// Separate negation for awkward constant overflow.
	.v[3] = xxhPrime64c1
	.v[3] = -.v[3]

	for  := range .buf {
		.buf[] = 0
	}
	.cnt = 0
}

// update adds a buffer to the has.
func ( *xxhash64) ( []byte) {
	.len += uint64(len())

	if .cnt+len() < len(.buf) {
		copy(.buf[.cnt:], )
		.cnt += len()
		return
	}

	if .cnt > 0 {
		 := copy(.buf[.cnt:], )
		 = [:]
		.v[0] = .round(.v[0], binary.LittleEndian.Uint64(.buf[:]))
		.v[1] = .round(.v[1], binary.LittleEndian.Uint64(.buf[8:]))
		.v[2] = .round(.v[2], binary.LittleEndian.Uint64(.buf[16:]))
		.v[3] = .round(.v[3], binary.LittleEndian.Uint64(.buf[24:]))
		.cnt = 0
	}

	for len() >= 32 {
		.v[0] = .round(.v[0], binary.LittleEndian.Uint64())
		.v[1] = .round(.v[1], binary.LittleEndian.Uint64([8:]))
		.v[2] = .round(.v[2], binary.LittleEndian.Uint64([16:]))
		.v[3] = .round(.v[3], binary.LittleEndian.Uint64([24:]))
		 = [32:]
	}

	if len() > 0 {
		copy(.buf[:], )
		.cnt = len()
	}
}

// digest returns the final hash value.
func ( *xxhash64) () uint64 {
	var  uint64
	if .len < 32 {
		 = .v[2] + xxhPrime64c5
	} else {
		 = bits.RotateLeft64(.v[0], 1) +
			bits.RotateLeft64(.v[1], 7) +
			bits.RotateLeft64(.v[2], 12) +
			bits.RotateLeft64(.v[3], 18)
		 = .mergeRound(, .v[0])
		 = .mergeRound(, .v[1])
		 = .mergeRound(, .v[2])
		 = .mergeRound(, .v[3])
	}

	 += .len

	 := .len
	 &= 31
	 := .buf[:]
	for  >= 8 {
		 := .round(0, binary.LittleEndian.Uint64())
		 = [8:]
		 ^= 
		 = bits.RotateLeft64(, 27)*xxhPrime64c1 + xxhPrime64c4
		 -= 8
	}
	if  >= 4 {
		 ^= uint64(binary.LittleEndian.Uint32()) * xxhPrime64c1
		 = [4:]
		 = bits.RotateLeft64(, 23)*xxhPrime64c2 + xxhPrime64c3
		 -= 4
	}
	for  > 0 {
		 ^= uint64([0]) * xxhPrime64c5
		 = [1:]
		 = bits.RotateLeft64(, 11) * xxhPrime64c1
		--
	}

	 ^=  >> 33
	 *= xxhPrime64c2
	 ^=  >> 29
	 *= xxhPrime64c3
	 ^=  >> 32

	return 
}

// round updates a value.
func ( *xxhash64) (,  uint64) uint64 {
	 +=  * xxhPrime64c2
	 = bits.RotateLeft64(, 31)
	 *= xxhPrime64c1
	return 
}

// mergeRound updates a value in the final round.
func ( *xxhash64) (,  uint64) uint64 {
	 = .round(0, )
	 ^= 
	 = *xxhPrime64c1 + xxhPrime64c4
	return 
}