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

const (
	tableBits        = 15                               // Bits used in the table
	tableSize        = 1 << tableBits                   // Size of the table
	tableShardCnt    = 1 << (tableBits - dictShardBits) // Number of shards in the table
	tableShardSize   = tableSize / tableShardCnt        // Size of an individual shard
	tableFastHashLen = 6
	tableMask        = tableSize - 1 // Mask for table indices. Redundant, but can eliminate bounds checks.
	maxMatchLength   = 131074
)

type tableEntry struct {
	val    uint32
	offset int32
}

type fastEncoder struct {
	fastBase
	table [tableSize]tableEntry
}

type fastEncoderDict struct {
	fastEncoder
	dictTable       []tableEntry
	tableShardDirty [tableShardCnt]bool
	allDirty        bool
}

// Encode mimmics functionality in zstd_fast.c
func ( *fastEncoder) ( *blockEnc,  []byte) {
	const (
		            = 8
		 = 1 + 1 + 
	)

	// Protect against e.cur wraparound.
	for .cur >= .bufferReset-int32(len(.hist)) {
		if len(.hist) == 0 {
			for  := range .table[:] {
				.table[] = tableEntry{}
			}
			.cur = .maxMatchOff
			break
		}
		// Shift down everything in the table that isn't already too far away.
		 := .cur + int32(len(.hist)) - .maxMatchOff
		for  := range .table[:] {
			 := .table[].offset
			if  <  {
				 = 0
			} else {
				 =  - .cur + .maxMatchOff
			}
			.table[].offset = 
		}
		.cur = .maxMatchOff
		break
	}

	 := .addBlock()
	.size = len()
	if len() <  {
		.extraLits = len()
		.literals = .literals[:len()]
		copy(.literals, )
		return
	}

	// Override src
	 = .hist
	 := int32(len()) - 
	// stepSize is the number of bytes to skip on every main loop iteration.
	// It should be >= 2.
	const  = 2

	// TEMPLATE
	const  = tableBits
	// seems global, but would be nice to tweak.
	const  = 6

	// nextEmit is where in src the next emitLiteral should start from.
	 := 
	 := load6432(, )

	// Relative offsets
	 := int32(.recentOffsets[0])
	 := int32(.recentOffsets[1])

	 := func( *seq,  int32) {
		if  ==  {
			return
		}
		.literals = append(.literals, [:]...)
		.litLen = uint32( - )
	}
	if debugEncoder {
		println("recent offsets:", .recentOffsets)
	}

:
	for {
		// t will contain the match offset when we find one.
		// When existing the search loop, we have already checked 4 bytes.
		var  int32

		// We will not use repeat offsets across blocks.
		// By not using them for the first 3 matches
		 := len(.sequences) > 2

		for {
			if debugAsserts &&  &&  == 0 {
				panic("offset0 was 0")
			}

			 := hashLen(, , tableFastHashLen)
			 := hashLen(>>8, , tableFastHashLen)
			 := .table[]
			 := .table[]
			 :=  -  + 2

			.table[] = tableEntry{offset:  + .cur, val: uint32()}
			.table[] = tableEntry{offset:  + .cur + 1, val: uint32( >> 8)}

			if  &&  >= 0 && load3232(, ) == uint32(>>16) {
				// Consider history as well.
				var  seq
				 := 4 + .matchlen(+6, +4, )
				.matchLen = uint32( - zstdMinMatch)

				// We might be able to match backwards.
				// Extend as long as we can.
				 :=  + 2
				// We end the search early, so we don't risk 0 literals
				// and have to do special offset treatment.
				 :=  + 1

				 :=  - .maxMatchOff
				if  < 0 {
					 = 0
				}
				for  >  &&  >  && [-1] == [-1] && .matchLen < maxMatchLength-zstdMinMatch {
					--
					--
					.matchLen++
				}
				(&, )

				// rep 0
				.offset = 1
				if debugSequences {
					println("repeat sequence", , "next s:", )
				}
				.sequences = append(.sequences, )
				 +=  + 2
				 = 
				if  >=  {
					if debugEncoder {
						println("repeat ended", , )

					}
					break 
				}
				 = load6432(, )
				continue
			}
			 :=  - (.offset - .cur)
			 :=  - (.offset - .cur) + 1
			if  < .maxMatchOff && uint32() == .val {
				// found a regular match
				 = .offset - .cur
				if debugAsserts &&  <=  {
					panic(fmt.Sprintf("s (%d) <= t (%d)", , ))
				}
				if debugAsserts && - > .maxMatchOff {
					panic("s - t >e.maxMatchOff")
				}
				break
			}

			if  < .maxMatchOff && uint32(>>8) == .val {
				// found a regular match
				 = .offset - .cur
				++
				if debugAsserts &&  <=  {
					panic(fmt.Sprintf("s (%d) <= t (%d)", , ))
				}
				if debugAsserts && - > .maxMatchOff {
					panic("s - t >e.maxMatchOff")
				}
				if debugAsserts &&  < 0 {
					panic("t<0")
				}
				break
			}
			 +=  + (( - ) >> ( - 1))
			if  >=  {
				break 
			}
			 = load6432(, )
		}
		// A 4-byte match has been found. We'll later see if more than 4 bytes.
		 = 
		 =  - 

		if debugAsserts &&  <=  {
			panic(fmt.Sprintf("s (%d) <= t (%d)", , ))
		}

		if debugAsserts &&  && int() > len() {
			panic("invalid offset")
		}

		// Extend the 4-byte match as long as possible.
		 := .matchlen(+4, +4, ) + 4

		// Extend backwards
		 :=  - .maxMatchOff
		if  < 0 {
			 = 0
		}
		for  >  &&  >  && [-1] == [-1] &&  < maxMatchLength {
			--
			--
			++
		}

		// Write our sequence.
		var  seq
		.litLen = uint32( - )
		.matchLen = uint32( - zstdMinMatch)
		if .litLen > 0 {
			.literals = append(.literals, [:]...)
		}
		// Don't use repeat offsets
		.offset = uint32(-) + 3
		 += 
		if debugSequences {
			println("sequence", , "next s:", )
		}
		.sequences = append(.sequences, )
		 = 
		if  >=  {
			break 
		}
		 = load6432(, )

		// Check offset 2
		if  :=  - ;  && load3232(, ) == uint32() {
			// We have at least 4 byte match.
			// No need to check backwards. We come straight from a match
			 := 4 + .matchlen(+4, +4, )

			// Store this, since we have it.
			 := hashLen(, , tableFastHashLen)
			.table[] = tableEntry{offset:  + .cur, val: uint32()}
			.matchLen = uint32() - zstdMinMatch
			.litLen = 0
			// Since litlen is always 0, this is offset 1.
			.offset = 1
			 += 
			 = 
			if debugSequences {
				println("sequence", , "next s:", )
			}
			.sequences = append(.sequences, )

			// Swap offset 1 and 2.
			,  = , 
			if  >=  {
				break 
			}
			// Prepare next loop.
			 = load6432(, )
		}
	}

	if int() < len() {
		.literals = append(.literals, [:]...)
		.extraLits = len() - int()
	}
	.recentOffsets[0] = uint32()
	.recentOffsets[1] = uint32()
	if debugEncoder {
		println("returning, recent offsets:", .recentOffsets, "extra literals:", .extraLits)
	}
}

