package zstd

import (
	
	
	
	
	
	
	

	
)

type dict struct {
	id uint32

	litEnc              *huff0.Scratch
	llDec, ofDec, mlDec sequenceDec
	offsets             [3]int
	content             []byte
}

const dictMagic = "\x37\xa4\x30\xec"

// Maximum dictionary size for the reference implementation (1.5.3) is 2 GiB.
const dictMaxLength = 1 << 31

// ID returns the dictionary id or 0 if d is nil.
func ( *dict) () uint32 {
	if  == nil {
		return 0
	}
	return .id
}

// ContentSize returns the dictionary content size or 0 if d is nil.
func ( *dict) () int {
	if  == nil {
		return 0
	}
	return len(.content)
}

// Content returns the dictionary content.
func ( *dict) () []byte {
	if  == nil {
		return nil
	}
	return .content
}

// Offsets returns the initial offsets.
func ( *dict) () [3]int {
	if  == nil {
		return [3]int{}
	}
	return .offsets
}

// LitEncoder returns the literal encoder.
func ( *dict) () *huff0.Scratch {
	if  == nil {
		return nil
	}
	return .litEnc
}

// Load a dictionary as described in
// https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#dictionary-format
func loadDict( []byte) (*dict, error) {
	// Check static field size.
	if len() <= 8+(3*4) {
		return nil, io.ErrUnexpectedEOF
	}
	 := dict{
		llDec: sequenceDec{fse: &fseDecoder{}},
		ofDec: sequenceDec{fse: &fseDecoder{}},
		mlDec: sequenceDec{fse: &fseDecoder{}},
	}
	if string([:4]) != dictMagic {
		return nil, ErrMagicMismatch
	}
	.id = binary.LittleEndian.Uint32([4:8])
	if .id == 0 {
		return nil, errors.New("dictionaries cannot have ID 0")
	}

	// Read literal table
	var  error
	.litEnc, ,  = huff0.ReadTable([8:], nil)
	if  != nil {
		return nil, fmt.Errorf("loading literal table: %w", )
	}
	.litEnc.Reuse = huff0.ReusePolicyMust

	 := byteReader{
		b:   ,
		off: 0,
	}
	 := func( tableIndex,  *fseDecoder) error {
		if  := .readNCount(&, uint16(maxTableSymbol[]));  != nil {
			return 
		}
		if .overread() {
			return io.ErrUnexpectedEOF
		}
		 = .transform(symbolTableX[])
		if  != nil {
			println("Transform table error:", )
			return 
		}
		if debugDecoder || debugEncoder {
			println("Read table ok", "symbolLen:", .symbolLen)
		}
		// Set decoders as predefined so they aren't reused.
		.preDefined = true
		return nil
	}

	if  := (tableOffsets, .ofDec.fse);  != nil {
		return nil, 
	}
	if  := (tableMatchLengths, .mlDec.fse);  != nil {
		return nil, 
	}
	if  := (tableLiteralLengths, .llDec.fse);  != nil {
		return nil, 
	}
	if .remain() < 12 {
		return nil, io.ErrUnexpectedEOF
	}

	.offsets[0] = int(.Uint32())
	.advance(4)
	.offsets[1] = int(.Uint32())
	.advance(4)
	.offsets[2] = int(.Uint32())
	.advance(4)
	if .offsets[0] <= 0 || .offsets[1] <= 0 || .offsets[2] <= 0 {
		return nil, errors.New("invalid offset in dictionary")
	}
	.content = make([]byte, .remain())
	copy(.content, .unread())
	if .offsets[0] > len(.content) || .offsets[1] > len(.content) || .offsets[2] > len(.content) {
		return nil, fmt.Errorf("initial offset bigger than dictionary content size %d, offsets: %v", len(.content), .offsets)
	}

	return &, nil
}

// InspectDictionary loads a zstd dictionary and provides functions to inspect the content.
func ( []byte) (interface {
	() uint32
	() int
	() []byte
	() [3]int
	() *huff0.Scratch
}, error) {
	initPredefined()
	,  := loadDict()
	return , 
}

type BuildDictOptions struct {
	// Dictionary ID.
	ID uint32

	// Content to use to create dictionary tables.
	Contents [][]byte

	// History to use for all blocks.
	History []byte

	// Offsets to use.
	Offsets [3]int

	// CompatV155 will make the dictionary compatible with Zstd v1.5.5 and earlier.
	// See https://github.com/facebook/zstd/issues/3724
	CompatV155 bool

	// Use the specified encoder level.
	// The dictionary will be built using the specified encoder level,
	// which will reflect speed and make the dictionary tailored for that level.
	// If not set SpeedBestCompression will be used.
	Level EncoderLevel

	// DebugOut will write stats and other details here if set.
	DebugOut io.Writer
}

