// 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 (
	dFastLongTableBits = 17                      // Bits used in the long match table
	dFastLongTableSize = 1 << dFastLongTableBits // Size of the table
	dFastLongTableMask = dFastLongTableSize - 1  // Mask for table indices. Redundant, but can eliminate bounds checks.
	dFastLongLen       = 8                       // Bytes used for table hash

	dLongTableShardCnt  = 1 << (dFastLongTableBits - dictShardBits) // Number of shards in the table
	dLongTableShardSize = dFastLongTableSize / dLongTableShardCnt   // Size of an individual shard

	dFastShortTableBits = tableBits                // Bits used in the short match table
	dFastShortTableSize = 1 << dFastShortTableBits // Size of the table
	dFastShortTableMask = dFastShortTableSize - 1  // Mask for table indices. Redundant, but can eliminate bounds checks.
	dFastShortLen       = 5                        // Bytes used for table hash

)

type doubleFastEncoder struct {
	fastEncoder
	longTable [dFastLongTableSize]tableEntry
}

type doubleFastEncoderDict struct {
	fastEncoderDict
	longTable           [dFastLongTableSize]tableEntry
	dictLongTable       []tableEntry
	longTableShardDirty [dLongTableShardCnt]bool
}

// Encode mimmics functionality in zstd_dfast.c
func ( *doubleFastEncoder) ( *blockEnc,  []byte) {
	const (
		// Input margin is the number of bytes we read (8)
		// and the maximum we will read ahead (2)
		            = 8 + 2
		 = 16
	)

	// Protect against e.cur wraparound.
	for .cur >= .bufferReset-int32(len(.hist)) {
		if len(.hist) == 0 {
			.table = [dFastShortTableSize]tableEntry{}
			.longTable = [dFastLongTableSize]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 = 
		}
		for  := range .longTable[:] {
			 := .longTable[].offset
			if  <  {
				 = 0
			} else {
				 =  - .cur + .maxMatchOff
			}
			.longTable[].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 >= 1.
	const  = 1

	const  = 8

	// 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 {
		var  int32
		// We allow the encoder to optionally turn off repeat offsets across blocks
		 := len(.sequences) > 2

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

			 := hashLen(, dFastLongTableBits, dFastLongLen)
			 := hashLen(, dFastShortTableBits, dFastShortLen)
			 := .longTable[]
			 := .table[]

			const  = 1
			 :=  -  + 
			 := tableEntry{offset:  + .cur, val: uint32()}
			.longTable[] = 
			.table[] = 

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

					.matchLen = uint32( - zstdMinMatch)

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

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

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

						}
						break 
					}
					 = load6432(, )
					continue
				}
			}
			// Find the offsets of our two matches.
			 :=  - (.offset - .cur)
			 :=  - (.offset - .cur)

			// Check if we have a long match.
			if  < .maxMatchOff && uint32() == .val {
				// Found a long match, likely at least 8 bytes.
				// Reference encoder checks all 8 bytes, we only check 4,
				// but the likelihood of both the first 4 bytes and the hash matching should be enough.
				 = .offset - .cur
				if debugAsserts &&  <=  {
					panic(fmt.Sprintf("s (%d) <= t (%d)", , ))
				}
				if debugAsserts && - > .maxMatchOff {
					panic("s - t >e.maxMatchOff")
				}
				if debugMatches {
					println("long match")
				}
				break
			}

			// Check if we have a short match.
			if  < .maxMatchOff && uint32() == .val {
				// found a regular match
				// See if we can find a long match at s+1
				const  = 1
				 := load6432(, +)
				 = hashLen(, dFastLongTableBits, dFastLongLen)
				 = .longTable[]
				 =  - (.offset - .cur) + 

				// We can store it, since we have at least a 4 byte match.
				.longTable[] = tableEntry{offset:  +  + .cur, val: uint32()}
				if  < .maxMatchOff && uint32() == .val {
					// Found a long match, likely at least 8 bytes.
					// Reference encoder checks all 8 bytes, we only check 4,
					// but the likelihood of both the first 4 bytes and the hash matching should be enough.
					 = .offset - .cur
					 += 
					if debugMatches {
						println("long match (after short)")
					}
					break
				}

				 = .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")
				}
				if debugMatches {
					println("short match")
				}
				break
			}

			// No match found, move forward in input.
			 +=  + (( - ) >> ( - 1))
			if  >=  {
				break 
			}
			 = load6432(, )
		}

		// A 4-byte match has been found. Update recent offsets.
		// 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
		 := max(-.maxMatchOff, 0)
		for  >  &&  >  && [-1] == [-1] &&  < maxMatchLength {
			--
			--
			++
		}

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

		// Index match start+1 (long) and start+2 (short)
		 :=  -  + 1
		// Index match end-2 (long) and end-1 (short)
		 :=  - 2

		 := load6432(, )
		 := load6432(, )
		 := tableEntry{offset:  + .cur, val: uint32()}
		 := tableEntry{offset:  + .cur, val: uint32()}
		.longTable[hashLen(, dFastLongTableBits, dFastLongLen)] = 
		.longTable[hashLen(, dFastLongTableBits, dFastLongLen)] = 
		 >>= 8
		 >>= 8
		.offset++
		.offset++
		.val = uint32()
		.val = uint32()
		.table[hashLen(, dFastShortTableBits, dFastShortLen)] = 
		.table[hashLen(, dFastShortTableBits, dFastShortLen)] = 

		 = load6432(, )

		if ! {
			continue
		}

		// Check offset 2
		for {
			 :=  - 
			if load3232(, ) != uint32() {
				// Do regular search
				break
			}

			// Store this, since we have it.
			 := hashLen(, dFastShortTableBits, dFastShortLen)
			 := hashLen(, dFastLongTableBits, dFastLongLen)

			// We have at least 4 byte match.
			// No need to check backwards. We come straight from a match
			 := 4 + .matchlen(+4, +4, )

			 := tableEntry{offset:  + .cur, val: uint32()}
			.longTable[] = 
			.table[] = 
			.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  >=  {
				// Finished
				break 
			}
			 = 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 ( *doubleFastEncoder) ( *blockEnc,  []byte) {
	const (
		// Input margin is the number of bytes we read (8)
		// and the maximum we will read ahead (2)
		            = 8 + 2
		 = 16
	)

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

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

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

	const  = 8

	// 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 {
		var  int32
		for {

			 := hashLen(, dFastLongTableBits, dFastLongLen)
			 := hashLen(, dFastShortTableBits, dFastShortLen)
			 := .longTable[]
			 := .table[]

			const  = 1
			 :=  -  + 
			 := tableEntry{offset:  + .cur, val: uint32()}
			.longTable[] = 
			.table[] = 

			if len(.sequences) > 2 {
				if load3232(, ) == uint32(>>(*8)) {
					// Consider history as well.
					var  seq
					//length := 4 + e.matchlen(s+4+repOff, repIndex+4, src)
					 := 4 + int32(matchLen([+4+:], [+4:]))

					.matchLen = uint32( - zstdMinMatch)

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

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

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

						}
						break 
					}
					 = load6432(, )
					continue
				}
			}
			// Find the offsets of our two matches.
			 :=  - (.offset - .cur)
			 :=  - (.offset - .cur)

			// Check if we have a long match.
			if  < .maxMatchOff && uint32() == .val {
				// Found a long match, likely at least 8 bytes.
				// Reference encoder checks all 8 bytes, we only check 4,
				// but the likelihood of both the first 4 bytes and the hash matching should be enough.
				 = .offset - .cur
				if debugAsserts &&  <=  {
					panic(fmt.Sprintf("s (%d) <= t (%d). cur: %d", , , .cur))
				}
				if debugAsserts && - > .maxMatchOff {
					panic("s - t >e.maxMatchOff")
				}
				if debugMatches {
					println("long match")
				}
				break
			}

			// Check if we have a short match.
			if  < .maxMatchOff && uint32() == .val {
				// found a regular match
				// See if we can find a long match at s+1
				const  = 1
				 := load6432(, +)
				 = hashLen(, dFastLongTableBits, dFastLongLen)
				 = .longTable[]
				 =  - (.offset - .cur) + 

				// We can store it, since we have at least a 4 byte match.
				.longTable[] = tableEntry{offset:  +  + .cur, val: uint32()}
				if  < .maxMatchOff && uint32() == .val {
					// Found a long match, likely at least 8 bytes.
					// Reference encoder checks all 8 bytes, we only check 4,
					// but the likelihood of both the first 4 bytes and the hash matching should be enough.
					 = .offset - .cur
					 += 
					if debugMatches {
						println("long match (after short)")
					}
					break
				}

				 = .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")
				}
				if debugMatches {
					println("short match")
				}
				break
			}

			// No match found, move forward in input.
			 +=  + (( - ) >> ( - 1))
			if  >=  {
				break 
			}
			 = load6432(, )
		}

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

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

		// Extend the 4-byte match as long as possible.
		//l := e.matchlen(s+4, t+4, src) + 4
		 := int32(matchLen([+4:], [+4:])) + 4

		// Extend backwards
		 := max(-.maxMatchOff, 0)
		for  >  &&  >  && [-1] == [-1] {
			--
			--
			++
		}

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

		// Index match start+1 (long) and start+2 (short)
		 :=  -  + 1
		// Index match end-2 (long) and end-1 (short)
		 :=  - 2

		 := load6432(, )
		 := load6432(, )
		 := tableEntry{offset:  + .cur, val: uint32()}
		 := tableEntry{offset:  + .cur, val: uint32()}
		.longTable[hashLen(, dFastLongTableBits, dFastLongLen)] = 
		.longTable[hashLen(, dFastLongTableBits, dFastLongLen)] = 
		 >>= 8
		 >>= 8
		.offset++
		.offset++
		.val = uint32()
		.val = uint32()
		.table[hashLen(, dFastShortTableBits, dFastShortLen)] = 
		.table[hashLen(, dFastShortTableBits, dFastShortLen)] = 

		 = load6432(, )

		if len(.sequences) <= 2 {
			continue
		}

		// Check offset 2
		for {
			 :=  - 
			if load3232(, ) != uint32() {
				// Do regular search
				break
			}

			// Store this, since we have it.
			 := hashLen(>>8, dFastShortTableBits, dFastShortLen)
			 := hashLen(, dFastLongTableBits, dFastLongLen)

			// We have at least 4 byte match.
			// No need to check backwards. We come straight from a match
			//l := 4 + e.matchlen(s+4, o2+4, src)
			 := 4 + int32(matchLen([+4:], [+4:]))

			 := tableEntry{offset:  + .cur, val: uint32()}
			.longTable[] = 
			.table[] = 
			.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  >=  {
				// Finished
				break 
			}
			 = 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 ( *doubleFastEncoderDict) ( *blockEnc,  []byte) {
	const (
		// Input margin is the number of bytes we read (8)
		// and the maximum we will read ahead (2)
		            = 8 + 2
		 = 16
	)

	// Protect against e.cur wraparound.
	for .cur >= .bufferReset-int32(len(.hist)) {
		if len(.hist) == 0 {
			for  := range .table[:] {
				.table[] = tableEntry{}
			}
			for  := range .longTable[:] {
				.longTable[] = tableEntry{}
			}
			.markAllShardsDirty()
			.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 = 
		}
		for  := range .longTable[:] {
			 := .longTable[].offset
			if  <  {
				 = 0
			} else {
				 =  - .cur + .maxMatchOff
			}
			.longTable[].offset = 
		}
		.markAllShardsDirty()
		.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 >= 1.
	const  = 1

	const  = 8

	// 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 {
		var  int32
		// We allow the encoder to optionally turn off repeat offsets across blocks
		 := len(.sequences) > 2

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

			 := hashLen(, dFastLongTableBits, dFastLongLen)
			 := hashLen(, dFastShortTableBits, dFastShortLen)
			 := .longTable[]
			 := .table[]

			const  = 1
			 :=  -  + 
			 := tableEntry{offset:  + .cur, val: uint32()}
			.longTable[] = 
			.markLongShardDirty()
			.table[] = 
			.markShardDirty()

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

					.matchLen = uint32( - zstdMinMatch)

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

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

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

						}
						break 
					}
					 = load6432(, )
					continue
				}
			}
			// Find the offsets of our two matches.
			 :=  - (.offset - .cur)
			 :=  - (.offset - .cur)

			// Check if we have a long match.
			if  < .maxMatchOff && uint32() == .val {
				// Found a long match, likely at least 8 bytes.
				// Reference encoder checks all 8 bytes, we only check 4,
				// but the likelihood of both the first 4 bytes and the hash matching should be enough.
				 = .offset - .cur
				if debugAsserts &&  <=  {
					panic(fmt.Sprintf("s (%d) <= t (%d)", , ))
				}
				if debugAsserts && - > .maxMatchOff {
					panic("s - t >e.maxMatchOff")
				}
				if debugMatches {
					println("long match")
				}
				break
			}

			// Check if we have a short match.
			if  < .maxMatchOff && uint32() == .val {
				// found a regular match
				// See if we can find a long match at s+1
				const  = 1
				 := load6432(, +)
				 = hashLen(, dFastLongTableBits, dFastLongLen)
				 = .longTable[]
				 =  - (.offset - .cur) + 

				// We can store it, since we have at least a 4 byte match.
				.longTable[] = tableEntry{offset:  +  + .cur, val: uint32()}
				.markLongShardDirty()
				if  < .maxMatchOff && uint32() == .val {
					// Found a long match, likely at least 8 bytes.
					// Reference encoder checks all 8 bytes, we only check 4,
					// but the likelihood of both the first 4 bytes and the hash matching should be enough.
					 = .offset - .cur
					 += 
					if debugMatches {
						println("long match (after short)")
					}
					break
				}

				 = .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")
				}
				if debugMatches {
					println("short match")
				}
				break
			}

			// No match found, move forward in input.
			 +=  + (( - ) >> ( - 1))
			if  >=  {
				break 
			}
			 = load6432(, )
		}

		// A 4-byte match has been found. Update recent offsets.
		// 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
		 := max(-.maxMatchOff, 0)
		for  >  &&  >  && [-1] == [-1] &&  < maxMatchLength {
			--
			--
			++
		}

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

		// Index match start+1 (long) and start+2 (short)
		 :=  -  + 1
		// Index match end-2 (long) and end-1 (short)
		 :=  - 2

		 := load6432(, )
		 := load6432(, )
		 := tableEntry{offset:  + .cur, val: uint32()}
		 := tableEntry{offset:  + .cur, val: uint32()}
		 := hashLen(, dFastLongTableBits, dFastLongLen)
		 := hashLen(, dFastLongTableBits, dFastLongLen)
		.longTable[] = 
		.longTable[] = 
		.markLongShardDirty()
		.markLongShardDirty()
		 >>= 8
		 >>= 8
		.offset++
		.offset++
		.val = uint32()
		.val = uint32()
		 := hashLen(, dFastShortTableBits, dFastShortLen)
		 := hashLen(, dFastShortTableBits, dFastShortLen)
		.table[] = 
		.markShardDirty()
		.table[] = 
		.markShardDirty()

		 = load6432(, )

		if ! {
			continue
		}

		// Check offset 2
		for {
			 :=  - 
			if load3232(, ) != uint32() {
				// Do regular search
				break
			}

			// Store this, since we have it.
			 := hashLen(, dFastLongTableBits, dFastLongLen)
			 := hashLen(, dFastShortTableBits, dFastShortLen)

			// We have at least 4 byte match.
			// No need to check backwards. We come straight from a match
			 := 4 + .matchlen(+4, +4, )

			 := tableEntry{offset:  + .cur, val: uint32()}
			.longTable[] = 
			.markLongShardDirty()
			.table[] = 
			.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  >=  {
				// Finished
				break 
			}
			 = 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)
	}
	// If we encoded more than 64K mark all dirty.
	if len() > 64<<10 {
		.markAllShardsDirty()
	}
}

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

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

	// Init or copy dict table
	if len(.dictLongTable) != len(.longTable) || .id != .lastDictID {
		if len(.dictLongTable) != len(.longTable) {
			.dictLongTable = make([]tableEntry, len(.longTable))
		}
		if len(.content) >= 8 {
			 := load6432(.content, 0)
			.dictLongTable[hashLen(, dFastLongTableBits, dFastLongLen)] = tableEntry{
				val:    uint32(),
				offset: .maxMatchOff,
			}
			 := int32(len(.content)) - 8 + .maxMatchOff
			for  := .maxMatchOff + 1;  < ; ++ {
				 = >>8 | (uint64(.content[-.maxMatchOff+7]) << 56)
				.dictLongTable[hashLen(, dFastLongTableBits, dFastLongLen)] = tableEntry{
					val:    uint32(),
					offset: ,
				}
			}
		}
		.lastDictID = .id
		 = true
	}
	// Reset table to initial state
	.cur = .maxMatchOff

	 := 0
	if ! {
		for  := range .longTableShardDirty {
			if .longTableShardDirty[] {
				++
			}
		}
	}

	if  ||  > dLongTableShardCnt/2 {
		//copy(e.longTable[:], e.dictLongTable)
		.longTable = *(*[dFastLongTableSize]tableEntry)(.dictLongTable)
		for  := range .longTableShardDirty {
			.longTableShardDirty[] = false
		}
		return
	}
	for  := range .longTableShardDirty {
		if !.longTableShardDirty[] {
			continue
		}

		// copy(e.longTable[i*dLongTableShardSize:(i+1)*dLongTableShardSize], e.dictLongTable[i*dLongTableShardSize:(i+1)*dLongTableShardSize])
		*(*[dLongTableShardSize]tableEntry)(.longTable[*dLongTableShardSize:]) = *(*[dLongTableShardSize]tableEntry)(.dictLongTable[*dLongTableShardSize:])

		.longTableShardDirty[] = false
	}
}

func ( *doubleFastEncoderDict) ( uint32) {
	.longTableShardDirty[/dLongTableShardSize] = true
}