package matchfinder

import (
	
	
)

type tableEntry struct {
	val    uint32
	offset int32
}

const (
	zfastTableBits = 15
	zfastTableSize = 1 << zfastTableBits
	zfastHashLen   = 6

	prime6Bytes = 227718039650203
)

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

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

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

func ( *ZFast) ( []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 = 
		}
		.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() < 10 {
		return append(, Match{
			Unmatched: len(),
		})
	}

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

	const  = 2

	 := 
	 := 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 {
			 := .hash()
			 := .hash( >> 8)
			 := .table[]
			 := .table[]
			 :=  -  + 2

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

			if  != 0 &&  >= 0 && binary.LittleEndian.Uint32([:]) == uint32(>>16) {
				// There is a repeated match at s+2.
				 := extendMatch(, int(+4), int(+6))
				 :=  + 2
				for  > 0 &&  >  && [-1] == [-1] {
					--
					--
				}
				 = append(, Match{
					Unmatched: int( - ),
					Length:     - int(),
					Distance:  int( - ),
				})
				 = int32()
				 = 
				if  >=  {
					break 
				}
				 = binary.LittleEndian.Uint64([:])
				continue
			}

			 :=  - (.offset - .current)
			 :=  - (.offset - .current) + 1
			if  < int32(.MaxDistance) && uint32() == .val {
				 = .offset - .current
				if binary.LittleEndian.Uint32([:]) == uint32() {
					// found a regular match
					break
				}
			}
			if  < int32(.MaxDistance) && uint32(>>8) == .val {
				 = .offset - .current
				if binary.LittleEndian.Uint32([:]) == uint32(>>8) {
					++
					break
				}
			}

			 +=  + (( - ) >> 5)
			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 
		}
		 = binary.LittleEndian.Uint64([:])

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

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

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

	return 
}

func ( *ZFast) ( uint64) uint32 {
	return uint32((( << 16) * prime6Bytes) >> (64 - zfastTableBits))
}