func ( BuildDictOptions) ([]byte, error) {
	initPredefined()
	 := .History
	 := .Contents
	 := .DebugOut != nil
	 := func( ...interface{}) {
		if .DebugOut != nil {
			fmt.Fprintln(.DebugOut, ...)
		}
	}
	 := func( string,  ...interface{}) {
		if .DebugOut != nil {
			fmt.Fprintf(.DebugOut, , ...)
		}
	}
	 := func( ...interface{}) {
		if .DebugOut != nil {
			fmt.Fprint(.DebugOut, ...)
		}
	}

	if int64(len()) > dictMaxLength {
		return nil, fmt.Errorf("dictionary of size %d > %d", len(), int64(dictMaxLength))
	}
	if len() < 8 {
		return nil, fmt.Errorf("dictionary of size %d < %d", len(), 8)
	}
	if len() == 0 {
		return nil, errors.New("no content provided")
	}
	 := dict{
		id:      .ID,
		litEnc:  nil,
		llDec:   sequenceDec{},
		ofDec:   sequenceDec{},
		mlDec:   sequenceDec{},
		offsets: .Offsets,
		content: ,
	}
	 := blockEnc{lowMem: false}
	.init()
	 := encoder(&bestFastEncoder{fastBase: fastBase{maxMatchOff: int32(maxMatchLen), bufferReset: math.MaxInt32 - int32(maxMatchLen*2), lowMem: false}})
	if .Level != 0 {
		 := encoderOptions{
			level:      .Level,
			blockSize:  maxMatchLen,
			windowSize: maxMatchLen,
			dict:       &,
			lowMem:     false,
		}
		 = .encoder()
	} else {
		.Level = SpeedBestCompression
	}
	var (
		 [256]int
		     [256]int
		     [256]int
		     [256]int
	)
	 := func( *[256]int,  []byte) {
		for ,  := range  {
			[]++
		}
	}
	 := func( *[256]int,  *[256]uint32) {
		for ,  := range  {
			[] += int()
		}
	}
	 := 0
	 := 0
	 := 0
	 := make(map[uint32]int, 1000)
	for ,  := range  {
		.reset(nil)
		if len() < 8 {
			continue
		}
		++
		.Reset(&, true)
		.Encode(&, )
		(&, .literals)
		 += len(.literals)
		if len(.sequences) == 0 {
			continue
		}
		 += len(.sequences)
		.genCodes()
		(&, .coders.llEnc.Histogram())
		(&, .coders.mlEnc.Histogram())
		(&, .coders.ofEnc.Histogram())
		for ,  := range .sequences {
			if  > 3 {
				break
			}
			 := .offset
			if  == 0 {
				continue
			}
			if int() >= len(.History) {
				continue
			}
			if  > 3 {
				[-3]++
			} else {
				[uint32(.Offsets[-1])]++
			}
		}
	}
	// Find most used offsets.
	var  []uint32
	for  := range  {
		 = append(, )
	}
	sort.Slice(, func(,  int) bool {
		,  := [], []
		if  ==  {
			// Prefer the longer offset
			return [] > []
		}
		return [[]] > [[]]
	})
	if len() > 3 {
		if  {
			("Offsets:")
			for ,  := range  {
				if  > 20 {
					break
				}
				("[%d: %d],", , [])
			}
			("")
		}

		 = [:3]
	}
	for ,  := range  {
		.Offsets[] = int()
	}
	if  {
		("New repeat offsets", .Offsets)
	}

	if  == 0 ||  == 0 {
		return nil, fmt.Errorf("%d blocks, %d sequences found", , )
	}
	if  {
		("Sequences:", , "Blocks:", , "Literals:", )
	}
	if / < 512 {
		// Use 512 as minimum.
		 =  / 512
		if  == 0 {
			 = 1
		}
	}
	 := func( *fseEncoder,  *[256]int) ([]byte, error) {
		 := .Histogram()
		var  uint8
		var  int
		var  int
		for ,  := range  {
			if  > 0 {
				 =  / 
				if  == 0 {
					 = 1
				}
			}
			if  >  {
				 = 
			}
			if  != 0 {
				 = uint8()
			}
			 += 
			[] = uint32()
		}

		// Ensure we aren't trying to represent RLE.
		if  ==  {
			for  := range  {
				if uint8() ==  {
					++
					++
					[+1] = 1
					if  > 1 {
						break
					}
				}
				if [0] == 0 {
					++
					[] = 1
					if  > 1 {
						break
					}
				}
			}
		}

		.HistogramFinished(, )
		.reUsed = false
		.useRLE = false
		 := .normalizeCount()
		if  != nil {
			return nil, 
		}
		if  {
			("RAW:", .count[:+1], "NORM:", .norm[:+1], "LEN:", )
		}
		return .writeCount(nil)
	}
	if  {
		("Literal lengths: ")
	}
	,  := (.coders.llEnc, &)
	if  != nil {
		return nil, 
	}
	if  {
		("Match lengths: ")
	}
	,  := (.coders.mlEnc, &)
	if  != nil {
		return nil, 
	}
	if  {
		("Offsets: ")
	}
	,  := (.coders.ofEnc, &)
	if  != nil {
		return nil, 
	}

	// Literal table
	 := 
	if  > huff0.BlockSizeMax/2 {
		 = huff0.BlockSizeMax / 2
	}
	 := make([]byte, 0, )
	// Target size
	 :=  / 
	if  < 1 {
		 = 1
	}
	if  {
		("Huffman weights:")
	}
	for ,  := range [:] {
		if  > 0 {
			 =  / 
			// Allow all entries to be represented.
			if  == 0 {
				 = 1
			}
			 = append(, bytes.Repeat([]byte{byte()}, )...)
			if  {
				("[%d: %d], ", , )
			}
		}
	}
	if .CompatV155 && [255]/ == 0 {
		 = append(, 255)
	}
	 := &huff0.Scratch{TableLog: 11}
	for  := 0;  < 255; ++ {
		 = &huff0.Scratch{TableLog: 11}
		_, _,  = huff0.Compress1X(, )
		if  == nil {
			break
		}
		if  {
			("Try %d: Huffman error: %v\n", +1, )
		}
		 = [:0]
		if  == 250 {
			if  {
				("Huffman: Bailing out with predefined table")
			}

			// Bail out.... Just generate something
			 = append(, bytes.Repeat([]byte{255}, 10000)...)
			for  := 0;  < 128; ++ {
				 = append(, byte())
			}
			continue
		}
		if errors.Is(, huff0.ErrIncompressible) {
			// Try truncating least common.
			for ,  := range [:] {
				if  > 0 {
					 =  / ( * ( + 1))
					if  > 0 {
						 = append(, bytes.Repeat([]byte{byte()}, )...)
					}
				}
			}
			if .CompatV155 && len() > 0 && [len()-1] != 255 {
				 = append(, 255)
			}
			if len() == 0 {
				 = append(, 0, 255)
			}
		}
		if errors.Is(, huff0.ErrUseRLE) {
			for ,  := range [:] {
				 =  / ( * ( + 1))
				// Allow all entries to be represented.
				if  == 0 {
					 = 1
				}
				 = append(, bytes.Repeat([]byte{byte()}, )...)
			}
		}
	}

	var  bytes.Buffer
	.Write([]byte(dictMagic))
	.Write(binary.LittleEndian.AppendUint32(nil, .ID))
	.Write(.OutTable)
	if  {
		("huff table:", len(.OutTable), "bytes")
		("of table:", len(), "bytes")
		("ml table:", len(), "bytes")
		("ll table:", len(), "bytes")
	}
	.Write()
	.Write()
	.Write()
	.Write(binary.LittleEndian.AppendUint32(nil, uint32(.Offsets[0])))
	.Write(binary.LittleEndian.AppendUint32(nil, uint32(.Offsets[1])))
	.Write(binary.LittleEndian.AppendUint32(nil, uint32(.Offsets[2])))
	.Write()
	if  {
		,  := loadDict(.Bytes())
		if  != nil {
			panic()
		}
		,  := InspectDictionary(.Bytes())
		if  != nil {
			panic()
		}
		("ID:", .ID())
		("Content size:", .ContentSize())
		("Encoder:", .LitEncoder() != nil)
		("Offsets:", .Offsets())
		var  int
		for ,  := range  {
			 += len()
		}

		 := func( ...EOption) int {
			,  := NewWriter(nil, ...)
			if  != nil {
				panic()
			}
			defer .Close()
			var  []byte
			var  int
			for ,  := range  {
				 = .EncodeAll(, [:0])
				 += len()
			}
			return 
		}
		 := (WithEncoderLevel(.Level))
		 := (WithEncoderLevel(.Level), WithEncoderDict(.Bytes()))
		("Input size:", )
		("Plain Compressed:", )
		("Dict Compressed:", )
		("Saved:", -, (-)/len(), "bytes per input (rounded down)")
	}
	return .Bytes(), nil
}