// Copyright 2011 The Snappy-Go Authors. All rights reserved.
// Copyright (c) 2019 Klaus Post. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package s2

import (
	
	
	
	

	
)

var (
	// ErrCorrupt reports that the input is invalid.
	ErrCorrupt = errors.New("s2: corrupt input")
	// ErrCRC reports that the input failed CRC validation (streams only)
	ErrCRC = errors.New("s2: corrupt input, crc mismatch")
	// ErrTooLarge reports that the uncompressed length is too large.
	ErrTooLarge = errors.New("s2: decoded block is too large")
	// ErrUnsupported reports that the input isn't supported.
	ErrUnsupported = errors.New("s2: unsupported input")
)

// DecodedLen returns the length of the decoded block.
func ( []byte) (int, error) {
	, ,  := decodedLen()
	return , 
}

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

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

const (
	decodeErrCodeCorrupt = 1
)

// Decode returns the decoded form of src. The returned slice may be a sub-
// slice of dst if dst was large enough to hold the entire decoded block.
// Otherwise, a newly allocated slice will be returned.
//
// The dst and src must not overlap. It is valid to pass a nil dst.
func (,  []byte) ([]byte, error) {
	, ,  := decodedLen()
	if  != nil {
		return nil, 
	}
	if  <= cap() {
		 = [:]
	} else {
		 = make([]byte, )
	}

	race.WriteSlice()
	race.ReadSlice([:])

	if s2Decode(, [:]) != 0 {
		return nil, ErrCorrupt
	}
	return , nil
}

