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

	
	snappy 
)

const (
	snappyTagLiteral = 0x00
	snappyTagCopy1   = 0x01
	snappyTagCopy2   = 0x02
	snappyTagCopy4   = 0x03
)

const (
	snappyChecksumSize = 4
	snappyMagicBody    = "sNaPpY"

	// snappyMaxBlockSize is the maximum size of the input to encodeBlock. It is not
	// part of the wire format per se, but some parts of the encoder assume
	// that an offset fits into a uint16.
	//
	// Also, for the framing format (Writer type instead of Encode function),
	// https://github.com/google/snappy/blob/master/framing_format.txt says
	// that "the uncompressed data in a chunk must be no longer than 65536
	// bytes".
	snappyMaxBlockSize = 65536

	// snappyMaxEncodedLenOfMaxBlockSize equals MaxEncodedLen(snappyMaxBlockSize), but is
	// hard coded to be a const instead of a variable, so that obufLen can also
	// be a const. Their equivalence is confirmed by
	// TestMaxEncodedLenOfMaxBlockSize.
	snappyMaxEncodedLenOfMaxBlockSize = 76490
)

const (
	chunkTypeCompressedData   = 0x00
	chunkTypeUncompressedData = 0x01
	chunkTypePadding          = 0xfe
	chunkTypeStreamIdentifier = 0xff
)

var (
	// ErrSnappyCorrupt reports that the input is invalid.
	ErrSnappyCorrupt = errors.New("snappy: corrupt input")
	// ErrSnappyTooLarge reports that the uncompressed length is too large.
	ErrSnappyTooLarge = errors.New("snappy: decoded block is too large")
	// ErrSnappyUnsupported reports that the input isn't supported.
	ErrSnappyUnsupported = errors.New("snappy: unsupported input")

	errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length")
)

// SnappyConverter can read SnappyConverter-compressed streams and convert them to zstd.
// Conversion is done by converting the stream directly from Snappy without intermediate
// full decoding.
// Therefore the compression ratio is much less than what can be done by a full decompression
// and compression, and a faulty Snappy stream may lead to a faulty Zstandard stream without
// any errors being generated.
// No CRC value is being generated and not all CRC values of the Snappy stream are checked.
// However, it provides really fast recompression of Snappy streams.
// The converter can be reused to avoid allocations, even after errors.
type SnappyConverter struct {
	r     io.Reader
	err   error
	buf   []byte
	block *blockEnc
}

