123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874 |
- // 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 (
- "errors"
- "fmt"
- "math"
- "math/bits"
- "github.com/klauspost/compress/huff0"
- )
- type blockEnc struct {
- size int
- literals []byte
- sequences []seq
- coders seqCoders
- litEnc *huff0.Scratch
- dictLitEnc *huff0.Scratch
- wr bitWriter
- extraLits int
- output []byte
- recentOffsets [3]uint32
- prevRecentOffsets [3]uint32
- last bool
- lowMem bool
- }
- // init should be used once the block has been created.
- // If called more than once, the effect is the same as calling reset.
- func (b *blockEnc) init() {
- if b.lowMem {
- // 1K literals
- if cap(b.literals) < 1<<10 {
- b.literals = make([]byte, 0, 1<<10)
- }
- const defSeqs = 20
- if cap(b.sequences) < defSeqs {
- b.sequences = make([]seq, 0, defSeqs)
- }
- // 1K
- if cap(b.output) < 1<<10 {
- b.output = make([]byte, 0, 1<<10)
- }
- } else {
- if cap(b.literals) < maxCompressedBlockSize {
- b.literals = make([]byte, 0, maxCompressedBlockSize)
- }
- const defSeqs = 2000
- if cap(b.sequences) < defSeqs {
- b.sequences = make([]seq, 0, defSeqs)
- }
- if cap(b.output) < maxCompressedBlockSize {
- b.output = make([]byte, 0, maxCompressedBlockSize)
- }
- }
- if b.coders.mlEnc == nil {
- b.coders.mlEnc = &fseEncoder{}
- b.coders.mlPrev = &fseEncoder{}
- b.coders.ofEnc = &fseEncoder{}
- b.coders.ofPrev = &fseEncoder{}
- b.coders.llEnc = &fseEncoder{}
- b.coders.llPrev = &fseEncoder{}
- }
- b.litEnc = &huff0.Scratch{WantLogLess: 4}
- b.reset(nil)
- }
- // initNewEncode can be used to reset offsets and encoders to the initial state.
- func (b *blockEnc) initNewEncode() {
- b.recentOffsets = [3]uint32{1, 4, 8}
- b.litEnc.Reuse = huff0.ReusePolicyNone
- b.coders.setPrev(nil, nil, nil)
- }
- // reset will reset the block for a new encode, but in the same stream,
- // meaning that state will be carried over, but the block content is reset.
- // If a previous block is provided, the recent offsets are carried over.
- func (b *blockEnc) reset(prev *blockEnc) {
- b.extraLits = 0
- b.literals = b.literals[:0]
- b.size = 0
- b.sequences = b.sequences[:0]
- b.output = b.output[:0]
- b.last = false
- if prev != nil {
- b.recentOffsets = prev.prevRecentOffsets
- }
- b.dictLitEnc = nil
- }
- // reset will reset the block for a new encode, but in the same stream,
- // meaning that state will be carried over, but the block content is reset.
- // If a previous block is provided, the recent offsets are carried over.
- func (b *blockEnc) swapEncoders(prev *blockEnc) {
- b.coders.swap(&prev.coders)
- b.litEnc, prev.litEnc = prev.litEnc, b.litEnc
- }
- // blockHeader contains the information for a block header.
- type blockHeader uint32
- // setLast sets the 'last' indicator on a block.
- func (h *blockHeader) setLast(b bool) {
- if b {
- *h = *h | 1
- } else {
- const mask = (1 << 24) - 2
- *h = *h & mask
- }
- }
- // setSize will store the compressed size of a block.
- func (h *blockHeader) setSize(v uint32) {
- const mask = 7
- *h = (*h)&mask | blockHeader(v<<3)
- }
- // setType sets the block type.
- func (h *blockHeader) setType(t blockType) {
- const mask = 1 | (((1 << 24) - 1) ^ 7)
- *h = (*h & mask) | blockHeader(t<<1)
- }
- // appendTo will append the block header to a slice.
- func (h blockHeader) appendTo(b []byte) []byte {
- return append(b, uint8(h), uint8(h>>8), uint8(h>>16))
- }
- // String returns a string representation of the block.
- func (h blockHeader) String() string {
- return fmt.Sprintf("Type: %d, Size: %d, Last:%t", (h>>1)&3, h>>3, h&1 == 1)
- }
- // literalsHeader contains literals header information.
- type literalsHeader uint64
- // setType can be used to set the type of literal block.
- func (h *literalsHeader) setType(t literalsBlockType) {
- const mask = math.MaxUint64 - 3
- *h = (*h & mask) | literalsHeader(t)
- }
- // setSize can be used to set a single size, for uncompressed and RLE content.
- func (h *literalsHeader) setSize(regenLen int) {
- inBits := bits.Len32(uint32(regenLen))
- // Only retain 2 bits
- const mask = 3
- lh := uint64(*h & mask)
- switch {
- case inBits < 5:
- lh |= (uint64(regenLen) << 3) | (1 << 60)
- if debugEncoder {
- got := int(lh>>3) & 0xff
- if got != regenLen {
- panic(fmt.Sprint("litRegenSize = ", regenLen, "(want) != ", got, "(got)"))
- }
- }
- case inBits < 12:
- lh |= (1 << 2) | (uint64(regenLen) << 4) | (2 << 60)
- case inBits < 20:
- lh |= (3 << 2) | (uint64(regenLen) << 4) | (3 << 60)
- default:
- panic(fmt.Errorf("internal error: block too big (%d)", regenLen))
- }
- *h = literalsHeader(lh)
- }
- // setSizes will set the size of a compressed literals section and the input length.
- func (h *literalsHeader) setSizes(compLen, inLen int, single bool) {
- compBits, inBits := bits.Len32(uint32(compLen)), bits.Len32(uint32(inLen))
- // Only retain 2 bits
- const mask = 3
- lh := uint64(*h & mask)
- switch {
- case compBits <= 10 && inBits <= 10:
- if !single {
- lh |= 1 << 2
- }
- lh |= (uint64(inLen) << 4) | (uint64(compLen) << (10 + 4)) | (3 << 60)
- if debugEncoder {
- const mmask = (1 << 24) - 1
- n := (lh >> 4) & mmask
- if int(n&1023) != inLen {
- panic(fmt.Sprint("regensize:", int(n&1023), "!=", inLen, inBits))
- }
- if int(n>>10) != compLen {
- panic(fmt.Sprint("compsize:", int(n>>10), "!=", compLen, compBits))
- }
- }
- case compBits <= 14 && inBits <= 14:
- lh |= (2 << 2) | (uint64(inLen) << 4) | (uint64(compLen) << (14 + 4)) | (4 << 60)
- if single {
- panic("single stream used with more than 10 bits length.")
- }
- case compBits <= 18 && inBits <= 18:
- lh |= (3 << 2) | (uint64(inLen) << 4) | (uint64(compLen) << (18 + 4)) | (5 << 60)
- if single {
- panic("single stream used with more than 10 bits length.")
- }
- default:
- panic("internal error: block too big")
- }
- *h = literalsHeader(lh)
- }
- // appendTo will append the literals header to a byte slice.
- func (h literalsHeader) appendTo(b []byte) []byte {
- size := uint8(h >> 60)
- switch size {
- case 1:
- b = append(b, uint8(h))
- case 2:
- b = append(b, uint8(h), uint8(h>>8))
- case 3:
- b = append(b, uint8(h), uint8(h>>8), uint8(h>>16))
- case 4:
- b = append(b, uint8(h), uint8(h>>8), uint8(h>>16), uint8(h>>24))
- case 5:
- b = append(b, uint8(h), uint8(h>>8), uint8(h>>16), uint8(h>>24), uint8(h>>32))
- default:
- panic(fmt.Errorf("internal error: literalsHeader has invalid size (%d)", size))
- }
- return b
- }
- // size returns the output size with currently set values.
- func (h literalsHeader) size() int {
- return int(h >> 60)
- }
- func (h literalsHeader) String() string {
- return fmt.Sprintf("Type: %d, SizeFormat: %d, Size: 0x%d, Bytes:%d", literalsBlockType(h&3), (h>>2)&3, h&((1<<60)-1)>>4, h>>60)
- }
- // pushOffsets will push the recent offsets to the backup store.
- func (b *blockEnc) pushOffsets() {
- b.prevRecentOffsets = b.recentOffsets
- }
- // pushOffsets will push the recent offsets to the backup store.
- func (b *blockEnc) popOffsets() {
- b.recentOffsets = b.prevRecentOffsets
- }
- // matchOffset will adjust recent offsets and return the adjusted one,
- // if it matches a previous offset.
- func (b *blockEnc) matchOffset(offset, lits uint32) uint32 {
- // Check if offset is one of the recent offsets.
- // Adjusts the output offset accordingly.
- // Gives a tiny bit of compression, typically around 1%.
- if true {
- if lits > 0 {
- switch offset {
- case b.recentOffsets[0]:
- offset = 1
- case b.recentOffsets[1]:
- b.recentOffsets[1] = b.recentOffsets[0]
- b.recentOffsets[0] = offset
- offset = 2
- case b.recentOffsets[2]:
- b.recentOffsets[2] = b.recentOffsets[1]
- b.recentOffsets[1] = b.recentOffsets[0]
- b.recentOffsets[0] = offset
- offset = 3
- default:
- b.recentOffsets[2] = b.recentOffsets[1]
- b.recentOffsets[1] = b.recentOffsets[0]
- b.recentOffsets[0] = offset
- offset += 3
- }
- } else {
- switch offset {
- case b.recentOffsets[1]:
- b.recentOffsets[1] = b.recentOffsets[0]
- b.recentOffsets[0] = offset
- offset = 1
- case b.recentOffsets[2]:
- b.recentOffsets[2] = b.recentOffsets[1]
- b.recentOffsets[1] = b.recentOffsets[0]
- b.recentOffsets[0] = offset
- offset = 2
- case b.recentOffsets[0] - 1:
- b.recentOffsets[2] = b.recentOffsets[1]
- b.recentOffsets[1] = b.recentOffsets[0]
- b.recentOffsets[0] = offset
- offset = 3
- default:
- b.recentOffsets[2] = b.recentOffsets[1]
- b.recentOffsets[1] = b.recentOffsets[0]
- b.recentOffsets[0] = offset
- offset += 3
- }
- }
- } else {
- offset += 3
- }
- return offset
- }
- // encodeRaw can be used to set the output to a raw representation of supplied bytes.
- func (b *blockEnc) encodeRaw(a []byte) {
- var bh blockHeader
- bh.setLast(b.last)
- bh.setSize(uint32(len(a)))
- bh.setType(blockTypeRaw)
- b.output = bh.appendTo(b.output[:0])
- b.output = append(b.output, a...)
- if debugEncoder {
- println("Adding RAW block, length", len(a), "last:", b.last)
- }
- }
- // encodeRaw can be used to set the output to a raw representation of supplied bytes.
- func (b *blockEnc) encodeRawTo(dst, src []byte) []byte {
- var bh blockHeader
- bh.setLast(b.last)
- bh.setSize(uint32(len(src)))
- bh.setType(blockTypeRaw)
- dst = bh.appendTo(dst)
- dst = append(dst, src...)
- if debugEncoder {
- println("Adding RAW block, length", len(src), "last:", b.last)
- }
- return dst
- }
- // encodeLits can be used if the block is only litLen.
- func (b *blockEnc) encodeLits(lits []byte, raw bool) error {
- var bh blockHeader
- bh.setLast(b.last)
- bh.setSize(uint32(len(lits)))
- // Don't compress extremely small blocks
- if len(lits) < 8 || (len(lits) < 32 && b.dictLitEnc == nil) || raw {
- if debugEncoder {
- println("Adding RAW block, length", len(lits), "last:", b.last)
- }
- bh.setType(blockTypeRaw)
- b.output = bh.appendTo(b.output)
- b.output = append(b.output, lits...)
- return nil
- }
- var (
- out []byte
- reUsed, single bool
- err error
- )
- if b.dictLitEnc != nil {
- b.litEnc.TransferCTable(b.dictLitEnc)
- b.litEnc.Reuse = huff0.ReusePolicyAllow
- b.dictLitEnc = nil
- }
- if len(lits) >= 1024 {
- // Use 4 Streams.
- out, reUsed, err = huff0.Compress4X(lits, b.litEnc)
- } else if len(lits) > 32 {
- // Use 1 stream
- single = true
- out, reUsed, err = huff0.Compress1X(lits, b.litEnc)
- } else {
- err = huff0.ErrIncompressible
- }
- switch err {
- case huff0.ErrIncompressible:
- if debugEncoder {
- println("Adding RAW block, length", len(lits), "last:", b.last)
- }
- bh.setType(blockTypeRaw)
- b.output = bh.appendTo(b.output)
- b.output = append(b.output, lits...)
- return nil
- case huff0.ErrUseRLE:
- if debugEncoder {
- println("Adding RLE block, length", len(lits))
- }
- bh.setType(blockTypeRLE)
- b.output = bh.appendTo(b.output)
- b.output = append(b.output, lits[0])
- return nil
- case nil:
- default:
- return err
- }
- // Compressed...
- // Now, allow reuse
- b.litEnc.Reuse = huff0.ReusePolicyAllow
- bh.setType(blockTypeCompressed)
- var lh literalsHeader
- if reUsed {
- if debugEncoder {
- println("Reused tree, compressed to", len(out))
- }
- lh.setType(literalsBlockTreeless)
- } else {
- if debugEncoder {
- println("New tree, compressed to", len(out), "tree size:", len(b.litEnc.OutTable))
- }
- lh.setType(literalsBlockCompressed)
- }
- // Set sizes
- lh.setSizes(len(out), len(lits), single)
- bh.setSize(uint32(len(out) + lh.size() + 1))
- // Write block headers.
- b.output = bh.appendTo(b.output)
- b.output = lh.appendTo(b.output)
- // Add compressed data.
- b.output = append(b.output, out...)
- // No sequences.
- b.output = append(b.output, 0)
- return nil
- }
- // fuzzFseEncoder can be used to fuzz the FSE encoder.
- func fuzzFseEncoder(data []byte) int {
- if len(data) > maxSequences || len(data) < 2 {
- return 0
- }
- enc := fseEncoder{}
- hist := enc.Histogram()
- maxSym := uint8(0)
- for i, v := range data {
- v = v & 63
- data[i] = v
- hist[v]++
- if v > maxSym {
- maxSym = v
- }
- }
- if maxSym == 0 {
- // All 0
- return 0
- }
- maxCount := func(a []uint32) int {
- var max uint32
- for _, v := range a {
- if v > max {
- max = v
- }
- }
- return int(max)
- }
- cnt := maxCount(hist[:maxSym])
- if cnt == len(data) {
- // RLE
- return 0
- }
- enc.HistogramFinished(maxSym, cnt)
- err := enc.normalizeCount(len(data))
- if err != nil {
- return 0
- }
- _, err = enc.writeCount(nil)
- if err != nil {
- panic(err)
- }
- return 1
- }
- // encode will encode the block and append the output in b.output.
- // Previous offset codes must be pushed if more blocks are expected.
- func (b *blockEnc) encode(org []byte, raw, rawAllLits bool) error {
- if len(b.sequences) == 0 {
- return b.encodeLits(b.literals, rawAllLits)
- }
- // We want some difference to at least account for the headers.
- saved := b.size - len(b.literals) - (b.size >> 6)
- if saved < 16 {
- if org == nil {
- return errIncompressible
- }
- b.popOffsets()
- return b.encodeLits(org, rawAllLits)
- }
- var bh blockHeader
- var lh literalsHeader
- bh.setLast(b.last)
- bh.setType(blockTypeCompressed)
- // Store offset of the block header. Needed when we know the size.
- bhOffset := len(b.output)
- b.output = bh.appendTo(b.output)
- var (
- out []byte
- reUsed, single bool
- err error
- )
- if b.dictLitEnc != nil {
- b.litEnc.TransferCTable(b.dictLitEnc)
- b.litEnc.Reuse = huff0.ReusePolicyAllow
- b.dictLitEnc = nil
- }
- if len(b.literals) >= 1024 && !raw {
- // Use 4 Streams.
- out, reUsed, err = huff0.Compress4X(b.literals, b.litEnc)
- } else if len(b.literals) > 32 && !raw {
- // Use 1 stream
- single = true
- out, reUsed, err = huff0.Compress1X(b.literals, b.litEnc)
- } else {
- err = huff0.ErrIncompressible
- }
- switch err {
- case huff0.ErrIncompressible:
- lh.setType(literalsBlockRaw)
- lh.setSize(len(b.literals))
- b.output = lh.appendTo(b.output)
- b.output = append(b.output, b.literals...)
- if debugEncoder {
- println("Adding literals RAW, length", len(b.literals))
- }
- case huff0.ErrUseRLE:
- lh.setType(literalsBlockRLE)
- lh.setSize(len(b.literals))
- b.output = lh.appendTo(b.output)
- b.output = append(b.output, b.literals[0])
- if debugEncoder {
- println("Adding literals RLE")
- }
- case nil:
- // Compressed litLen...
- if reUsed {
- if debugEncoder {
- println("reused tree")
- }
- lh.setType(literalsBlockTreeless)
- } else {
- if debugEncoder {
- println("new tree, size:", len(b.litEnc.OutTable))
- }
- lh.setType(literalsBlockCompressed)
- if debugEncoder {
- _, _, err := huff0.ReadTable(out, nil)
- if err != nil {
- panic(err)
- }
- }
- }
- lh.setSizes(len(out), len(b.literals), single)
- if debugEncoder {
- printf("Compressed %d literals to %d bytes", len(b.literals), len(out))
- println("Adding literal header:", lh)
- }
- b.output = lh.appendTo(b.output)
- b.output = append(b.output, out...)
- b.litEnc.Reuse = huff0.ReusePolicyAllow
- if debugEncoder {
- println("Adding literals compressed")
- }
- default:
- if debugEncoder {
- println("Adding literals ERROR:", err)
- }
- return err
- }
- // Sequence compression
- // Write the number of sequences
- switch {
- case len(b.sequences) < 128:
- b.output = append(b.output, uint8(len(b.sequences)))
- case len(b.sequences) < 0x7f00: // TODO: this could be wrong
- n := len(b.sequences)
- b.output = append(b.output, 128+uint8(n>>8), uint8(n))
- default:
- n := len(b.sequences) - 0x7f00
- b.output = append(b.output, 255, uint8(n), uint8(n>>8))
- }
- if debugEncoder {
- println("Encoding", len(b.sequences), "sequences")
- }
- b.genCodes()
- llEnc := b.coders.llEnc
- ofEnc := b.coders.ofEnc
- mlEnc := b.coders.mlEnc
- err = llEnc.normalizeCount(len(b.sequences))
- if err != nil {
- return err
- }
- err = ofEnc.normalizeCount(len(b.sequences))
- if err != nil {
- return err
- }
- err = mlEnc.normalizeCount(len(b.sequences))
- if err != nil {
- return err
- }
- // Choose the best compression mode for each type.
- // Will evaluate the new vs predefined and previous.
- chooseComp := func(cur, prev, preDef *fseEncoder) (*fseEncoder, seqCompMode) {
- // See if predefined/previous is better
- hist := cur.count[:cur.symbolLen]
- nSize := cur.approxSize(hist) + cur.maxHeaderSize()
- predefSize := preDef.approxSize(hist)
- prevSize := prev.approxSize(hist)
- // Add a small penalty for new encoders.
- // Don't bother with extremely small (<2 byte gains).
- nSize = nSize + (nSize+2*8*16)>>4
- switch {
- case predefSize <= prevSize && predefSize <= nSize || forcePreDef:
- if debugEncoder {
- println("Using predefined", predefSize>>3, "<=", nSize>>3)
- }
- return preDef, compModePredefined
- case prevSize <= nSize:
- if debugEncoder {
- println("Using previous", prevSize>>3, "<=", nSize>>3)
- }
- return prev, compModeRepeat
- default:
- if debugEncoder {
- println("Using new, predef", predefSize>>3, ". previous:", prevSize>>3, ">", nSize>>3, "header max:", cur.maxHeaderSize()>>3, "bytes")
- println("tl:", cur.actualTableLog, "symbolLen:", cur.symbolLen, "norm:", cur.norm[:cur.symbolLen], "hist", cur.count[:cur.symbolLen])
- }
- return cur, compModeFSE
- }
- }
- // Write compression mode
- var mode uint8
- if llEnc.useRLE {
- mode |= uint8(compModeRLE) << 6
- llEnc.setRLE(b.sequences[0].llCode)
- if debugEncoder {
- println("llEnc.useRLE")
- }
- } else {
- var m seqCompMode
- llEnc, m = chooseComp(llEnc, b.coders.llPrev, &fsePredefEnc[tableLiteralLengths])
- mode |= uint8(m) << 6
- }
- if ofEnc.useRLE {
- mode |= uint8(compModeRLE) << 4
- ofEnc.setRLE(b.sequences[0].ofCode)
- if debugEncoder {
- println("ofEnc.useRLE")
- }
- } else {
- var m seqCompMode
- ofEnc, m = chooseComp(ofEnc, b.coders.ofPrev, &fsePredefEnc[tableOffsets])
- mode |= uint8(m) << 4
- }
- if mlEnc.useRLE {
- mode |= uint8(compModeRLE) << 2
- mlEnc.setRLE(b.sequences[0].mlCode)
- if debugEncoder {
- println("mlEnc.useRLE, code: ", b.sequences[0].mlCode, "value", b.sequences[0].matchLen)
- }
- } else {
- var m seqCompMode
- mlEnc, m = chooseComp(mlEnc, b.coders.mlPrev, &fsePredefEnc[tableMatchLengths])
- mode |= uint8(m) << 2
- }
- b.output = append(b.output, mode)
- if debugEncoder {
- printf("Compression modes: 0b%b", mode)
- }
- b.output, err = llEnc.writeCount(b.output)
- if err != nil {
- return err
- }
- start := len(b.output)
- b.output, err = ofEnc.writeCount(b.output)
- if err != nil {
- return err
- }
- if false {
- println("block:", b.output[start:], "tablelog", ofEnc.actualTableLog, "maxcount:", ofEnc.maxCount)
- fmt.Printf("selected TableLog: %d, Symbol length: %d\n", ofEnc.actualTableLog, ofEnc.symbolLen)
- for i, v := range ofEnc.norm[:ofEnc.symbolLen] {
- fmt.Printf("%3d: %5d -> %4d \n", i, ofEnc.count[i], v)
- }
- }
- b.output, err = mlEnc.writeCount(b.output)
- if err != nil {
- return err
- }
- // Maybe in block?
- wr := &b.wr
- wr.reset(b.output)
- var ll, of, ml cState
- // Current sequence
- seq := len(b.sequences) - 1
- s := b.sequences[seq]
- llEnc.setBits(llBitsTable[:])
- mlEnc.setBits(mlBitsTable[:])
- ofEnc.setBits(nil)
- llTT, ofTT, mlTT := llEnc.ct.symbolTT[:256], ofEnc.ct.symbolTT[:256], mlEnc.ct.symbolTT[:256]
- // We have 3 bounds checks here (and in the loop).
- // Since we are iterating backwards it is kinda hard to avoid.
- llB, ofB, mlB := llTT[s.llCode], ofTT[s.ofCode], mlTT[s.mlCode]
- ll.init(wr, &llEnc.ct, llB)
- of.init(wr, &ofEnc.ct, ofB)
- wr.flush32()
- ml.init(wr, &mlEnc.ct, mlB)
- // Each of these lookups also generates a bounds check.
- wr.addBits32NC(s.litLen, llB.outBits)
- wr.addBits32NC(s.matchLen, mlB.outBits)
- wr.flush32()
- wr.addBits32NC(s.offset, ofB.outBits)
- if debugSequences {
- println("Encoded seq", seq, s, "codes:", s.llCode, s.mlCode, s.ofCode, "states:", ll.state, ml.state, of.state, "bits:", llB, mlB, ofB)
- }
- seq--
- // Store sequences in reverse...
- for seq >= 0 {
- s = b.sequences[seq]
- ofB := ofTT[s.ofCode]
- wr.flush32() // tablelog max is below 8 for each, so it will fill max 24 bits.
- //of.encode(ofB)
- nbBitsOut := (uint32(of.state) + ofB.deltaNbBits) >> 16
- dstState := int32(of.state>>(nbBitsOut&15)) + int32(ofB.deltaFindState)
- wr.addBits16NC(of.state, uint8(nbBitsOut))
- of.state = of.stateTable[dstState]
- // Accumulate extra bits.
- outBits := ofB.outBits & 31
- extraBits := uint64(s.offset & bitMask32[outBits])
- extraBitsN := outBits
- mlB := mlTT[s.mlCode]
- //ml.encode(mlB)
- nbBitsOut = (uint32(ml.state) + mlB.deltaNbBits) >> 16
- dstState = int32(ml.state>>(nbBitsOut&15)) + int32(mlB.deltaFindState)
- wr.addBits16NC(ml.state, uint8(nbBitsOut))
- ml.state = ml.stateTable[dstState]
- outBits = mlB.outBits & 31
- extraBits = extraBits<<outBits | uint64(s.matchLen&bitMask32[outBits])
- extraBitsN += outBits
- llB := llTT[s.llCode]
- //ll.encode(llB)
- nbBitsOut = (uint32(ll.state) + llB.deltaNbBits) >> 16
- dstState = int32(ll.state>>(nbBitsOut&15)) + int32(llB.deltaFindState)
- wr.addBits16NC(ll.state, uint8(nbBitsOut))
- ll.state = ll.stateTable[dstState]
- outBits = llB.outBits & 31
- extraBits = extraBits<<outBits | uint64(s.litLen&bitMask32[outBits])
- extraBitsN += outBits
- wr.flush32()
- wr.addBits64NC(extraBits, extraBitsN)
- if debugSequences {
- println("Encoded seq", seq, s)
- }
- seq--
- }
- ml.flush(mlEnc.actualTableLog)
- of.flush(ofEnc.actualTableLog)
- ll.flush(llEnc.actualTableLog)
- err = wr.close()
- if err != nil {
- return err
- }
- b.output = wr.out
- // Maybe even add a bigger margin.
- if len(b.output)-3-bhOffset >= b.size {
- // Discard and encode as raw block.
- b.output = b.encodeRawTo(b.output[:bhOffset], org)
- b.popOffsets()
- b.litEnc.Reuse = huff0.ReusePolicyNone
- return nil
- }
- // Size is output minus block header.
- bh.setSize(uint32(len(b.output)-bhOffset) - 3)
- if debugEncoder {
- println("Rewriting block header", bh)
- }
- _ = bh.appendTo(b.output[bhOffset:bhOffset])
- b.coders.setPrev(llEnc, mlEnc, ofEnc)
- return nil
- }
- var errIncompressible = errors.New("incompressible")
- func (b *blockEnc) genCodes() {
- if len(b.sequences) == 0 {
- // nothing to do
- return
- }
- if len(b.sequences) > math.MaxUint16 {
- panic("can only encode up to 64K sequences")
- }
- // No bounds checks after here:
- llH := b.coders.llEnc.Histogram()
- ofH := b.coders.ofEnc.Histogram()
- mlH := b.coders.mlEnc.Histogram()
- for i := range llH {
- llH[i] = 0
- }
- for i := range ofH {
- ofH[i] = 0
- }
- for i := range mlH {
- mlH[i] = 0
- }
- var llMax, ofMax, mlMax uint8
- for i := range b.sequences {
- seq := &b.sequences[i]
- v := llCode(seq.litLen)
- seq.llCode = v
- llH[v]++
- if v > llMax {
- llMax = v
- }
- v = ofCode(seq.offset)
- seq.ofCode = v
- ofH[v]++
- if v > ofMax {
- ofMax = v
- }
- v = mlCode(seq.matchLen)
- seq.mlCode = v
- mlH[v]++
- if v > mlMax {
- mlMax = v
- if debugAsserts && mlMax > maxMatchLengthSymbol {
- panic(fmt.Errorf("mlMax > maxMatchLengthSymbol (%d), matchlen: %d", mlMax, seq.matchLen))
- }
- }
- }
- maxCount := func(a []uint32) int {
- var max uint32
- for _, v := range a {
- if v > max {
- max = v
- }
- }
- return int(max)
- }
- if debugAsserts && mlMax > maxMatchLengthSymbol {
- panic(fmt.Errorf("mlMax > maxMatchLengthSymbol (%d)", mlMax))
- }
- if debugAsserts && ofMax > maxOffsetBits {
- panic(fmt.Errorf("ofMax > maxOffsetBits (%d)", ofMax))
- }
- if debugAsserts && llMax > maxLiteralLengthSymbol {
- panic(fmt.Errorf("llMax > maxLiteralLengthSymbol (%d)", llMax))
- }
- b.coders.mlEnc.HistogramFinished(mlMax, maxCount(mlH[:mlMax+1]))
- b.coders.ofEnc.HistogramFinished(ofMax, maxCount(ofH[:ofMax+1]))
- b.coders.llEnc.HistogramFinished(llMax, maxCount(llH[:llMax+1]))
- }
|