// Copyright 2019+ Klaus Post. All rights reserved.
// License information can be found in the LICENSE file.
// Based on work by Yann Collet, released under BSD License.

package zstd

import (
	
	
	
	
	
	rdebug 
	

	
)

// Encoder provides encoding to Zstandard.
// An Encoder can be used for either compressing a stream via the
// io.WriteCloser interface supported by the Encoder or as multiple independent
// tasks via the EncodeAll function.
// Smaller encodes are encouraged to use the EncodeAll function.
// Use NewWriter to create a new instance.
type Encoder struct {
	o        encoderOptions
	encoders chan encoder
	state    encoderState
	init     sync.Once
}

type encoder interface {
	Encode(blk *blockEnc, src []byte)
	EncodeNoHist(blk *blockEnc, src []byte)
	Block() *blockEnc
	CRC() *xxhash.Digest
	AppendCRC([]byte) []byte
	WindowSize(size int64) int32
	UseBlock(*blockEnc)
	Reset(d *dict, singleBlock bool)
}

type encoderState struct {
	w                io.Writer
	filling          []byte
	current          []byte
	previous         []byte
	encoder          encoder
	writing          *blockEnc
	err              error
	writeErr         error
	nWritten         int64
	nInput           int64
	frameContentSize int64
	headerWritten    bool
	eofWritten       bool
	fullFrameWritten bool

	// This waitgroup indicates an encode is running.
	wg sync.WaitGroup
	// This waitgroup indicates we have a block encoding/writing.
	wWg sync.WaitGroup
}

// NewWriter will create a new Zstandard encoder.
// If the encoder will be used for encoding blocks a nil writer can be used.
func ( io.Writer,  ...EOption) (*Encoder, error) {
	initPredefined()
	var  Encoder
	.o.setDefault()
	for ,  := range  {
		 := (&.o)
		if  != nil {
			return nil, 
		}
	}
	if  != nil {
		.Reset()
	}
	return &, nil
}

func ( *Encoder) () {
	if .o.concurrent == 0 {
		.o.setDefault()
	}
	.encoders = make(chan encoder, .o.concurrent)
	for  := 0;  < .o.concurrent; ++ {
		 := .o.encoder()
		.encoders <- 
	}
}

// Reset will re-initialize the writer and new writes will encode to the supplied writer
// as a new, independent stream.
func ( *Encoder) ( io.Writer) {
	 := &.state
	.wg.Wait()
	.wWg.Wait()
	if cap(.filling) == 0 {
		.filling = make([]byte, 0, .o.blockSize)
	}
	if .o.concurrent > 1 {
		if cap(.current) == 0 {
			.current = make([]byte, 0, .o.blockSize)
		}
		if cap(.previous) == 0 {
			.previous = make([]byte, 0, .o.blockSize)
		}
		.current = .current[:0]
		.previous = .previous[:0]
		if .writing == nil {
			.writing = &blockEnc{lowMem: .o.lowMem}
			.writing.init()
		}
		.writing.initNewEncode()
	}
	if .encoder == nil {
		.encoder = .o.encoder()
	}
	.filling = .filling[:0]
	.encoder.Reset(.o.dict, false)
	.headerWritten = false
	.eofWritten = false
	.fullFrameWritten = false
	.w = 
	.err = nil
	.nWritten = 0
	.nInput = 0
	.writeErr = nil
	.frameContentSize = 0
}

// ResetContentSize will reset and set a content size for the next stream.
// If the bytes written does not match the size given an error will be returned
// when calling Close().
// This is removed when Reset is called.
// Sizes <= 0 results in no content size set.
func ( *Encoder) ( io.Writer,  int64) {
	.Reset()
	if  >= 0 {
		.state.frameContentSize = 
	}
}