// EncodeNoHist will encode a block with no history and no following blocks.
// Most notable difference is that src will not be copied for history and
// we do not need to check for max match length.
func ( *fastEncoder) ( *blockEnc,  []byte) {
	const (
		            = 8
		 = 1 + 1 + 
	)
	if debugEncoder {
		if len() > maxCompressedBlockSize {
			panic("src too big")
		}
	}

	// Protect against e.cur wraparound.
	if .cur >= .bufferReset {
		for  := range .table[:] {
			.table[] = tableEntry{}
		}
		.cur = .maxMatchOff
	}

	 := int32(0)
	.size = len()
	if len() <  {
		.extraLits = len()
		.literals = .literals[:len()]
		copy(.literals, )
		return
	}

	 := int32(len()) - 
	// stepSize is the number of bytes to skip on every main loop iteration.
	// It should be >= 2.
	const  = 2

	// TEMPLATE
	const  = tableBits
	// seems global, but would be nice to tweak.
	const  = 6

	// nextEmit is where in src the next emitLiteral should start from.
	 := 
	 := load6432(, )

	// Relative offsets
	 := int32(.recentOffsets[0])
	 := int32(.recentOffsets[1])

	 := func( *seq,  int32) {
		if  ==  {
			return
		}
		.literals = append(.literals, [:]...)
		.litLen = uint32( - )
	}
	if debugEncoder {
		println("recent offsets:", .recentOffsets)
	}

:
	for {
		// t will contain the match offset when we find one.
		// When existing the search loop, we have already checked 4 bytes.
		var  int32

		// We will not use repeat offsets across blocks.
		// By not using them for the first 3 matches

		for {
			 := hashLen(, , tableFastHashLen)
			 := hashLen(>>8, , tableFastHashLen)
			 := .table[]
			 := .table[]
			 :=  -  + 2

			.table[] = tableEntry{offset:  + .cur, val: uint32()}
			.table[] = tableEntry{offset:  + .cur + 1, val: uint32( >> 8)}

			if len(.sequences) > 2 && load3232(, ) == uint32(>>16) {
				// Consider history as well.
				var  seq
				 := 4 + .matchlen(+6, +4, )

				.matchLen = uint32( - zstdMinMatch)

				// We might be able to match backwards.
				// Extend as long as we can.
				 :=  + 2
				// We end the search early, so we don't risk 0 literals
				// and have to do special offset treatment.
				 :=  + 1

				 :=  - .maxMatchOff
				if  < 0 {
					 = 0
				}
				for  >  &&  >  && [-1] == [-1] {
					--
					--
					.matchLen++
				}
				(&, )

				// rep 0
				.offset = 1
				if debugSequences {
					println("repeat sequence", , "next s:", )
				}
				.sequences = append(.sequences, )
				 +=  + 2
				 = 
				if  >=  {
					if debugEncoder {
						println("repeat ended", , )

					}
					break 
				}
				 = load6432(, )
				continue
			}
			 :=  - (.offset - .cur)
			 :=  - (.offset - .cur) + 1
			if  < .maxMatchOff && uint32() == .val {
				// found a regular match
				 = .offset - .cur
				if debugAsserts &&  <=  {
					panic(fmt.Sprintf("s (%d) <= t (%d)", , ))
				}
				if debugAsserts && - > .maxMatchOff {
					panic("s - t >e.maxMatchOff")
				}
				if debugAsserts &&  < 0 {
					panic(fmt.Sprintf("t (%d) < 0, candidate.offset: %d, e.cur: %d, coffset0: %d, e.maxMatchOff: %d", , .offset, .cur, , .maxMatchOff))
				}
				break
			}

			if  < .maxMatchOff && uint32(>>8) == .val {
				// found a regular match
				 = .offset - .cur
				++
				if debugAsserts &&  <=  {
					panic(fmt.Sprintf("s (%d) <= t (%d)", , ))
				}
				if debugAsserts && - > .maxMatchOff {
					panic("s - t >e.maxMatchOff")
				}
				if debugAsserts &&  < 0 {
					panic("t<0")
				}
				break
			}
			 +=  + (( - ) >> ( - 1))
			if  >=  {
				break 
			}
			 = load6432(, )
		}
		// A 4-byte match has been found. We'll later see if more than 4 bytes.
		 = 
		 =  - 

		if debugAsserts &&  <=  {
			panic(fmt.Sprintf("s (%d) <= t (%d)", , ))
		}

		if debugAsserts &&  < 0 {
			panic(fmt.Sprintf("t (%d) < 0 ", ))
		}
		// Extend the 4-byte match as long as possible.
		 := .matchlen(+4, +4, ) + 4

		// Extend backwards
		 :=  - .maxMatchOff
		if  < 0 {
			 = 0
		}
		for  >  &&  >  && [-1] == [-1] {
			--
			--
			++
		}

		// Write our sequence.
		var  seq
		.litLen = uint32( - )
		.matchLen = uint32( - zstdMinMatch)
		if .litLen > 0 {
			.literals = append(.literals, [:]...)
		}
		// Don't use repeat offsets
		.offset = uint32(-) + 3
		 += 
		if debugSequences {
			println("sequence", , "next s:", )
		}
		.sequences = append(.sequences, )
		 = 
		if  >=  {
			break 
		}
		 = load6432(, )

		// Check offset 2
		if  :=  - ; len(.sequences) > 2 && load3232(, ) == uint32() {
			// We have at least 4 byte match.
			// No need to check backwards. We come straight from a match
			 := 4 + .matchlen(+4, +4, )

			// Store this, since we have it.
			 := hashLen(, , tableFastHashLen)
			.table[] = tableEntry{offset:  + .cur, val: uint32()}
			.matchLen = uint32() - zstdMinMatch
			.litLen = 0
			// Since litlen is always 0, this is offset 1.
			.offset = 1
			 += 
			 = 
			if debugSequences {
				println("sequence", , "next s:", )
			}
			.sequences = append(.sequences, )

			// Swap offset 1 and 2.
			,  = , 
			if  >=  {
				break 
			}
			// Prepare next loop.
			 = load6432(, )
		}
	}

	if int() < len() {
		.literals = append(.literals, [:]...)
		.extraLits = len() - int()
	}
	if debugEncoder {
		println("returning, recent offsets:", .recentOffsets, "extra literals:", .extraLits)
	}
	// We do not store history, so we must offset e.cur to avoid false matches for next user.
	if .cur < .bufferReset {
		.cur += int32(len())
	}
}

