package flate
import (
)
const (
NoCompression = 0
BestSpeed = 1
BestCompression = 9
DefaultCompression = -1
HuffmanOnly = -2
)
const (
logWindowSize = 15
windowSize = 1 << logWindowSize
windowMask = windowSize - 1
minMatchLength = 4
maxMatchLength = 258
minOffsetSize = 1
maxFlateBlockTokens = 1 << 15
maxStoreBlockSize = 65535
hashBits = 17
hashSize = 1 << hashBits
hashMask = (1 << hashBits) - 1
maxHashOffset = 1 << 28
skipNever = math.MaxInt32
)
type compressionLevel struct {
good int32
lazy int32
nice int32
chain int32
level int
}
var levels = []compressionLevel{
{},
{0, 0, 0, 0, 1},
{0, 0, 0, 0, 2},
{0, 0, 0, 0, 3},
{0, 0, 0, 0, 4},
{0, 0, 0, 0, 5},
{0, 0, 0, 0, 6},
{8, 12, 16, 24, 7},
{16, 30, 40, 64, 8},
{32, 258, 258, 1024, 9},
}
type advancedState struct {
length int32
offset int32
maxInsertIndex int32
chainHead int32
hashOffset int32
literalCounter uint16
index int32
hashMatch [maxMatchLength + minMatchLength]uint32
hashHead [hashSize]int32
hashPrev [windowSize]int32
}
type compressor struct {
compressionLevel
h *huffmanEncoder
w *huffmanBitWriter
fill func(*compressor, []byte) int
step func(*compressor)
window []byte
windowEnd int32
blockStart int32
err error
tokens tokens
fast fastEnc
state *advancedState
sync bool
byteAvailable bool
}
func ( *compressor) ( []byte) int {
:= .state
if .index >= 2*windowSize-(minMatchLength+maxMatchLength) {
copy(.window[:], .window[windowSize:2*windowSize])
.index -= windowSize
.windowEnd -= windowSize
if .blockStart >= windowSize {
.blockStart -= windowSize
} else {
.blockStart = math.MaxInt32
}
.hashOffset += windowSize
if .hashOffset > maxHashOffset {
:= .hashOffset - 1
.hashOffset -=
.chainHead -=
for , := range &.hashPrev {
.hashPrev[] = max(-, 0)
}
for , := range &.hashHead {
.hashHead[] = max(-, 0)
}
}
}
:= copy(.window[.windowEnd:], )
.windowEnd += int32()
return
}
func ( *compressor) ( *tokens, int32, bool) error {
if > 0 || {
var []byte
if .blockStart <= {
= .window[.blockStart:]
}
.blockStart =
.w.writeBlockDynamic(, , , .sync)
return .w.err
}
return nil
}
func ( *compressor) ( *tokens, int32, bool) error {
if > 0 || {
if .blockStart <= {
:= .window[.blockStart:]
if int(.n) > len()-(len()>>6) {
.w.writeBlockHuff(, , .sync)
} else {
.w.writeBlockDynamic(, , , .sync)
}
} else {
.w.writeBlock(, , nil)
}
.blockStart =
return .w.err
}
return nil
}
func ( *compressor) ( []byte) {
if .level <= 0 {
return
}
if .fast != nil {
if len() > maxMatchOffset {
= [len()-maxMatchOffset:]
}
.fast.encode(&.tokens, )
.tokens.Reset()
return
}
:= .state
if len() > windowSize {
= [len()-windowSize:]
}
:= int32(copy(.window[.windowEnd:], ))
:= ( + 256 - minMatchLength) / 256
for := range {
:= * 256
:= min(+256+minMatchLength-1, )
:= .window[:]
:= len() - minMatchLength + 1
if <= 0 {
continue
}
:= .hashMatch[:]
bulkHash4(, )
var uint32
for , := range {
:= int32() +
= & hashMask
.hashPrev[&windowMask] = .hashHead[]
.hashHead[] = + .hashOffset
}
}
.windowEnd +=
.index =
.blockStart = .windowEnd
}
func ( *compressor) ( int32, int32, int32) (, int32, bool) {
:= min(, maxMatchLength)
:= .window[0 : +]
:= min(.nice, int32(len())-)
:= .chain
= minMatchLength - 1
:= [+]
:= [:]
:= max(-windowSize, 0)
= 0
:= 4
const = 3
for := ; > 0; -- {
if == [+] {
:= int32(matchLen([:+], ))
if > {
if .chain >= 100 {
:= .h.bitLengthRaw([:]) - int(offsetExtraBits[offsetCode(uint32(-))]) - - int(lengthExtraBits[lengthCodes[(-3)&255]])
if <= {
goto
}
=
}
=
= -
= true
if >= {
break
}
= [+]
}
}
:
if <= {
break
}
= .state.hashPrev[&windowMask] - .state.hashOffset
if < {
break
}
}
return
}
func ( *compressor) ( []byte) error {
if .w.writeStoredHeader(len(), false); .w.err != nil {
return .w.err
}
.w.writeBytes()
return .w.err
}
func ( []byte) uint32 {
return hash4u(loadLE32(, 0), hashBits)
}
func ( uint32, uint8) uint32 {
return ( * prime4bytes) >> (32 - )
}
func ( []byte, []uint32) {
if len() < 4 {
return
}
:= loadLE32(, 0)
[0] = hash4u(, hashBits)
:= len() - 4 + 1
for := 1; < ; ++ {
= ( >> 8) | uint32([+3])<<24
[] = hash4u(, hashBits)
}
}
func ( *compressor) () {
.window = make([]byte, 2*windowSize)
.byteAvailable = false
.err = nil
if .state == nil {
return
}
:= .state
.index = 0
.hashOffset = 1
.length = minMatchLength - 1
.offset = 0
.chainHead = -1
}
func ( *compressor) (, , int32) (, int32) {
const = 2
:= .state
if >= maxMatchLength- {
return ,
}
:= .index - 1
if + >= .maxInsertIndex {
return ,
}
:= min(, maxMatchLength+) +
:= max(.index-windowSize, 0)
:= hash4(.window[+:])
:= .hashHead[] - .hashOffset -
if - == || <= + {
return ,
}
:= int32(matchLen(.window[+:], .window[+:]))
if <= {
return ,
}
=
= -
for := int32( - 1); >= 0; -- {
if >= maxMatchLength || .window[+] != .window[+] {
for := range + 1 {
.tokens.AddLiteral(.window[+])
if .tokens.n == maxFlateBlockTokens {
if .err = .writeBlock(&.tokens, .index, false); .err != nil {
return ,
}
.tokens.Reset()
}
.index++
if .index < .maxInsertIndex {
:= hash4(.window[.index:])
:= .hashHead[]
.chainHead =
.hashPrev[.index&windowMask] =
.hashHead[] = .index + .hashOffset
}
}
break
}
++
}
return ,
}
func ( *compressor) () bool {
:= .state
:= int32(.literalCounter) - .chain
if <= 0 {
return true
}
= 1 + >>6
for range {
if .index >= .windowEnd-1 {
break
}
.tokens.AddLiteral(.window[.index-1])
if .tokens.n == maxFlateBlockTokens {
if .err = .writeBlock(&.tokens, .index, false); .err != nil {
return false
}
.tokens.Reset()
}
if .index < .maxInsertIndex {
:= hash4(.window[.index:])
:= .hashHead[]
.chainHead =
.hashPrev[.index&windowMask] =
.hashHead[] = .index + .hashOffset
}
.index++
}
.tokens.AddLiteral(.window[.index-1])
.byteAvailable = false
if .tokens.n == maxFlateBlockTokens {
if .err = .writeBlock(&.tokens, .index, false); .err != nil {
return false
}
.tokens.Reset()
}
return true
}
func ( *compressor) () {
:= .state
if .windowEnd-.index < minMatchLength+maxMatchLength && !.sync {
return
}
if .windowEnd != .index && .chain > 100 {
if .h == nil {
.h = newHuffmanEncoder(maxFlateBlockTokens)
}
var [256]uint16
:= .window[.index:.windowEnd]
= [:min(len(), maxFlateBlockTokens)]
for , := range {
[]++
}
.h.generate([:], 15)
}
.maxInsertIndex = .windowEnd - (minMatchLength - 1)
for {
:= .windowEnd - .index
if < minMatchLength+maxMatchLength {
if !.sync {
return
}
if == 0 {
if .byteAvailable {
.tokens.AddLiteral(.window[.index-1])
.byteAvailable = false
}
if .tokens.n > 0 {
if .err = .writeBlock(&.tokens, .index, false); .err != nil {
return
}
.tokens.Reset()
}
return
}
}
if .index < .maxInsertIndex {
:= hash4(.window[.index:])
:= .hashHead[]
.chainHead =
.hashPrev[.index&windowMask] =
.hashHead[] = .index + .hashOffset
}
:= .length
:= .offset
.length = minMatchLength - 1
.offset = 0
:= max(.index-windowSize, 0)
if .chainHead-.hashOffset >= && > && < .lazy {
if , , := .findMatch(.index, .chainHead-.hashOffset, ); {
.length =
.offset =
}
}
if >= minMatchLength && .length <= {
, = .tryBetterMatchAtEnd(, , )
if .err != nil {
return
}
.tokens.AddMatch(uint32(-3), uint32(-minOffsetSize))
:= .index + - 1
:= min(, .maxInsertIndex)
+= minMatchLength - 1
:= min(.index+1, .maxInsertIndex)
:= .window[:]
:= len() - minMatchLength + 1
if > 0 {
:= .hashMatch[:]
bulkHash4(, )
var uint32
for , := range {
:= int32() +
= & hashMask
.hashPrev[&windowMask] = .hashHead[]
.hashHead[] = + .hashOffset
}
}
.index =
.byteAvailable = false
.length = minMatchLength - 1
if .tokens.n == maxFlateBlockTokens {
if .err = .writeBlock(&.tokens, .index, false); .err != nil {
return
}
.tokens.Reset()
}
.literalCounter = 0
continue
}
if .length >= minMatchLength {
.literalCounter = 0
}
if .byteAvailable {
.literalCounter++
.tokens.AddLiteral(.window[.index-1])
if .tokens.n == maxFlateBlockTokens {
if .err = .writeBlock(&.tokens, .index, false); .err != nil {
return
}
.tokens.Reset()
}
.index++
if !.skipLiterals() {
return
}
} else {
.index++
.byteAvailable = true
}
}
}
func ( *compressor) () {
if .windowEnd > 0 && (.windowEnd == maxStoreBlockSize || .sync) {
.err = .writeStoredBlock(.window[:.windowEnd])
.windowEnd = 0
}
}
func ( *compressor) ( []byte) int {
:= copy(.window[.windowEnd:], )
.windowEnd += int32()
return
}
func ( *compressor) () {
if int(.windowEnd) < len(.window) && !.sync || .windowEnd == 0 {
return
}
.w.writeBlockHuff(false, .window[:.windowEnd], .sync)
.err = .w.err
.windowEnd = 0
}
func ( *compressor) () {
if int(.windowEnd) < len(.window) {
if !.sync {
return
}
if .windowEnd < 128 {
if .windowEnd == 0 {
return
}
if .windowEnd <= 32 {
.err = .writeStoredBlock(.window[:.windowEnd])
} else {
.w.writeBlockHuff(false, .window[:.windowEnd], true)
.err = .w.err
}
.tokens.Reset()
.windowEnd = 0
.fast.reset()
return
}
}
.fast.encode(&.tokens, .window[:.windowEnd])
if .tokens.n == 0 {
.err = .writeStoredBlock(.window[:.windowEnd])
} else if int32(.tokens.n) > .windowEnd-(.windowEnd>>4) {
.w.writeBlockHuff(false, .window[:.windowEnd], .sync)
.err = .w.err
} else {
.w.writeBlockDynamic(&.tokens, false, .window[:.windowEnd], .sync)
.err = .w.err
}
.tokens.Reset()
.windowEnd = 0
}
func ( *compressor) ( []byte) ( int, error) {
if .err != nil {
return 0, .err
}
= len()
for len() > 0 {
if int(.windowEnd) == len(.window) || .sync {
.step()
}
= [.fill(, ):]
if .err != nil {
return 0, .err
}
}
return , .err
}
func ( *compressor) () error {
if .err != nil {
return .err
}
.sync = true
.step()
if .err == nil {
.w.writeStoredHeader(0, false)
.w.flush()
.err = .w.err
}
.sync = false
return .err
}
func ( *compressor) ( io.Writer, int) ( error) {
.w = newHuffmanBitWriter()
switch {
case == NoCompression:
.window = make([]byte, maxStoreBlockSize)
.fill = (*compressor).fillBlock
.step = (*compressor).store
case == HuffmanOnly:
.w.logNewTablePenalty = 10
.window = make([]byte, 32<<10)
.fill = (*compressor).fillBlock
.step = (*compressor).deflateHuff
case == DefaultCompression:
= 6
fallthrough
case 1 <= && <= 6:
.w.logNewTablePenalty = 7
.fast = newFastEnc()
.window = make([]byte, maxStoreBlockSize)
.fill = (*compressor).fillBlock
.step = (*compressor).deflateFast
case 7 <= && <= 9:
.w.logNewTablePenalty = 8
.state = &advancedState{}
.compressionLevel = levels[]
.initDeflate()
.fill = (*compressor).fillDeflate
.step = (*compressor).deflateLazy
default:
return fmt.Errorf("flate: invalid compression level %d: want value in range [-2, 9]", )
}
.level =
return nil
}
func ( *compressor) ( io.Writer) {
.w.reset()
.sync = false
.err = nil
.windowEnd = 0
if .fast != nil {
.fast.reset()
.tokens.Reset()
return
}
if .compressionLevel.chain == 0 {
return
}
:= .state
.chainHead = -1
clear(.hashHead[:])
clear(.hashPrev[:])
.hashOffset = 1
.index = 0
.blockStart, .byteAvailable = 0, false
.tokens.Reset()
.length = minMatchLength - 1
.offset = 0
.literalCounter = 0
.maxInsertIndex = 0
}
var errWriterClosed = errors.New("flate: closed writer")
func ( *compressor) () error {
if .err == errWriterClosed {
return nil
}
if .err != nil {
return .err
}
.sync = true
.step()
if .err != nil {
return .err
}
if .w.writeStoredHeader(0, true); .w.err != nil {
return .w.err
}
.w.flush()
if .w.err != nil {
return .w.err
}
.err = errWriterClosed
.w.reset(nil)
return nil
}
func ( io.Writer, int) (*Writer, error) {
var Writer
if := .d.init(, ); != nil {
return nil,
}
return &, nil
}
func ( io.Writer, int, []byte) (*Writer, error) {
, := NewWriter(, )
if != nil {
return nil,
}
.d.fillWindow()
.dict = slices.Clone()
return ,
}
type Writer struct {
d compressor
dict []byte
}
func ( *Writer) ( []byte) ( int, error) {
return .d.write()
}
func ( *Writer) () error {
return .d.syncFlush()
}
func ( *Writer) () error {
return .d.close()
}
func ( *Writer) ( io.Writer) {
.d.reset()
.d.fillWindow(.dict)
}