// s2DecodeDict writes the decoding of src to dst. It assumes that the varint-encoded
// length of the decompressed bytes has already been read, and that len(dst)
// equals that length.
//
// It returns 0 on success or a decodeErrCodeXxx error code on failure.
func s2DecodeDict(,  []byte,  *Dict) int {
	if  == nil {
		return s2Decode(, )
	}
	const  = false
	const  = 

	if  {
		fmt.Println("Starting decode, dst len:", len())
	}
	var , ,  int
	 := len(.dict) - .repeat

	// As long as we can read at least 5 bytes...
	for  < len()-5 {
		// Removing bounds checks is SLOWER, when if doing
		// in := src[s:s+5]
		// Checked on Go 1.18
		switch [] & 0x03 {
		case tagLiteral:
			 := uint32([] >> 2)
			switch {
			case  < 60:
				++
			case  == 60:
				 += 2
				 = uint32([-1])
			case  == 61:
				 := [ : +3]
				 = uint32([1]) | uint32([2])<<8
				 += 3
			case  == 62:
				 := [ : +4]
				// Load as 32 bit and shift down.
				 = uint32([0]) | uint32([1])<<8 | uint32([2])<<16 | uint32([3])<<24
				 >>= 8
				 += 4
			case  == 63:
				 := [ : +5]
				 = uint32([1]) | uint32([2])<<8 | uint32([3])<<16 | uint32([4])<<24
				 += 5
			}
			 = int() + 1
			if  {
				fmt.Println("literals, length:", , "d-after:", +)
			}
			if  > len()- ||  > len()- || (strconv.IntSize == 32 &&  <= 0) {
				if  {
					fmt.Println("corrupt literal: length:", , "d-left:", len()-, "src-left:", len()-)
				}
				return decodeErrCodeCorrupt
			}

			copy([:], [:+])
			 += 
			 += 
			continue

		case tagCopy1:
			 += 2
			 := int(uint32([-2])&0xe0<<3 | uint32([-1]))
			 = int([-2]) >> 2 & 0x7
			if  == 0 {
				if  {
					fmt.Print("(repeat) ")
				}
				// keep last offset
				switch  {
				case 5:
					 = int([]) + 4
					 += 1
				case 6:
					 := [ : +2]
					 = int(uint32([0])|(uint32([1])<<8)) + (1 << 8)
					 += 2
				case 7:
					 := [ : +3]
					 = int((uint32([2])<<16)|(uint32([1])<<8)|uint32([0])) + (1 << 16)
					 += 3
				default: // 0-> 4
				}
			} else {
				 = 
			}
			 += 4
		case tagCopy2:
			 := [ : +3]
			 = int(uint32([1]) | uint32([2])<<8)
			 = 1 + int([0])>>2
			 += 3

		case tagCopy4:
			 := [ : +5]
			 = int(uint32([1]) | uint32([2])<<8 | uint32([3])<<16 | uint32([4])<<24)
			 = 1 + int([0])>>2
			 += 5
		}

		if  <= 0 ||  > len()- {
			if  {
				fmt.Println("match error; offset:", , "length:", , "dst-left:", len()-)
			}
			return decodeErrCodeCorrupt
		}

		// copy from dict
		if  <  {
			if  > MaxDictSrcOffset {
				if  {
					fmt.Println("dict after", MaxDictSrcOffset, "d:", , "offset:", , "length:", )
				}
				return decodeErrCodeCorrupt
			}
			 := len(.dict) -  + 
			if  < 0 || + > len(.dict) {
				if  {
					fmt.Printf("offset (%d) + length (%d) bigger than dict (%d)\n", , , len(.dict))
				}
				return decodeErrCodeCorrupt
			}
			if  {
				fmt.Println("dict copy, length:", , "offset:", , "d-after:", +, "dict start offset:", )
			}
			copy([:+], .dict[:])
			 += 
			continue
		}

		if  {
			fmt.Println("copy, length:", , "offset:", , "d-after:", +)
		}

		// Copy from an earlier sub-slice of dst to a later sub-slice.
		// If no overlap, use the built-in copy:
		if  >  {
			copy([:+], [-:])
			 += 
			continue
		}

		// Unlike the built-in copy function, this byte-by-byte copy always runs
		// forwards, even if the slices overlap. Conceptually, this is:
		//
		// d += forwardCopy(dst[d:d+length], dst[d-offset:])
		//
		// We align the slices into a and b and show the compiler they are the same size.
		// This allows the loop to run without bounds checks.
		 := [ : +]
		 := [-:]
		 = [:len()]
		for  := range  {
			[] = []
		}
		 += 
	}

	// Remaining with extra checks...
	for  < len() {
		switch [] & 0x03 {
		case tagLiteral:
			 := uint32([] >> 2)
			switch {
			case  < 60:
				++
			case  == 60:
				 += 2
				if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
					if  {
						fmt.Println("src went oob")
					}
					return decodeErrCodeCorrupt
				}
				 = uint32([-1])
			case  == 61:
				 += 3
				if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
					if  {
						fmt.Println("src went oob")
					}
					return decodeErrCodeCorrupt
				}
				 = uint32([-2]) | uint32([-1])<<8
			case  == 62:
				 += 4
				if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
					if  {
						fmt.Println("src went oob")
					}
					return decodeErrCodeCorrupt
				}
				 = uint32([-3]) | uint32([-2])<<8 | uint32([-1])<<16
			case  == 63:
				 += 5
				if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
					if  {
						fmt.Println("src went oob")
					}
					return decodeErrCodeCorrupt
				}
				 = uint32([-4]) | uint32([-3])<<8 | uint32([-2])<<16 | uint32([-1])<<24
			}
			 = int() + 1
			if  > len()- ||  > len()- || (strconv.IntSize == 32 &&  <= 0) {
				if  {
					fmt.Println("corrupt literal: length:", , "d-left:", len()-, "src-left:", len()-)
				}
				return decodeErrCodeCorrupt
			}
			if  {
				fmt.Println("literals, length:", , "d-after:", +)
			}

			copy([:], [:+])
			 += 
			 += 
			continue

		case tagCopy1:
			 += 2
			if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
				if  {
					fmt.Println("src went oob")
				}
				return decodeErrCodeCorrupt
			}
			 = int([-2]) >> 2 & 0x7
			 := int(uint32([-2])&0xe0<<3 | uint32([-1]))
			if  == 0 {
				if  {
					fmt.Print("(repeat) ")
				}
				// keep last offset
				switch  {
				case 5:
					 += 1
					if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
						if  {
							fmt.Println("src went oob")
						}
						return decodeErrCodeCorrupt
					}
					 = int(uint32([-1])) + 4
				case 6:
					 += 2
					if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
						if  {
							fmt.Println("src went oob")
						}
						return decodeErrCodeCorrupt
					}
					 = int(uint32([-2])|(uint32([-1])<<8)) + (1 << 8)
				case 7:
					 += 3
					if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
						if  {
							fmt.Println("src went oob")
						}
						return decodeErrCodeCorrupt
					}
					 = int(uint32([-3])|(uint32([-2])<<8)|(uint32([-1])<<16)) + (1 << 16)
				default: // 0-> 4
				}
			} else {
				 = 
			}
			 += 4
		case tagCopy2:
			 += 3
			if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
				if  {
					fmt.Println("src went oob")
				}
				return decodeErrCodeCorrupt
			}
			 = 1 + int([-3])>>2
			 = int(uint32([-2]) | uint32([-1])<<8)

		case tagCopy4:
			 += 5
			if uint() > uint(len()) { // The uint conversions catch overflow from the previous line.
				if  {
					fmt.Println("src went oob")
				}
				return decodeErrCodeCorrupt
			}
			 = 1 + int([-5])>>2
			 = int(uint32([-4]) | uint32([-3])<<8 | uint32([-2])<<16 | uint32([-1])<<24)
		}

		if  <= 0 ||  > len()- {
			if  {
				fmt.Println("match error; offset:", , "length:", , "dst-left:", len()-)
			}
			return decodeErrCodeCorrupt
		}

		// copy from dict
		if  <  {
			if  > MaxDictSrcOffset {
				if  {
					fmt.Println("dict after", MaxDictSrcOffset, "d:", , "offset:", , "length:", )
				}
				return decodeErrCodeCorrupt
			}
			 := len(.dict) - ( - )
			if  {
				fmt.Println("starting dict entry from dict offset", len(.dict)-)
			}
			if + > len(.dict) {
				if  {
					fmt.Println("err: END offset", +, "bigger than dict", len(.dict), "dict offset:", , "length:", )
				}
				return decodeErrCodeCorrupt
			}
			if  < 0 {
				if  {
					fmt.Println("err: START offset", , "less than 0", len(.dict), "dict offset:", , "length:", )
				}
				return decodeErrCodeCorrupt
			}
			copy([:+], .dict[:])
			 += 
			continue
		}

		if  {
			fmt.Println("copy, length:", , "offset:", , "d-after:", +)
		}

		// Copy from an earlier sub-slice of dst to a later sub-slice.
		// If no overlap, use the built-in copy:
		if  >  {
			copy([:+], [-:])
			 += 
			continue
		}

		// Unlike the built-in copy function, this byte-by-byte copy always runs
		// forwards, even if the slices overlap. Conceptually, this is:
		//
		// d += forwardCopy(dst[d:d+length], dst[d-offset:])
		//
		// We align the slices into a and b and show the compiler they are the same size.
		// This allows the loop to run without bounds checks.
		 := [ : +]
		 := [-:]
		 = [:len()]
		for  := range  {
			[] = []
		}
		 += 
	}

	if  != len() {
		if  {
			fmt.Println("wanted length", len(), "got", )
		}
		return decodeErrCodeCorrupt
	}
	return 0
}