// Convert the Snappy stream supplied in 'in' and write the zStandard stream to 'w'.
// If any error is detected on the Snappy stream it is returned.
// The number of bytes written is returned.
func ( *SnappyConverter) ( io.Reader,  io.Writer) (int64, error) {
	initPredefined()
	.err = nil
	.r = 
	if .block == nil {
		.block = &blockEnc{}
		.block.init()
	}
	.block.initNewEncode()
	if len(.buf) != snappyMaxEncodedLenOfMaxBlockSize+snappyChecksumSize {
		.buf = make([]byte, snappyMaxEncodedLenOfMaxBlockSize+snappyChecksumSize)
	}
	.block.litEnc.Reuse = huff0.ReusePolicyNone
	var  int64
	var  bool
	{
		 := frameHeader{WindowSize: snappyMaxBlockSize}.appendTo(.buf[:0])

		var  int
		, .err = .Write()
		if .err != nil {
			return , .err
		}
		 += int64()
	}

	for {
		if !.readFull(.buf[:4], true) {
			// Add empty last block
			.block.reset(nil)
			.block.last = true
			 := .block.encodeLits(.block.literals, false)
			if  != nil {
				return , 
			}
			,  := .Write(.block.output)
			if  != nil {
				return , 
			}
			 += int64()

			return , .err
		}
		 := .buf[0]
		if ! {
			if  != chunkTypeStreamIdentifier {
				println("chunkType != chunkTypeStreamIdentifier", )
				.err = ErrSnappyCorrupt
				return , .err
			}
			 = true
		}
		 := int(.buf[1]) | int(.buf[2])<<8 | int(.buf[3])<<16
		if  > len(.buf) {
			println("chunkLen > len(r.buf)", )
			.err = ErrSnappyUnsupported
			return , .err
		}

		// The chunk types are specified at
		// https://github.com/google/snappy/blob/master/framing_format.txt
		switch  {
		case chunkTypeCompressedData:
			// Section 4.2. Compressed data (chunk type 0x00).
			if  < snappyChecksumSize {
				println("chunkLen < snappyChecksumSize", , snappyChecksumSize)
				.err = ErrSnappyCorrupt
				return , .err
			}
			 := .buf[:]
			if !.readFull(, false) {
				return , .err
			}
			//checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
			 = [snappyChecksumSize:]

			, ,  := snappyDecodedLen()
			if  != nil {
				.err = 
				return , .err
			}
			 = [:]
			if  > snappyMaxBlockSize {
				println("n > snappyMaxBlockSize", , snappyMaxBlockSize)
				.err = ErrSnappyCorrupt
				return , .err
			}
			.block.reset(nil)
			.block.pushOffsets()
			if  := decodeSnappy(.block, );  != nil {
				.err = 
				return , .err
			}
			if .block.size+.block.extraLits !=  {
				printf("invalid size, want %d, got %d\n", , .block.size+.block.extraLits)
				.err = ErrSnappyCorrupt
				return , .err
			}
			 = .block.encode(nil, false, false)
			switch  {
			case errIncompressible:
				.block.popOffsets()
				.block.reset(nil)
				.block.literals,  = snappy.Decode(.block.literals[:], .buf[snappyChecksumSize:])
				if  != nil {
					return , 
				}
				 = .block.encodeLits(.block.literals, false)
				if  != nil {
					return , 
				}
			case nil:
			default:
				return , 
			}

			, .err = .Write(.block.output)
			if .err != nil {
				return , .err
			}
			 += int64()
			continue
		case chunkTypeUncompressedData:
			if debugEncoder {
				println("Uncompressed, chunklen", )
			}
			// Section 4.3. Uncompressed data (chunk type 0x01).
			if  < snappyChecksumSize {
				println("chunkLen < snappyChecksumSize", , snappyChecksumSize)
				.err = ErrSnappyCorrupt
				return , .err
			}
			.block.reset(nil)
			 := .buf[:snappyChecksumSize]
			if !.readFull(, false) {
				return , .err
			}
			 := uint32([0]) | uint32([1])<<8 | uint32([2])<<16 | uint32([3])<<24
			// Read directly into r.decoded instead of via r.buf.
			 :=  - snappyChecksumSize
			if  > snappyMaxBlockSize {
				println("n > snappyMaxBlockSize", , snappyMaxBlockSize)
				.err = ErrSnappyCorrupt
				return , .err
			}
			.block.literals = .block.literals[:]
			if !.readFull(.block.literals, false) {
				return , .err
			}
			if snappyCRC(.block.literals) !=  {
				println("literals crc mismatch")
				.err = ErrSnappyCorrupt
				return , .err
			}
			 := .block.encodeLits(.block.literals, false)
			if  != nil {
				return , 
			}
			, .err = .Write(.block.output)
			if .err != nil {
				return , .err
			}
			 += int64()
			continue

		case chunkTypeStreamIdentifier:
			if debugEncoder {
				println("stream id", , len(snappyMagicBody))
			}
			// Section 4.1. Stream identifier (chunk type 0xff).
			if  != len(snappyMagicBody) {
				println("chunkLen != len(snappyMagicBody)", , len(snappyMagicBody))
				.err = ErrSnappyCorrupt
				return , .err
			}
			if !.readFull(.buf[:len(snappyMagicBody)], false) {
				return , .err
			}
			for  := 0;  < len(snappyMagicBody); ++ {
				if .buf[] != snappyMagicBody[] {
					println("r.buf[i] != snappyMagicBody[i]", .buf[], snappyMagicBody[], )
					.err = ErrSnappyCorrupt
					return , .err
				}
			}
			continue
		}

		if  <= 0x7f {
			// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
			println("chunkType <= 0x7f")
			.err = ErrSnappyUnsupported
			return , .err
		}
		// Section 4.4 Padding (chunk type 0xfe).
		// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
		if !.readFull(.buf[:], false) {
			return , .err
		}
	}
}