// Write data to the encoder.
// Input data will be buffered and as the buffer fills up
// content will be compressed and written to the output.
// When done writing, use Close to flush the remaining output
// and write CRC if requested.
func ( *Encoder) ( []byte) ( int,  error) {
	 := &.state
	if .eofWritten {
		return 0, ErrEncoderClosed
	}
	for len() > 0 {
		if len()+len(.filling) < .o.blockSize {
			if .o.crc {
				_, _ = .encoder.CRC().Write()
			}
			.filling = append(.filling, ...)
			return  + len(), nil
		}
		 := 
		if len()+len(.filling) > .o.blockSize {
			 = [:.o.blockSize-len(.filling)]
		}
		if .o.crc {
			_, _ = .encoder.CRC().Write()
		}
		.filling = append(.filling, ...)
		 = [len():]
		 += len()
		if len(.filling) < .o.blockSize {
			return , nil
		}
		 := .nextBlock(false)
		if  != nil {
			return , 
		}
		if debugAsserts && len(.filling) > 0 {
			panic(len(.filling))
		}
	}
	return , nil
}

// nextBlock will synchronize and start compressing input in e.state.filling.
// If an error has occurred during encoding it will be returned.
func ( *Encoder) ( bool) error {
	 := &.state
	// Wait for current block.
	.wg.Wait()
	if .err != nil {
		return .err
	}
	if len(.filling) > .o.blockSize {
		return fmt.Errorf("block > maxStoreBlockSize")
	}
	if !.headerWritten {
		// If we have a single block encode, do a sync compression.
		if  && len(.filling) == 0 && !.o.fullZero {
			.headerWritten = true
			.fullFrameWritten = true
			.eofWritten = true
			return nil
		}
		if  && len(.filling) > 0 {
			.current = .encodeAll(.encoder, .filling, .current[:0])
			var  int
			, .err = .w.Write(.current)
			if .err != nil {
				return .err
			}
			.nWritten += int64()
			.nInput += int64(len(.filling))
			.current = .current[:0]
			.filling = .filling[:0]
			.headerWritten = true
			.fullFrameWritten = true
			.eofWritten = true
			return nil
		}

		var  [maxHeaderSize]byte
		 := frameHeader{
			ContentSize:   uint64(.frameContentSize),
			WindowSize:    uint32(.encoder.WindowSize(.frameContentSize)),
			SingleSegment: false,
			Checksum:      .o.crc,
			DictID:        .o.dict.ID(),
		}

		 := .appendTo([:0])
		.headerWritten = true
		.wWg.Wait()
		var  int
		, .err = .w.Write()
		if .err != nil {
			return .err
		}
		.nWritten += int64()
	}
	if .eofWritten {
		// Ensure we only write it once.
		 = false
	}

	if len(.filling) == 0 {
		// Final block, but no data.
		if  {
			 := .encoder
			 := .Block()
			.reset(nil)
			.last = true
			.encodeRaw(nil)
			.wWg.Wait()
			_, .err = .w.Write(.output)
			.nWritten += int64(len(.output))
			.eofWritten = true
		}
		return .err
	}

	// SYNC:
	if .o.concurrent == 1 {
		 := .filling
		.nInput += int64(len(.filling))
		if debugEncoder {
			println("Adding sync block,", len(), "bytes, final:", )
		}
		 := .encoder
		 := .Block()
		.reset(nil)
		.Encode(, )
		.last = 
		if  {
			.eofWritten = true
		}

		.err = .encode(, .o.noEntropy, !.o.allLitEntropy)
		if .err != nil {
			return .err
		}
		_, .err = .w.Write(.output)
		.nWritten += int64(len(.output))
		.filling = .filling[:0]
		return .err
	}

	// Move blocks forward.
	.filling, .current, .previous = .previous[:0], .filling, .current
	.nInput += int64(len(.current))
	.wg.Add(1)
	if  {
		.eofWritten = true
	}
	go func( []byte) {
		if debugEncoder {
			println("Adding block,", len(), "bytes, final:", )
		}
		defer func() {
			if  := recover();  != nil {
				.err = fmt.Errorf("panic while encoding: %v", )
				rdebug.PrintStack()
			}
			.wg.Done()
		}()
		 := .encoder
		 := .Block()
		.Encode(, )
		.last = 
		// Wait for pending writes.
		.wWg.Wait()
		if .writeErr != nil {
			.err = .writeErr
			return
		}
		// Transfer encoders from previous write block.
		.swapEncoders(.writing)
		// Transfer recent offsets to next.
		.UseBlock(.writing)
		.writing = 
		.wWg.Add(1)
		go func() {
			defer func() {
				if  := recover();  != nil {
					.writeErr = fmt.Errorf("panic while encoding/writing: %v", )
					rdebug.PrintStack()
				}
				.wWg.Done()
			}()
			.writeErr = .encode(, .o.noEntropy, !.o.allLitEntropy)
			if .writeErr != nil {
				return
			}
			_, .writeErr = .w.Write(.output)
			.nWritten += int64(len(.output))
		}()
	}(.current)
	return nil
}

