package blake2b
import (
	
	
	
)
type XOF interface {
	
	io.Writer
	
	io.Reader
	
	Clone() XOF
	
	Reset()
}
const OutputLengthUnknown = 0
const magicUnknownOutputLength = (1 << 32) - 1
const maxOutputLength = (1 << 32) * 64
func ( uint32,  []byte) (XOF, error) {
	if len() > Size {
		return nil, errKeySize
	}
	if  == magicUnknownOutputLength {
		
		return nil, errors.New("blake2b: XOF length too large")
	}
	if  == OutputLengthUnknown {
		 = magicUnknownOutputLength
	}
	 := &xof{
		d: digest{
			size:   Size,
			keyLen: len(),
		},
		length: ,
	}
	copy(.d.key[:], )
	.Reset()
	return , nil
}
type xof struct {
	d                digest
	length           uint32
	remaining        uint64
	cfg, root, block [Size]byte
	offset           int
	nodeOffset       uint32
	readMode         bool
}
func ( *xof) ( []byte) ( int,  error) {
	if .readMode {
		panic("blake2b: write to XOF after read")
	}
	return .d.Write()
}
func ( *xof) () XOF {
	 := *
	return &
}
func ( *xof) () int {
	return .d.BlockSize()
}
func ( *xof) () {
	.cfg[0] = byte(Size)
	binary.LittleEndian.PutUint32(.cfg[4:], uint32(Size)) 
	binary.LittleEndian.PutUint32(.cfg[12:], .length)    
	.cfg[17] = byte(Size)                                 
	.d.Reset()
	.d.h[1] ^= uint64(.length) << 32
	.remaining = uint64(.length)
	if .remaining == magicUnknownOutputLength {
		.remaining = maxOutputLength
	}
	.offset, .nodeOffset = 0, 0
	.readMode = false
}
func ( *xof) ( []byte) ( int,  error) {
	if !.readMode {
		.d.finalize(&.root)
		.readMode = true
	}
	if .remaining == 0 {
		return 0, io.EOF
	}
	 = len()
	if uint64() > .remaining {
		 = int(.remaining)
		 = [:]
	}
	if .offset > 0 {
		 := Size - .offset
		if  <  {
			.offset += copy(, .block[.offset:])
			.remaining -= uint64()
			return
		}
		copy(, .block[.offset:])
		 = [:]
		.offset = 0
		.remaining -= uint64()
	}
	for len() >= Size {
		binary.LittleEndian.PutUint32(.cfg[8:], .nodeOffset)
		.nodeOffset++
		.d.initConfig(&.cfg)
		.d.Write(.root[:])
		.d.finalize(&.block)
		copy(, .block[:])
		 = [Size:]
		.remaining -= uint64(Size)
	}
	if  := len();  > 0 {
		if .remaining < uint64(Size) {
			.cfg[0] = byte(.remaining)
		}
		binary.LittleEndian.PutUint32(.cfg[8:], .nodeOffset)
		.nodeOffset++
		.d.initConfig(&.cfg)
		.d.Write(.root[:])
		.d.finalize(&.block)
		.offset = copy(, .block[:])
		.remaining -= uint64()
	}
	return
}
func ( *digest) ( *[Size]byte) {
	.offset, .c[0], .c[1] = 0, 0, 0
	for  := range .h {
		.h[] = iv[] ^ binary.LittleEndian.Uint64([*8:])
	}
}