package xxh3

import (
	
	
)

// Hasher implements the hash.Hash interface
type Hasher struct {
	acc  [8]u64
	blk  u64
	len  u64
	key  ptr
	buf  [_block + _stripe]byte
	seed u64
}

var (
	_ hash.Hash   = (*Hasher)(nil)
	_ hash.Hash64 = (*Hasher)(nil)
)

// New returns a new Hasher that implements the hash.Hash interface.
func () *Hasher {
	return new(Hasher)
}

// NewSeed returns a new Hasher that implements the hash.Hash interface.
func ( uint64) *Hasher {
	var  Hasher
	.Reset()
	.seed = 
	.key = key

	// Only initiate once, not on reset.
	if  != 0 {
		.key = ptr(&[secretSize]byte{})
		initSecret(.key, )
	}
	return &
}

// Reset resets the Hash to its initial state.
func ( *Hasher) () {
	.acc = [8]u64{
		prime32_3, prime64_1, prime64_2, prime64_3,
		prime64_4, prime32_2, prime64_5, prime32_1,
	}
	.blk = 0
	.len = 0
}

// BlockSize returns the hash's underlying block size.
// The Write method will accept any amount of data, but
// it may operate more efficiently if all writes are a
// multiple of the block size.
func ( *Hasher) () int { return _stripe }

// Size returns the number of bytes Sum will return.
func ( *Hasher) () int { return 8 }

// Sum appends the current hash to b and returns the resulting slice.
// It does not change the underlying hash state.
func ( *Hasher) ( []byte) []byte {
	var  [8]byte
	binary.BigEndian.PutUint64([:], .Sum64())
	return append(, [:]...)
}

// Write adds more data to the running hash.
// It never returns an error.
func ( *Hasher) ( []byte) (int, error) {
	.update()
	return len(), nil
}

// WriteString adds more data to the running hash.
// It never returns an error.
func ( *Hasher) ( string) (int, error) {
	.updateString()
	return len(), nil
}

func ( *Hasher) ( []byte) {
	// relies on the data pointer being the first word in the string header
	.updateString(*(*string)(ptr(&)))
}

func ( *Hasher) ( string) {
	if .key == nil {
		.key = key
		.Reset()
	}

	// On first write, if more than 1 block, process without copy.
	for .len == 0 && len() > len(.buf) {
		if hasAVX2 {
			accumBlockAVX2(&.acc, *(*ptr)(ptr(&)), .key)
		} else if hasSSE2 {
			accumBlockSSE(&.acc, *(*ptr)(ptr(&)), .key)
		} else {
			accumBlockScalar(&.acc, *(*ptr)(ptr(&)), .key)
		}
		 = [_block:]
		.blk++
	}

	for len() > 0 {
		if .len < u64(len(.buf)) {
			 := copy(.buf[.len:], )
			.len += u64()
			 = [:]
			continue
		}

		if hasAVX2 {
			accumBlockAVX2(&.acc, ptr(&.buf), .key)
		} else if hasSSE2 {
			accumBlockSSE(&.acc, ptr(&.buf), .key)
		} else {
			accumBlockScalar(&.acc, ptr(&.buf), .key)
		}

		.blk++
		.len = _stripe
		copy(.buf[:_stripe], .buf[_block:])
	}
}

// Sum64 returns the 64-bit hash of the written data.
func ( *Hasher) () uint64 {
	if .key == nil {
		.key = key
		.Reset()
	}

	if .blk == 0 {
		if .seed == 0 {
			return Hash(.buf[:.len])
		}
		return HashSeed(.buf[:.len], .seed)
	}

	 := .blk*_block + .len
	 :=  * prime64_1
	 := .acc

	if .len > 0 {
		// We are only ever doing 1 block here, so no avx512.
		if hasAVX2 {
			accumAVX2(&, ptr(&.buf[0]), .key, .len)
		} else if hasSSE2 {
			accumSSE(&, ptr(&.buf[0]), .key, .len)
		} else {
			accumScalar(&, ptr(&.buf[0]), .key, .len)
		}
	}

	if .seed == 0 {
		 += mulFold64([0]^key64_011, [1]^key64_019)
		 += mulFold64([2]^key64_027, [3]^key64_035)
		 += mulFold64([4]^key64_043, [5]^key64_051)
		 += mulFold64([6]^key64_059, [7]^key64_067)
	} else {
		 := .key
		 += mulFold64([0]^readU64(, 11), [1]^readU64(, 19))
		 += mulFold64([2]^readU64(, 27), [3]^readU64(, 35))
		 += mulFold64([4]^readU64(, 43), [5]^readU64(, 51))
		 += mulFold64([6]^readU64(, 59), [7]^readU64(, 67))
	}

	 = xxh3Avalanche()

	return 
}

// Sum128 returns the 128-bit hash of the written data.
func ( *Hasher) () Uint128 {
	if .key == nil {
		.key = key
		.Reset()
	}

	if .blk == 0 {
		if .seed == 0 {
			return Hash128(.buf[:.len])
		}
		return Hash128Seed(.buf[:.len], .seed)
	}

	 := .blk*_block + .len
	 := Uint128{Lo:  * prime64_1, Hi: ^( * prime64_2)}
	 := .acc

	if .len > 0 {
		// We are only ever doing 1 block here, so no avx512.
		if hasAVX2 {
			accumAVX2(&, ptr(&.buf[0]), .key, .len)
		} else if hasSSE2 {
			accumSSE(&, ptr(&.buf[0]), .key, .len)
		} else {
			accumScalar(&, ptr(&.buf[0]), .key, .len)
		}
	}

	if .seed == 0 {
		.Lo += mulFold64([0]^key64_011, [1]^key64_019)
		.Hi += mulFold64([0]^key64_117, [1]^key64_125)

		.Lo += mulFold64([2]^key64_027, [3]^key64_035)
		.Hi += mulFold64([2]^key64_133, [3]^key64_141)

		.Lo += mulFold64([4]^key64_043, [5]^key64_051)
		.Hi += mulFold64([4]^key64_149, [5]^key64_157)

		.Lo += mulFold64([6]^key64_059, [7]^key64_067)
		.Hi += mulFold64([6]^key64_165, [7]^key64_173)
	} else {
		 := .key
		const  = 117 - 11

		.Lo += mulFold64([0]^readU64(, 11), [1]^readU64(, 19))
		.Hi += mulFold64([0]^readU64(, 11+), [1]^readU64(, 19+))

		.Lo += mulFold64([2]^readU64(, 27), [3]^readU64(, 35))
		.Hi += mulFold64([2]^readU64(, 27+), [3]^readU64(, 35+))

		.Lo += mulFold64([4]^readU64(, 43), [5]^readU64(, 51))
		.Hi += mulFold64([4]^readU64(, 43+), [5]^readU64(, 51+))

		.Lo += mulFold64([6]^readU64(, 59), [7]^readU64(, 67))
		.Hi += mulFold64([6]^readU64(, 59+), [7]^readU64(, 67+))
	}

	.Lo = xxh3Avalanche(.Lo)
	.Hi = xxh3Avalanche(.Hi)

	return 
}