// ReadFrom reads data from r until EOF or error.
// The return value n is the number of bytes read.
// Any error except io.EOF encountered during the read is also returned.
//
// The Copy function uses ReaderFrom if available.
func ( *Encoder) ( io.Reader) ( int64,  error) {
	if debugEncoder {
		println("Using ReadFrom")
	}

	// Flush any current writes.
	if len(.state.filling) > 0 {
		if  := .nextBlock(false);  != nil {
			return 0, 
		}
	}
	.state.filling = .state.filling[:.o.blockSize]
	 := .state.filling
	for {
		,  := .Read()
		if .o.crc {
			_, _ = .state.encoder.CRC().Write([:])
		}
		// src is now the unfilled part...
		 = [:]
		 += int64()
		switch  {
		case io.EOF:
			.state.filling = .state.filling[:len(.state.filling)-len()]
			if debugEncoder {
				println("ReadFrom: got EOF final block:", len(.state.filling))
			}
			return , nil
		case nil:
		default:
			if debugEncoder {
				println("ReadFrom: got error:", )
			}
			.state.err = 
			return , 
		}
		if len() > 0 {
			if debugEncoder {
				println("ReadFrom: got space left in source:", len())
			}
			continue
		}
		 = .nextBlock(false)
		if  != nil {
			return , 
		}
		.state.filling = .state.filling[:.o.blockSize]
		 = .state.filling
	}
}

// Flush will send the currently written data to output
// and block until everything has been written.
// This should only be used on rare occasions where pushing the currently queued data is critical.
func ( *Encoder) () error {
	 := &.state
	if len(.filling) > 0 {
		 := .nextBlock(false)
		if  != nil {
			// Ignore Flush after Close.
			if errors.Is(.err, ErrEncoderClosed) {
				return nil
			}
			return 
		}
	}
	.wg.Wait()
	.wWg.Wait()
	if .err != nil {
		// Ignore Flush after Close.
		if errors.Is(.err, ErrEncoderClosed) {
			return nil
		}
		return .err
	}
	return .writeErr
}

// Close will flush the final output and close the stream.
// The function will block until everything has been written.
// The Encoder can still be re-used after calling this.
func ( *Encoder) () error {
	 := &.state
	if .encoder == nil {
		return nil
	}
	 := .nextBlock(true)
	if  != nil {
		if errors.Is(.err, ErrEncoderClosed) {
			return nil
		}
		return 
	}
	if .frameContentSize > 0 {
		if .nInput != .frameContentSize {
			return fmt.Errorf("frame content size %d given, but %d bytes was written", .frameContentSize, .nInput)
		}
	}
	if .state.fullFrameWritten {
		return .err
	}
	.wg.Wait()
	.wWg.Wait()

	if .err != nil {
		return .err
	}
	if .writeErr != nil {
		return .writeErr
	}

	// Write CRC
	if .o.crc && .err == nil {
		// heap alloc.
		var  [4]byte
		_, .err = .w.Write(.encoder.AppendCRC([:0]))
		.nWritten += 4
	}

	// Add padding with content from crypto/rand.Reader
	if .err == nil && .o.pad > 0 {
		 := calcSkippableFrame(.nWritten, int64(.o.pad))
		,  := skippableFrame(.filling[:0], , rand.Reader)
		if  != nil {
			return 
		}
		_, .err = .w.Write()
	}
	if .err == nil {
		.err = ErrEncoderClosed
		return nil
	}

	return .err
}