// Encode will encode the content, with a dictionary if initialized for it.
func ( *fastEncoderDict) ( *blockEnc,  []byte) {
	const (
		            = 8
		 = 1 + 1 + 
	)
	if .allDirty || len() > 32<<10 {
		.fastEncoder.Encode(, )
		.allDirty = true
		return
	}
	// Protect against e.cur wraparound.
	for .cur >= .bufferReset-int32(len(.hist)) {
		if len(.hist) == 0 {
			.table = [tableSize]tableEntry{}
			.cur = .maxMatchOff
			break
		}
		// Shift down everything in the table that isn't already too far away.
		 := .cur + int32(len(.hist)) - .maxMatchOff
		for  := range .table[:] {
			 := .table[].offset
			if  <  {
				 = 0
			} else {
				 =  - .cur + .maxMatchOff
			}
			.table[].offset = 
		}
		.cur = .maxMatchOff
		break
	}

	 := .addBlock()
	.size = len()
	if len() <  {
		.extraLits = len()
		.literals = .literals[:len()]
		copy(.literals, )
		return
	}

	// Override src
	 = .hist
	 := int32(len()) - 
	// stepSize is the number of bytes to skip on every main loop iteration.
	// It should be >= 2.
	const  = 2

	// TEMPLATE
	const  = tableBits
	// seems global, but would be nice to tweak.
	const  = 7

	// nextEmit is where in src the next emitLiteral should start from.
	 := 
	 := load6432(, )

	// Relative offsets
	 := int32(.recentOffsets[0])
	 := int32(.recentOffsets[1])

	 := func( *seq,  int32) {
		if  ==  {
			return
		}
		.literals = append(.literals, [:]...)
		.litLen = uint32( - )
	}
	if debugEncoder {
		println("recent offsets:", .recentOffsets)
	}

:
	for {
		// t will contain the match offset when we find one.
		// When existing the search loop, we have already checked 4 bytes.
		var  int32

		// We will not use repeat offsets across blocks.
		// By not using them for the first 3 matches
		 := len(.sequences) > 2

		for {
			if debugAsserts &&  &&  == 0 {
				panic("offset0 was 0")
			}

			 := hashLen(, , tableFastHashLen)
			 := hashLen(>>8, , tableFastHashLen)
			 := .table[]
			 := .table[]
			 :=  -  + 2

			.table[] = tableEntry{offset:  + .cur, val: uint32()}
			.markShardDirty()
			.table[] = tableEntry{offset:  + .cur + 1, val: uint32( >> 8)}
			.markShardDirty()

			if  &&  >= 0 && load3232(, ) == uint32(>>16) {
				// Consider history as well.
				var  seq
				 := 4 + .matchlen(+6, +4, )

				.matchLen = uint32( - zstdMinMatch)

				// We might be able to match backwards.
				// Extend as long as we can.
				 :=  + 2
				// We end the search early, so we don't risk 0 literals
				// and have to do special offset treatment.
				 :=  + 1

				 :=  - .maxMatchOff
				if  < 0 {
					 = 0
				}
				for  >  &&  >  && [-1] == [-1] && .matchLen < maxMatchLength-zstdMinMatch {
					--
					--
					.matchLen++
				}
				(&, )

				// rep 0
				.offset = 1
				if debugSequences {
					println("repeat sequence", , "next s:", )
				}
				.sequences = append(.sequences, )
				 +=  + 2
				 = 
				if  >=  {
					if debugEncoder {
						println("repeat ended", , )

					}
					break 
				}
				 = load6432(, )
				continue
			}
			 :=  - (.offset - .cur)
			 :=  - (.offset - .cur) + 1
			if  < .maxMatchOff && uint32() == .val {
				// found a regular match
				 = .offset - .cur
				if debugAsserts &&  <=  {
					panic(fmt.Sprintf("s (%d) <= t (%d)", , ))
				}
				if debugAsserts && - > .maxMatchOff {
					panic("s - t >e.maxMatchOff")
				}
				break
			}

			if  < .maxMatchOff && uint32(>>8) == .val {
				// found a regular match
				 = .offset - .cur
				++
				if debugAsserts &&  <=  {
					panic(fmt.Sprintf("s (%d) <= t (%d)", , ))
				}
				if debugAsserts && - > .maxMatchOff {
					panic("s - t >e.maxMatchOff")
				}
				if debugAsserts &&  < 0 {
					panic("t<0")
				}
				break
			}
			 +=  + (( - ) >> ( - 1))
			if  >=  {
				break 
			}
			 = load6432(, )
		}
		// A 4-byte match has been found. We'll later see if more than 4 bytes.
		 = 
		 =  - 

		if debugAsserts &&  <=  {
			panic(fmt.Sprintf("s (%d) <= t (%d)", , ))
		}

		if debugAsserts &&  && int() > len() {
			panic("invalid offset")
		}

		// Extend the 4-byte match as long as possible.
		 := .matchlen(+4, +4, ) + 4

		// Extend backwards
		 :=  - .maxMatchOff
		if  < 0 {
			 = 0
		}
		for  >  &&  >  && [-1] == [-1] &&  < maxMatchLength {
			--
			--
			++
		}

		// Write our sequence.
		var  seq
		.litLen = uint32( - )
		.matchLen = uint32( - zstdMinMatch)
		if .litLen > 0 {
			.literals = append(.literals, [:]...)
		}
		// Don't use repeat offsets
		.offset = uint32(-) + 3
		 += 
		if debugSequences {
			println("sequence", , "next s:", )
		}
		.sequences = append(.sequences, )
		 = 
		if  >=  {
			break 
		}
		 = load6432(, )

		// Check offset 2
		if  :=  - ;  && load3232(, ) == uint32() {
			// We have at least 4 byte match.
			// No need to check backwards. We come straight from a match
			 := 4 + .matchlen(+4, +4, )

			// Store this, since we have it.
			 := hashLen(, , tableFastHashLen)
			.table[] = tableEntry{offset:  + .cur, val: uint32()}
			.markShardDirty()
			.matchLen = uint32() - zstdMinMatch
			.litLen = 0
			// Since litlen is always 0, this is offset 1.
			.offset = 1
			 += 
			 = 
			if debugSequences {
				println("sequence", , "next s:", )
			}
			.sequences = append(.sequences, )

			// Swap offset 1 and 2.
			,  = , 
			if  >=  {
				break 
			}
			// Prepare next loop.
			 = load6432(, )
		}
	}

	if int() < len() {
		.literals = append(.literals, [:]...)
		.extraLits = len() - int()
	}
	.recentOffsets[0] = uint32()
	.recentOffsets[1] = uint32()
	if debugEncoder {
		println("returning, recent offsets:", .recentOffsets, "extra literals:", .extraLits)
	}
}

