1// Copyright 2019+ Klaus Post. All rights reserved. 2// License information can be found in the LICENSE file. 3// Based on work by Yann Collet, released under BSD License. 4 5package zstd 6 7import ( 8 "errors" 9 "fmt" 10 "math" 11 "math/bits" 12 13 "github.com/klauspost/compress/huff0" 14) 15 16type blockEnc struct { 17 size int 18 literals []byte 19 sequences []seq 20 coders seqCoders 21 litEnc *huff0.Scratch 22 dictLitEnc *huff0.Scratch 23 wr bitWriter 24 25 extraLits int 26 output []byte 27 recentOffsets [3]uint32 28 prevRecentOffsets [3]uint32 29 30 last bool 31 lowMem bool 32} 33 34// init should be used once the block has been created. 35// If called more than once, the effect is the same as calling reset. 36func (b *blockEnc) init() { 37 if b.lowMem { 38 // 1K literals 39 if cap(b.literals) < 1<<10 { 40 b.literals = make([]byte, 0, 1<<10) 41 } 42 const defSeqs = 20 43 if cap(b.sequences) < defSeqs { 44 b.sequences = make([]seq, 0, defSeqs) 45 } 46 // 1K 47 if cap(b.output) < 1<<10 { 48 b.output = make([]byte, 0, 1<<10) 49 } 50 } else { 51 if cap(b.literals) < maxCompressedBlockSize { 52 b.literals = make([]byte, 0, maxCompressedBlockSize) 53 } 54 const defSeqs = 200 55 if cap(b.sequences) < defSeqs { 56 b.sequences = make([]seq, 0, defSeqs) 57 } 58 if cap(b.output) < maxCompressedBlockSize { 59 b.output = make([]byte, 0, maxCompressedBlockSize) 60 } 61 } 62 63 if b.coders.mlEnc == nil { 64 b.coders.mlEnc = &fseEncoder{} 65 b.coders.mlPrev = &fseEncoder{} 66 b.coders.ofEnc = &fseEncoder{} 67 b.coders.ofPrev = &fseEncoder{} 68 b.coders.llEnc = &fseEncoder{} 69 b.coders.llPrev = &fseEncoder{} 70 } 71 b.litEnc = &huff0.Scratch{WantLogLess: 4} 72 b.reset(nil) 73} 74 75// initNewEncode can be used to reset offsets and encoders to the initial state. 76func (b *blockEnc) initNewEncode() { 77 b.recentOffsets = [3]uint32{1, 4, 8} 78 b.litEnc.Reuse = huff0.ReusePolicyNone 79 b.coders.setPrev(nil, nil, nil) 80} 81 82// reset will reset the block for a new encode, but in the same stream, 83// meaning that state will be carried over, but the block content is reset. 84// If a previous block is provided, the recent offsets are carried over. 85func (b *blockEnc) reset(prev *blockEnc) { 86 b.extraLits = 0 87 b.literals = b.literals[:0] 88 b.size = 0 89 b.sequences = b.sequences[:0] 90 b.output = b.output[:0] 91 b.last = false 92 if prev != nil { 93 b.recentOffsets = prev.prevRecentOffsets 94 } 95 b.dictLitEnc = nil 96} 97 98// reset will reset the block for a new encode, but in the same stream, 99// meaning that state will be carried over, but the block content is reset. 100// If a previous block is provided, the recent offsets are carried over. 101func (b *blockEnc) swapEncoders(prev *blockEnc) { 102 b.coders.swap(&prev.coders) 103 b.litEnc, prev.litEnc = prev.litEnc, b.litEnc 104} 105 106// blockHeader contains the information for a block header. 107type blockHeader uint32 108 109// setLast sets the 'last' indicator on a block. 110func (h *blockHeader) setLast(b bool) { 111 if b { 112 *h = *h | 1 113 } else { 114 const mask = (1 << 24) - 2 115 *h = *h & mask 116 } 117} 118 119// setSize will store the compressed size of a block. 120func (h *blockHeader) setSize(v uint32) { 121 const mask = 7 122 *h = (*h)&mask | blockHeader(v<<3) 123} 124 125// setType sets the block type. 126func (h *blockHeader) setType(t blockType) { 127 const mask = 1 | (((1 << 24) - 1) ^ 7) 128 *h = (*h & mask) | blockHeader(t<<1) 129} 130 131// appendTo will append the block header to a slice. 132func (h blockHeader) appendTo(b []byte) []byte { 133 return append(b, uint8(h), uint8(h>>8), uint8(h>>16)) 134} 135 136// String returns a string representation of the block. 137func (h blockHeader) String() string { 138 return fmt.Sprintf("Type: %d, Size: %d, Last:%t", (h>>1)&3, h>>3, h&1 == 1) 139} 140 141// literalsHeader contains literals header information. 142type literalsHeader uint64 143 144// setType can be used to set the type of literal block. 145func (h *literalsHeader) setType(t literalsBlockType) { 146 const mask = math.MaxUint64 - 3 147 *h = (*h & mask) | literalsHeader(t) 148} 149 150// setSize can be used to set a single size, for uncompressed and RLE content. 151func (h *literalsHeader) setSize(regenLen int) { 152 inBits := bits.Len32(uint32(regenLen)) 153 // Only retain 2 bits 154 const mask = 3 155 lh := uint64(*h & mask) 156 switch { 157 case inBits < 5: 158 lh |= (uint64(regenLen) << 3) | (1 << 60) 159 if debugEncoder { 160 got := int(lh>>3) & 0xff 161 if got != regenLen { 162 panic(fmt.Sprint("litRegenSize = ", regenLen, "(want) != ", got, "(got)")) 163 } 164 } 165 case inBits < 12: 166 lh |= (1 << 2) | (uint64(regenLen) << 4) | (2 << 60) 167 case inBits < 20: 168 lh |= (3 << 2) | (uint64(regenLen) << 4) | (3 << 60) 169 default: 170 panic(fmt.Errorf("internal error: block too big (%d)", regenLen)) 171 } 172 *h = literalsHeader(lh) 173} 174 175// setSizes will set the size of a compressed literals section and the input length. 176func (h *literalsHeader) setSizes(compLen, inLen int, single bool) { 177 compBits, inBits := bits.Len32(uint32(compLen)), bits.Len32(uint32(inLen)) 178 // Only retain 2 bits 179 const mask = 3 180 lh := uint64(*h & mask) 181 switch { 182 case compBits <= 10 && inBits <= 10: 183 if !single { 184 lh |= 1 << 2 185 } 186 lh |= (uint64(inLen) << 4) | (uint64(compLen) << (10 + 4)) | (3 << 60) 187 if debugEncoder { 188 const mmask = (1 << 24) - 1 189 n := (lh >> 4) & mmask 190 if int(n&1023) != inLen { 191 panic(fmt.Sprint("regensize:", int(n&1023), "!=", inLen, inBits)) 192 } 193 if int(n>>10) != compLen { 194 panic(fmt.Sprint("compsize:", int(n>>10), "!=", compLen, compBits)) 195 } 196 } 197 case compBits <= 14 && inBits <= 14: 198 lh |= (2 << 2) | (uint64(inLen) << 4) | (uint64(compLen) << (14 + 4)) | (4 << 60) 199 if single { 200 panic("single stream used with more than 10 bits length.") 201 } 202 case compBits <= 18 && inBits <= 18: 203 lh |= (3 << 2) | (uint64(inLen) << 4) | (uint64(compLen) << (18 + 4)) | (5 << 60) 204 if single { 205 panic("single stream used with more than 10 bits length.") 206 } 207 default: 208 panic("internal error: block too big") 209 } 210 *h = literalsHeader(lh) 211} 212 213// appendTo will append the literals header to a byte slice. 214func (h literalsHeader) appendTo(b []byte) []byte { 215 size := uint8(h >> 60) 216 switch size { 217 case 1: 218 b = append(b, uint8(h)) 219 case 2: 220 b = append(b, uint8(h), uint8(h>>8)) 221 case 3: 222 b = append(b, uint8(h), uint8(h>>8), uint8(h>>16)) 223 case 4: 224 b = append(b, uint8(h), uint8(h>>8), uint8(h>>16), uint8(h>>24)) 225 case 5: 226 b = append(b, uint8(h), uint8(h>>8), uint8(h>>16), uint8(h>>24), uint8(h>>32)) 227 default: 228 panic(fmt.Errorf("internal error: literalsHeader has invalid size (%d)", size)) 229 } 230 return b 231} 232 233// size returns the output size with currently set values. 234func (h literalsHeader) size() int { 235 return int(h >> 60) 236} 237 238func (h literalsHeader) String() string { 239 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) 240} 241 242// pushOffsets will push the recent offsets to the backup store. 243func (b *blockEnc) pushOffsets() { 244 b.prevRecentOffsets = b.recentOffsets 245} 246 247// pushOffsets will push the recent offsets to the backup store. 248func (b *blockEnc) popOffsets() { 249 b.recentOffsets = b.prevRecentOffsets 250} 251 252// matchOffset will adjust recent offsets and return the adjusted one, 253// if it matches a previous offset. 254func (b *blockEnc) matchOffset(offset, lits uint32) uint32 { 255 // Check if offset is one of the recent offsets. 256 // Adjusts the output offset accordingly. 257 // Gives a tiny bit of compression, typically around 1%. 258 if true { 259 if lits > 0 { 260 switch offset { 261 case b.recentOffsets[0]: 262 offset = 1 263 case b.recentOffsets[1]: 264 b.recentOffsets[1] = b.recentOffsets[0] 265 b.recentOffsets[0] = offset 266 offset = 2 267 case b.recentOffsets[2]: 268 b.recentOffsets[2] = b.recentOffsets[1] 269 b.recentOffsets[1] = b.recentOffsets[0] 270 b.recentOffsets[0] = offset 271 offset = 3 272 default: 273 b.recentOffsets[2] = b.recentOffsets[1] 274 b.recentOffsets[1] = b.recentOffsets[0] 275 b.recentOffsets[0] = offset 276 offset += 3 277 } 278 } else { 279 switch offset { 280 case b.recentOffsets[1]: 281 b.recentOffsets[1] = b.recentOffsets[0] 282 b.recentOffsets[0] = offset 283 offset = 1 284 case b.recentOffsets[2]: 285 b.recentOffsets[2] = b.recentOffsets[1] 286 b.recentOffsets[1] = b.recentOffsets[0] 287 b.recentOffsets[0] = offset 288 offset = 2 289 case b.recentOffsets[0] - 1: 290 b.recentOffsets[2] = b.recentOffsets[1] 291 b.recentOffsets[1] = b.recentOffsets[0] 292 b.recentOffsets[0] = offset 293 offset = 3 294 default: 295 b.recentOffsets[2] = b.recentOffsets[1] 296 b.recentOffsets[1] = b.recentOffsets[0] 297 b.recentOffsets[0] = offset 298 offset += 3 299 } 300 } 301 } else { 302 offset += 3 303 } 304 return offset 305} 306 307// encodeRaw can be used to set the output to a raw representation of supplied bytes. 308func (b *blockEnc) encodeRaw(a []byte) { 309 var bh blockHeader 310 bh.setLast(b.last) 311 bh.setSize(uint32(len(a))) 312 bh.setType(blockTypeRaw) 313 b.output = bh.appendTo(b.output[:0]) 314 b.output = append(b.output, a...) 315 if debugEncoder { 316 println("Adding RAW block, length", len(a), "last:", b.last) 317 } 318} 319 320// encodeRaw can be used to set the output to a raw representation of supplied bytes. 321func (b *blockEnc) encodeRawTo(dst, src []byte) []byte { 322 var bh blockHeader 323 bh.setLast(b.last) 324 bh.setSize(uint32(len(src))) 325 bh.setType(blockTypeRaw) 326 dst = bh.appendTo(dst) 327 dst = append(dst, src...) 328 if debugEncoder { 329 println("Adding RAW block, length", len(src), "last:", b.last) 330 } 331 return dst 332} 333 334// encodeLits can be used if the block is only litLen. 335func (b *blockEnc) encodeLits(lits []byte, raw bool) error { 336 var bh blockHeader 337 bh.setLast(b.last) 338 bh.setSize(uint32(len(lits))) 339 340 // Don't compress extremely small blocks 341 if len(lits) < 8 || (len(lits) < 32 && b.dictLitEnc == nil) || raw { 342 if debugEncoder { 343 println("Adding RAW block, length", len(lits), "last:", b.last) 344 } 345 bh.setType(blockTypeRaw) 346 b.output = bh.appendTo(b.output) 347 b.output = append(b.output, lits...) 348 return nil 349 } 350 351 var ( 352 out []byte 353 reUsed, single bool 354 err error 355 ) 356 if b.dictLitEnc != nil { 357 b.litEnc.TransferCTable(b.dictLitEnc) 358 b.litEnc.Reuse = huff0.ReusePolicyAllow 359 b.dictLitEnc = nil 360 } 361 if len(lits) >= 1024 { 362 // Use 4 Streams. 363 out, reUsed, err = huff0.Compress4X(lits, b.litEnc) 364 } else if len(lits) > 32 { 365 // Use 1 stream 366 single = true 367 out, reUsed, err = huff0.Compress1X(lits, b.litEnc) 368 } else { 369 err = huff0.ErrIncompressible 370 } 371 372 switch err { 373 case huff0.ErrIncompressible: 374 if debugEncoder { 375 println("Adding RAW block, length", len(lits), "last:", b.last) 376 } 377 bh.setType(blockTypeRaw) 378 b.output = bh.appendTo(b.output) 379 b.output = append(b.output, lits...) 380 return nil 381 case huff0.ErrUseRLE: 382 if debugEncoder { 383 println("Adding RLE block, length", len(lits)) 384 } 385 bh.setType(blockTypeRLE) 386 b.output = bh.appendTo(b.output) 387 b.output = append(b.output, lits[0]) 388 return nil 389 case nil: 390 default: 391 return err 392 } 393 // Compressed... 394 // Now, allow reuse 395 b.litEnc.Reuse = huff0.ReusePolicyAllow 396 bh.setType(blockTypeCompressed) 397 var lh literalsHeader 398 if reUsed { 399 if debugEncoder { 400 println("Reused tree, compressed to", len(out)) 401 } 402 lh.setType(literalsBlockTreeless) 403 } else { 404 if debugEncoder { 405 println("New tree, compressed to", len(out), "tree size:", len(b.litEnc.OutTable)) 406 } 407 lh.setType(literalsBlockCompressed) 408 } 409 // Set sizes 410 lh.setSizes(len(out), len(lits), single) 411 bh.setSize(uint32(len(out) + lh.size() + 1)) 412 413 // Write block headers. 414 b.output = bh.appendTo(b.output) 415 b.output = lh.appendTo(b.output) 416 // Add compressed data. 417 b.output = append(b.output, out...) 418 // No sequences. 419 b.output = append(b.output, 0) 420 return nil 421} 422 423// fuzzFseEncoder can be used to fuzz the FSE encoder. 424func fuzzFseEncoder(data []byte) int { 425 if len(data) > maxSequences || len(data) < 2 { 426 return 0 427 } 428 enc := fseEncoder{} 429 hist := enc.Histogram()[:256] 430 maxSym := uint8(0) 431 for i, v := range data { 432 v = v & 63 433 data[i] = v 434 hist[v]++ 435 if v > maxSym { 436 maxSym = v 437 } 438 } 439 if maxSym == 0 { 440 // All 0 441 return 0 442 } 443 maxCount := func(a []uint32) int { 444 var max uint32 445 for _, v := range a { 446 if v > max { 447 max = v 448 } 449 } 450 return int(max) 451 } 452 cnt := maxCount(hist[:maxSym]) 453 if cnt == len(data) { 454 // RLE 455 return 0 456 } 457 enc.HistogramFinished(maxSym, cnt) 458 err := enc.normalizeCount(len(data)) 459 if err != nil { 460 return 0 461 } 462 _, err = enc.writeCount(nil) 463 if err != nil { 464 panic(err) 465 } 466 return 1 467} 468 469// encode will encode the block and append the output in b.output. 470// Previous offset codes must be pushed if more blocks are expected. 471func (b *blockEnc) encode(org []byte, raw, rawAllLits bool) error { 472 if len(b.sequences) == 0 { 473 return b.encodeLits(b.literals, rawAllLits) 474 } 475 // We want some difference to at least account for the headers. 476 saved := b.size - len(b.literals) - (b.size >> 5) 477 if saved < 16 { 478 if org == nil { 479 return errIncompressible 480 } 481 b.popOffsets() 482 return b.encodeLits(org, rawAllLits) 483 } 484 485 var bh blockHeader 486 var lh literalsHeader 487 bh.setLast(b.last) 488 bh.setType(blockTypeCompressed) 489 // Store offset of the block header. Needed when we know the size. 490 bhOffset := len(b.output) 491 b.output = bh.appendTo(b.output) 492 493 var ( 494 out []byte 495 reUsed, single bool 496 err error 497 ) 498 if b.dictLitEnc != nil { 499 b.litEnc.TransferCTable(b.dictLitEnc) 500 b.litEnc.Reuse = huff0.ReusePolicyAllow 501 b.dictLitEnc = nil 502 } 503 if len(b.literals) >= 1024 && !raw { 504 // Use 4 Streams. 505 out, reUsed, err = huff0.Compress4X(b.literals, b.litEnc) 506 } else if len(b.literals) > 32 && !raw { 507 // Use 1 stream 508 single = true 509 out, reUsed, err = huff0.Compress1X(b.literals, b.litEnc) 510 } else { 511 err = huff0.ErrIncompressible 512 } 513 514 switch err { 515 case huff0.ErrIncompressible: 516 lh.setType(literalsBlockRaw) 517 lh.setSize(len(b.literals)) 518 b.output = lh.appendTo(b.output) 519 b.output = append(b.output, b.literals...) 520 if debugEncoder { 521 println("Adding literals RAW, length", len(b.literals)) 522 } 523 case huff0.ErrUseRLE: 524 lh.setType(literalsBlockRLE) 525 lh.setSize(len(b.literals)) 526 b.output = lh.appendTo(b.output) 527 b.output = append(b.output, b.literals[0]) 528 if debugEncoder { 529 println("Adding literals RLE") 530 } 531 case nil: 532 // Compressed litLen... 533 if reUsed { 534 if debugEncoder { 535 println("reused tree") 536 } 537 lh.setType(literalsBlockTreeless) 538 } else { 539 if debugEncoder { 540 println("new tree, size:", len(b.litEnc.OutTable)) 541 } 542 lh.setType(literalsBlockCompressed) 543 if debugEncoder { 544 _, _, err := huff0.ReadTable(out, nil) 545 if err != nil { 546 panic(err) 547 } 548 } 549 } 550 lh.setSizes(len(out), len(b.literals), single) 551 if debugEncoder { 552 printf("Compressed %d literals to %d bytes", len(b.literals), len(out)) 553 println("Adding literal header:", lh) 554 } 555 b.output = lh.appendTo(b.output) 556 b.output = append(b.output, out...) 557 b.litEnc.Reuse = huff0.ReusePolicyAllow 558 if debugEncoder { 559 println("Adding literals compressed") 560 } 561 default: 562 if debugEncoder { 563 println("Adding literals ERROR:", err) 564 } 565 return err 566 } 567 // Sequence compression 568 569 // Write the number of sequences 570 switch { 571 case len(b.sequences) < 128: 572 b.output = append(b.output, uint8(len(b.sequences))) 573 case len(b.sequences) < 0x7f00: // TODO: this could be wrong 574 n := len(b.sequences) 575 b.output = append(b.output, 128+uint8(n>>8), uint8(n)) 576 default: 577 n := len(b.sequences) - 0x7f00 578 b.output = append(b.output, 255, uint8(n), uint8(n>>8)) 579 } 580 if debugEncoder { 581 println("Encoding", len(b.sequences), "sequences") 582 } 583 b.genCodes() 584 llEnc := b.coders.llEnc 585 ofEnc := b.coders.ofEnc 586 mlEnc := b.coders.mlEnc 587 err = llEnc.normalizeCount(len(b.sequences)) 588 if err != nil { 589 return err 590 } 591 err = ofEnc.normalizeCount(len(b.sequences)) 592 if err != nil { 593 return err 594 } 595 err = mlEnc.normalizeCount(len(b.sequences)) 596 if err != nil { 597 return err 598 } 599 600 // Choose the best compression mode for each type. 601 // Will evaluate the new vs predefined and previous. 602 chooseComp := func(cur, prev, preDef *fseEncoder) (*fseEncoder, seqCompMode) { 603 // See if predefined/previous is better 604 hist := cur.count[:cur.symbolLen] 605 nSize := cur.approxSize(hist) + cur.maxHeaderSize() 606 predefSize := preDef.approxSize(hist) 607 prevSize := prev.approxSize(hist) 608 609 // Add a small penalty for new encoders. 610 // Don't bother with extremely small (<2 byte gains). 611 nSize = nSize + (nSize+2*8*16)>>4 612 switch { 613 case predefSize <= prevSize && predefSize <= nSize || forcePreDef: 614 if debugEncoder { 615 println("Using predefined", predefSize>>3, "<=", nSize>>3) 616 } 617 return preDef, compModePredefined 618 case prevSize <= nSize: 619 if debugEncoder { 620 println("Using previous", prevSize>>3, "<=", nSize>>3) 621 } 622 return prev, compModeRepeat 623 default: 624 if debugEncoder { 625 println("Using new, predef", predefSize>>3, ". previous:", prevSize>>3, ">", nSize>>3, "header max:", cur.maxHeaderSize()>>3, "bytes") 626 println("tl:", cur.actualTableLog, "symbolLen:", cur.symbolLen, "norm:", cur.norm[:cur.symbolLen], "hist", cur.count[:cur.symbolLen]) 627 } 628 return cur, compModeFSE 629 } 630 } 631 632 // Write compression mode 633 var mode uint8 634 if llEnc.useRLE { 635 mode |= uint8(compModeRLE) << 6 636 llEnc.setRLE(b.sequences[0].llCode) 637 if debugEncoder { 638 println("llEnc.useRLE") 639 } 640 } else { 641 var m seqCompMode 642 llEnc, m = chooseComp(llEnc, b.coders.llPrev, &fsePredefEnc[tableLiteralLengths]) 643 mode |= uint8(m) << 6 644 } 645 if ofEnc.useRLE { 646 mode |= uint8(compModeRLE) << 4 647 ofEnc.setRLE(b.sequences[0].ofCode) 648 if debugEncoder { 649 println("ofEnc.useRLE") 650 } 651 } else { 652 var m seqCompMode 653 ofEnc, m = chooseComp(ofEnc, b.coders.ofPrev, &fsePredefEnc[tableOffsets]) 654 mode |= uint8(m) << 4 655 } 656 657 if mlEnc.useRLE { 658 mode |= uint8(compModeRLE) << 2 659 mlEnc.setRLE(b.sequences[0].mlCode) 660 if debugEncoder { 661 println("mlEnc.useRLE, code: ", b.sequences[0].mlCode, "value", b.sequences[0].matchLen) 662 } 663 } else { 664 var m seqCompMode 665 mlEnc, m = chooseComp(mlEnc, b.coders.mlPrev, &fsePredefEnc[tableMatchLengths]) 666 mode |= uint8(m) << 2 667 } 668 b.output = append(b.output, mode) 669 if debugEncoder { 670 printf("Compression modes: 0b%b", mode) 671 } 672 b.output, err = llEnc.writeCount(b.output) 673 if err != nil { 674 return err 675 } 676 start := len(b.output) 677 b.output, err = ofEnc.writeCount(b.output) 678 if err != nil { 679 return err 680 } 681 if false { 682 println("block:", b.output[start:], "tablelog", ofEnc.actualTableLog, "maxcount:", ofEnc.maxCount) 683 fmt.Printf("selected TableLog: %d, Symbol length: %d\n", ofEnc.actualTableLog, ofEnc.symbolLen) 684 for i, v := range ofEnc.norm[:ofEnc.symbolLen] { 685 fmt.Printf("%3d: %5d -> %4d \n", i, ofEnc.count[i], v) 686 } 687 } 688 b.output, err = mlEnc.writeCount(b.output) 689 if err != nil { 690 return err 691 } 692 693 // Maybe in block? 694 wr := &b.wr 695 wr.reset(b.output) 696 697 var ll, of, ml cState 698 699 // Current sequence 700 seq := len(b.sequences) - 1 701 s := b.sequences[seq] 702 llEnc.setBits(llBitsTable[:]) 703 mlEnc.setBits(mlBitsTable[:]) 704 ofEnc.setBits(nil) 705 706 llTT, ofTT, mlTT := llEnc.ct.symbolTT[:256], ofEnc.ct.symbolTT[:256], mlEnc.ct.symbolTT[:256] 707 708 // We have 3 bounds checks here (and in the loop). 709 // Since we are iterating backwards it is kinda hard to avoid. 710 llB, ofB, mlB := llTT[s.llCode], ofTT[s.ofCode], mlTT[s.mlCode] 711 ll.init(wr, &llEnc.ct, llB) 712 of.init(wr, &ofEnc.ct, ofB) 713 wr.flush32() 714 ml.init(wr, &mlEnc.ct, mlB) 715 716 // Each of these lookups also generates a bounds check. 717 wr.addBits32NC(s.litLen, llB.outBits) 718 wr.addBits32NC(s.matchLen, mlB.outBits) 719 wr.flush32() 720 wr.addBits32NC(s.offset, ofB.outBits) 721 if debugSequences { 722 println("Encoded seq", seq, s, "codes:", s.llCode, s.mlCode, s.ofCode, "states:", ll.state, ml.state, of.state, "bits:", llB, mlB, ofB) 723 } 724 seq-- 725 if llEnc.maxBits+mlEnc.maxBits+ofEnc.maxBits <= 32 { 726 // No need to flush (common) 727 for seq >= 0 { 728 s = b.sequences[seq] 729 wr.flush32() 730 llB, ofB, mlB := llTT[s.llCode], ofTT[s.ofCode], mlTT[s.mlCode] 731 // tabelog max is 8 for all. 732 of.encode(ofB) 733 ml.encode(mlB) 734 ll.encode(llB) 735 wr.flush32() 736 737 // We checked that all can stay within 32 bits 738 wr.addBits32NC(s.litLen, llB.outBits) 739 wr.addBits32NC(s.matchLen, mlB.outBits) 740 wr.addBits32NC(s.offset, ofB.outBits) 741 742 if debugSequences { 743 println("Encoded seq", seq, s) 744 } 745 746 seq-- 747 } 748 } else { 749 for seq >= 0 { 750 s = b.sequences[seq] 751 wr.flush32() 752 llB, ofB, mlB := llTT[s.llCode], ofTT[s.ofCode], mlTT[s.mlCode] 753 // tabelog max is below 8 for each. 754 of.encode(ofB) 755 ml.encode(mlB) 756 ll.encode(llB) 757 wr.flush32() 758 759 // ml+ll = max 32 bits total 760 wr.addBits32NC(s.litLen, llB.outBits) 761 wr.addBits32NC(s.matchLen, mlB.outBits) 762 wr.flush32() 763 wr.addBits32NC(s.offset, ofB.outBits) 764 765 if debugSequences { 766 println("Encoded seq", seq, s) 767 } 768 769 seq-- 770 } 771 } 772 ml.flush(mlEnc.actualTableLog) 773 of.flush(ofEnc.actualTableLog) 774 ll.flush(llEnc.actualTableLog) 775 err = wr.close() 776 if err != nil { 777 return err 778 } 779 b.output = wr.out 780 781 if len(b.output)-3-bhOffset >= b.size { 782 // Maybe even add a bigger margin. 783 b.litEnc.Reuse = huff0.ReusePolicyNone 784 return errIncompressible 785 } 786 787 // Size is output minus block header. 788 bh.setSize(uint32(len(b.output)-bhOffset) - 3) 789 if debugEncoder { 790 println("Rewriting block header", bh) 791 } 792 _ = bh.appendTo(b.output[bhOffset:bhOffset]) 793 b.coders.setPrev(llEnc, mlEnc, ofEnc) 794 return nil 795} 796 797var errIncompressible = errors.New("incompressible") 798 799func (b *blockEnc) genCodes() { 800 if len(b.sequences) == 0 { 801 // nothing to do 802 return 803 } 804 805 if len(b.sequences) > math.MaxUint16 { 806 panic("can only encode up to 64K sequences") 807 } 808 // No bounds checks after here: 809 llH := b.coders.llEnc.Histogram()[:256] 810 ofH := b.coders.ofEnc.Histogram()[:256] 811 mlH := b.coders.mlEnc.Histogram()[:256] 812 for i := range llH { 813 llH[i] = 0 814 } 815 for i := range ofH { 816 ofH[i] = 0 817 } 818 for i := range mlH { 819 mlH[i] = 0 820 } 821 822 var llMax, ofMax, mlMax uint8 823 for i, seq := range b.sequences { 824 v := llCode(seq.litLen) 825 seq.llCode = v 826 llH[v]++ 827 if v > llMax { 828 llMax = v 829 } 830 831 v = ofCode(seq.offset) 832 seq.ofCode = v 833 ofH[v]++ 834 if v > ofMax { 835 ofMax = v 836 } 837 838 v = mlCode(seq.matchLen) 839 seq.mlCode = v 840 mlH[v]++ 841 if v > mlMax { 842 mlMax = v 843 if debugAsserts && mlMax > maxMatchLengthSymbol { 844 panic(fmt.Errorf("mlMax > maxMatchLengthSymbol (%d), matchlen: %d", mlMax, seq.matchLen)) 845 } 846 } 847 b.sequences[i] = seq 848 } 849 maxCount := func(a []uint32) int { 850 var max uint32 851 for _, v := range a { 852 if v > max { 853 max = v 854 } 855 } 856 return int(max) 857 } 858 if debugAsserts && mlMax > maxMatchLengthSymbol { 859 panic(fmt.Errorf("mlMax > maxMatchLengthSymbol (%d)", mlMax)) 860 } 861 if debugAsserts && ofMax > maxOffsetBits { 862 panic(fmt.Errorf("ofMax > maxOffsetBits (%d)", ofMax)) 863 } 864 if debugAsserts && llMax > maxLiteralLengthSymbol { 865 panic(fmt.Errorf("llMax > maxLiteralLengthSymbol (%d)", llMax)) 866 } 867 868 b.coders.mlEnc.HistogramFinished(mlMax, maxCount(mlH[:mlMax+1])) 869 b.coders.ofEnc.HistogramFinished(ofMax, maxCount(ofH[:ofMax+1])) 870 b.coders.llEnc.HistogramFinished(llMax, maxCount(llH[:llMax+1])) 871} 872