package matchfinder

import (
	
	
)

const (
	zdfastLongTableBits = 17
	zdfastLongTableSize = 1 << zdfastLongTableBits
)

// ZDFast is a MatchFinder based on the "Default" setting in
// github.com/klauspost/compress/zstd.
type ZDFast struct {
	MaxDistance int

	history []byte
	// current is the offset at the start of history
	current   int32
	table     [zfastTableSize]tableEntry
	longTable [zdfastLongTableSize]tableEntry
}

func ( *ZDFast) () {
	.current = 0
	.table = [zfastTableSize]tableEntry{}
	.longTable = [zdfastLongTableSize]tableEntry{}
	.history = .history[:0]
}

func ( *ZDFast) ( []Match,  []byte) []Match {
	if .MaxDistance == 0 {
		.MaxDistance = 1 << 16
	}

	// Protect against overflow of current.
	if int(.current) >= int(math.MaxInt32)-2*.MaxDistance-len(.history) {
		 := .current + int32(len(.history)) - int32(.MaxDistance)
		for  := range .table {
			 := .table[].offset
			if  <  {
				 = 0
			} else {
				 =  - .current + int32(.MaxDistance)
			}
			.table[].offset = 
		}
		for  := range .longTable {
			 := .longTable[].offset
			if  <  {
				 = 0
			} else {
				 =  - .current + int32(.MaxDistance)
			}
			.longTable[].offset = 
		}
		.current = int32(.MaxDistance)
	}

	if len(.history)+len() > cap(.history) {
		// history doesn't have enough capacity to hold the new block.
		if cap(.history) == 0 {
			 := max(2*.MaxDistance, 1<<20, len())
			.history = make([]byte, 0, )
		} else {
			// Move down
			 := len(.history) - .MaxDistance
			copy(.history[:.MaxDistance], .history[:])
			.current += int32()
			.history = .history[:.MaxDistance]
		}
	}
	 := int32(len(.history))
	.history = append(.history, ...)

	if len() < 16 {
		return append(, Match{
			Unmatched: len(),
		})
	}

	 = .history
	 := int32(len()) - 10

	const  = 1

	 := 
	 := binary.LittleEndian.Uint64([:])
	var ,  int32

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

		for {
			 := .hashLong()
			 := .hashShort()
			 := .longTable[]
			 := .table[]

			 :=  -  + 1

			 := tableEntry{offset:  + .current, val: uint32()}
			.longTable[] = 
			.table[] = 

			if  != 0 &&  >= 0 && binary.LittleEndian.Uint32([:]) == uint32(>>8) {
				// There is a repeated match at s+1.
				 := extendMatch(, int(+4), int(+5))
				 :=  + 1
				for  > 0 &&  >  && [-1] == [-1] {
					--
					--
				}

				 = append(, Match{
					Unmatched: int( - ),
					Length:     - int(),
					Distance:  int( - ),
				})
				 = int32()
				 = 
				if  >=  {
					break 
				}
				 = binary.LittleEndian.Uint64([:])
				continue
			}

			 :=  - (.offset - .current)
			 :=  - (.offset - .current)
			if  < int32(.MaxDistance) && uint32() == .val {
				 = .offset - .current
				if binary.LittleEndian.Uint32([:]) == uint32() {
					// found a long match (likely at least 8 bytes)
					break
				}
			}
			if  < int32(.MaxDistance) && uint32() == .val {
				 = .offset - .current
				if binary.LittleEndian.Uint32([:]) != uint32() {
					goto 
				}
				// Found a regular match.
				// See if we can find a long match at s+1
				 := binary.LittleEndian.Uint64([+1:])
				 = .hashLong()
				 = .longTable[]
				 =  - (.offset - .current) + 1
				.longTable[] = tableEntry{offset:  + 1 + .current, val: uint32()}
				if  < int32(.MaxDistance) && uint32() == .val {
					 = .offset - .current
					if binary.LittleEndian.Uint32([:]) == uint32() {
						// We found a long match at s+1, so we'll use that instead
						// of the regular match at s.
						++
						break
					}
				}

				 = .offset - .current
				break
			}
		:

			 +=  + (( - ) >> 7)
			if  >  {
				break 
			}
			 = binary.LittleEndian.Uint64([:])
		}

		// A 4-byte match has been found. We'll later see if more than
		// 4 bytes.
		 = 
		 =  - 

		 := extendMatch(, int(+4), int(+4))
		for  > 0 &&  >  && [-1] == [-1] {
			--
			--
		}

		 = append(, Match{
			Unmatched: int( - ),
			Length:     - int(),
			Distance:  int( - ),
		})
		 := 
		 = int32()
		 = 
		if  >=  {
			break 
		}

		// Store some table entries near the start and end of the match.
		 :=  + 1
		 :=  - 2
		 := binary.LittleEndian.Uint64([:])
		 := binary.LittleEndian.Uint64([:])
		 := tableEntry{offset:  + .current, val: uint32()}
		 := tableEntry{offset:  + .current, val: uint32()}
		.longTable[.hashLong()] = 
		.longTable[.hashLong()] = 
		 >>= 8
		 >>= 8
		.offset++
		.offset++
		.val = uint32()
		.val = uint32()
		.table[.hashShort()] = 
		.table[.hashShort()] = 

		 = binary.LittleEndian.Uint64([:])

		// Check offset 2
		if  :=  - ;  != 0 && binary.LittleEndian.Uint32([:]) == uint32() {
			 := extendMatch(, int(+4), int(+4))

			// Store the hashes, since we have them.
			 := .hashShort()
			 := .hashLong()
			 := tableEntry{offset:  + .current, val: uint32()}
			.table[] = 
			.longTable[] = 
			 = append(, Match{
				Length:    - int(),
				Distance: int(),
			})
			 = int32()
			 = 
			,  = , 
			if  >=  {
				break 
			}
			 = binary.LittleEndian.Uint64([:])
		}
	}

	if int() < len() {
		 = append(, Match{
			Unmatched: len() - int(),
		})
	}

	return 
}

func ( *ZDFast) ( uint64) uint32 {
	return uint32((( << 24) * 889523592379) >> (64 - zfastTableBits))
}

func ( *ZDFast) ( uint64) uint32 {
	return uint32(( * 0xcf1bbcdcb7a56463) >> (64 - zdfastLongTableBits))
}