// ResetDict will reset and set a dictionary if not nil
func ( *fastEncoder) ( *dict,  bool) {
	.resetBase(, )
	if  != nil {
		panic("fastEncoder: Reset with dict")
	}
}

// ResetDict will reset and set a dictionary if not nil
func ( *fastEncoderDict) ( *dict,  bool) {
	.resetBase(, )
	if  == nil {
		return
	}

	// Init or copy dict table
	if len(.dictTable) != len(.table) || .id != .lastDictID {
		if len(.dictTable) != len(.table) {
			.dictTable = make([]tableEntry, len(.table))
		}
		if true {
			 := .maxMatchOff + int32(len(.content)) - 8
			for  := .maxMatchOff;  < ;  += 2 {
				const  = tableBits

				 := load6432(.content, -.maxMatchOff)
				 := hashLen(, , tableFastHashLen)     // 0 -> 6
				 := hashLen(>>8, , tableFastHashLen) // 1 -> 7
				.dictTable[] = tableEntry{
					val:    uint32(),
					offset: ,
				}
				.dictTable[] = tableEntry{
					val:    uint32( >> 8),
					offset:  + 1,
				}
			}
		}
		.lastDictID = .id
		.allDirty = true
	}

	.cur = .maxMatchOff
	 := 0
	if !.allDirty {
		for  := range .tableShardDirty {
			if .tableShardDirty[] {
				++
			}
		}
	}

	const  = tableShardCnt
	const  = tableShardSize
	if .allDirty ||  > *4/6 {
		//copy(e.table[:], e.dictTable)
		.table = *(*[tableSize]tableEntry)(.dictTable)
		for  := range .tableShardDirty {
			.tableShardDirty[] = false
		}
		.allDirty = false
		return
	}
	for  := range .tableShardDirty {
		if !.tableShardDirty[] {
			continue
		}

		//copy(e.table[i*shardSize:(i+1)*shardSize], e.dictTable[i*shardSize:(i+1)*shardSize])
		*(*[]tableEntry)(.table[*:]) = *(*[]tableEntry)(.dictTable[*:])
		.tableShardDirty[] = false
	}
	.allDirty = false
}

func ( *fastEncoderDict) () {
	.allDirty = true
}

func ( *fastEncoderDict) ( uint32) {
	.tableShardDirty[/tableShardSize] = true
}