// EncodeAll will encode all input in src and append it to dst.
// This function can be called concurrently, but each call will only run on a single goroutine.
// If empty input is given, nothing is returned, unless WithZeroFrames is specified.
// Encoded blocks can be concatenated and the result will be the combined input stream.
// Data compressed with EncodeAll can be decoded with the Decoder,
// using either a stream or DecodeAll.
func ( *Encoder) (,  []byte) []byte {
	.init.Do(.initialize)
	 := <-.encoders
	defer func() {
		.encoders <- 
	}()
	return .encodeAll(, , )
}

func ( *Encoder) ( encoder, ,  []byte) []byte {
	if len() == 0 {
		if .o.fullZero {
			// Add frame header.
			 := frameHeader{
				ContentSize:   0,
				WindowSize:    MinWindowSize,
				SingleSegment: true,
				// Adding a checksum would be a waste of space.
				Checksum: false,
				DictID:   0,
			}
			 = .appendTo()

			// Write raw block as last one only.
			var  blockHeader
			.setSize(0)
			.setType(blockTypeRaw)
			.setLast(true)
			 = .appendTo()
		}
		return 
	}

	// Use single segments when above minimum window and below window size.
	 := len() <= .o.windowSize && len() > MinWindowSize
	if .o.single != nil {
		 = *.o.single
	}
	 := frameHeader{
		ContentSize:   uint64(len()),
		WindowSize:    uint32(.WindowSize(int64(len()))),
		SingleSegment: ,
		Checksum:      .o.crc,
		DictID:        .o.dict.ID(),
	}

	// If less than 1MB, allocate a buffer up front.
	if len() == 0 && cap() == 0 && len() < 1<<20 && !.o.lowMem {
		 = make([]byte, 0, len())
	}
	 = .appendTo()

	// If we can do everything in one block, prefer that.
	if len() <= .o.blockSize {
		.Reset(.o.dict, true)
		// Slightly faster with no history and everything in one block.
		if .o.crc {
			_, _ = .CRC().Write()
		}
		 := .Block()
		.last = true
		if .o.dict == nil {
			.EncodeNoHist(, )
		} else {
			.Encode(, )
		}

		// If we got the exact same number of literals as input,
		// assume the literals cannot be compressed.
		 := .output
		// Output directly to dst
		.output = 

		 := .encode(, .o.noEntropy, !.o.allLitEntropy)
		if  != nil {
			panic()
		}
		 = .output
		.output = 
	} else {
		.Reset(.o.dict, false)
		 := .Block()
		for len() > 0 {
			 := 
			if len() > .o.blockSize {
				 = [:.o.blockSize]
			}
			 = [len():]
			if .o.crc {
				_, _ = .CRC().Write()
			}
			.pushOffsets()
			.Encode(, )
			if len() == 0 {
				.last = true
			}
			 := .encode(, .o.noEntropy, !.o.allLitEntropy)
			if  != nil {
				panic()
			}
			 = append(, .output...)
			.reset(nil)
		}
	}
	if .o.crc {
		 = .AppendCRC()
	}
	// Add padding with content from crypto/rand.Reader
	if .o.pad > 0 {
		 := calcSkippableFrame(int64(len()), int64(.o.pad))
		var  error
		,  = skippableFrame(, , rand.Reader)
		if  != nil {
			panic()
		}
	}
	return 
}

// MaxEncodedSize returns the expected maximum
// size of an encoded block or stream.
func ( *Encoder) ( int) int {
	 := 4 + 2 // magic + frame header & window descriptor
	if .o.dict != nil {
		 += 4
	}
	// Frame content size:
	if  < 256 {
		++
	} else if  < 65536+256 {
		 += 2
	} else if  < math.MaxInt32 {
		 += 4
	} else {
		 += 8
	}
	// Final crc
	if .o.crc {
		 += 4
	}

	// Max overhead is 3 bytes/block.
	// There cannot be 0 blocks.
	 := ( + .o.blockSize) / .o.blockSize

	// Combine, add padding.
	 :=  + 3* + 
	if .o.pad > 1 {
		 += calcSkippableFrame(int64(), int64(.o.pad))
	}
	return 
}