// decodeSnappy writes the decoding of src to dst. It assumes that the varint-encoded
// length of the decompressed bytes has already been read.
func decodeSnappy( *blockEnc,  []byte) error {
	//decodeRef(make([]byte, snappyMaxBlockSize), src)
	var ,  int
	 := .extraLits
	var  uint32
	for  < len() {
		switch [] & 0x03 {
		case snappyTagLiteral:
			 := uint32([] >> 2)
			switch {
			case  < 60:
				++
			case  == 60:
				 += 2
				if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
					println("uint(s) > uint(len(src)", , )
					return ErrSnappyCorrupt
				}
				 = uint32([-1])
			case  == 61:
				 += 3
				if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
					println("uint(s) > uint(len(src)", , )
					return ErrSnappyCorrupt
				}
				 = uint32([-2]) | uint32([-1])<<8
			case  == 62:
				 += 4
				if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
					println("uint(s) > uint(len(src)", , )
					return ErrSnappyCorrupt
				}
				 = uint32([-3]) | uint32([-2])<<8 | uint32([-1])<<16
			case  == 63:
				 += 5
				if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
					println("uint(s) > uint(len(src)", , )
					return ErrSnappyCorrupt
				}
				 = uint32([-4]) | uint32([-3])<<8 | uint32([-2])<<16 | uint32([-1])<<24
			}
			if  > snappyMaxBlockSize {
				println("x > snappyMaxBlockSize", , snappyMaxBlockSize)
				return ErrSnappyCorrupt
			}
			 = int() + 1
			if  <= 0 {
				println("length <= 0 ", )

				return errUnsupportedLiteralLength
			}
			//if length > snappyMaxBlockSize-d || uint32(length) > len(src)-s {
			//	return ErrSnappyCorrupt
			//}

			.literals = append(.literals, [:+]...)
			//println(length, "litLen")
			 += 
			 += 
			continue

		case snappyTagCopy1:
			 += 2
			if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
				println("uint(s) > uint(len(src)", , len())
				return ErrSnappyCorrupt
			}
			 = 4 + int([-2])>>2&0x7
			 = uint32([-2])&0xe0<<3 | uint32([-1])

		case snappyTagCopy2:
			 += 3
			if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
				println("uint(s) > uint(len(src)", , len())
				return ErrSnappyCorrupt
			}
			 = 1 + int([-3])>>2
			 = uint32([-2]) | uint32([-1])<<8

		case snappyTagCopy4:
			 += 5
			if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
				println("uint(s) > uint(len(src)", , len())
				return ErrSnappyCorrupt
			}
			 = 1 + int([-5])>>2
			 = uint32([-4]) | uint32([-3])<<8 | uint32([-2])<<16 | uint32([-1])<<24
		}

		if  <= 0 || .size+ < int() /*|| length > len(blk)-d */ {
			println("offset <= 0 || blk.size+lits < int(offset)", , .size+, int(), .size, )

			return ErrSnappyCorrupt
		}

		// Check if offset is one of the recent offsets.
		// Adjusts the output offset accordingly.
		// Gives a tiny bit of compression, typically around 1%.
		if false {
			 = .matchOffset(, uint32())
		} else {
			 += 3
		}

		.sequences = append(.sequences, seq{
			litLen:   uint32(),
			offset:   ,
			matchLen: uint32() - zstdMinMatch,
		})
		.size +=  + 
		 = 0
	}
	.extraLits = 
	return nil
}

func ( *SnappyConverter) ( []byte,  bool) ( bool) {
	if _, .err = io.ReadFull(.r, ); .err != nil {
		if .err == io.ErrUnexpectedEOF || (.err == io.EOF && !) {
			.err = ErrSnappyCorrupt
		}
		return false
	}
	return true
}

var crcTable = crc32.MakeTable(crc32.Castagnoli)

// crc implements the checksum specified in section 3 of
// https://github.com/google/snappy/blob/master/framing_format.txt
func snappyCRC( []byte) uint32 {
	 := crc32.Update(0, crcTable, )
	return >>15 | <<17 + 0xa282ead8
}

// snappyDecodedLen returns the length of the decoded block and the number of bytes
// that the length header occupied.
func snappyDecodedLen( []byte) (,  int,  error) {
	,  := binary.Uvarint()
	if  <= 0 ||  > 0xffffffff {
		return 0, 0, ErrSnappyCorrupt
	}

	const  = 32 << (^uint(0) >> 32 & 1)
	if  == 32 &&  > 0x7fffffff {
		return 0, 0, ErrSnappyTooLarge
	}
	return int(), , nil
}