github.com/andybalholm/brotli@v1.0.6/compress_fragment_two_pass.go (about) 1 package brotli 2 3 import "encoding/binary" 4 5 /* Copyright 2015 Google Inc. All Rights Reserved. 6 7 Distributed under MIT license. 8 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 9 */ 10 11 /* Function for fast encoding of an input fragment, independently from the input 12 history. This function uses two-pass processing: in the first pass we save 13 the found backward matches and literal bytes into a buffer, and in the 14 second pass we emit them into the bit stream using prefix codes built based 15 on the actual command and literal byte histograms. */ 16 17 const kCompressFragmentTwoPassBlockSize uint = 1 << 17 18 19 func hash1(p []byte, shift uint, length uint) uint32 { 20 var h uint64 = (binary.LittleEndian.Uint64(p) << ((8 - length) * 8)) * uint64(kHashMul32) 21 return uint32(h >> shift) 22 } 23 24 func hashBytesAtOffset(v uint64, offset uint, shift uint, length uint) uint32 { 25 assert(offset <= 8-length) 26 { 27 var h uint64 = ((v >> (8 * offset)) << ((8 - length) * 8)) * uint64(kHashMul32) 28 return uint32(h >> shift) 29 } 30 } 31 32 func isMatch1(p1 []byte, p2 []byte, length uint) bool { 33 if binary.LittleEndian.Uint32(p1) != binary.LittleEndian.Uint32(p2) { 34 return false 35 } 36 if length == 4 { 37 return true 38 } 39 return p1[4] == p2[4] && p1[5] == p2[5] 40 } 41 42 /* Builds a command and distance prefix code (each 64 symbols) into "depth" and 43 "bits" based on "histogram" and stores it into the bit stream. */ 44 func buildAndStoreCommandPrefixCode(histogram []uint32, depth []byte, bits []uint16, storage_ix *uint, storage []byte) { 45 var tree [129]huffmanTree 46 var cmd_depth = [numCommandSymbols]byte{0} 47 /* Tree size for building a tree over 64 symbols is 2 * 64 + 1. */ 48 49 var cmd_bits [64]uint16 50 createHuffmanTree(histogram, 64, 15, tree[:], depth) 51 createHuffmanTree(histogram[64:], 64, 14, tree[:], depth[64:]) 52 53 /* We have to jump through a few hoops here in order to compute 54 the command bits because the symbols are in a different order than in 55 the full alphabet. This looks complicated, but having the symbols 56 in this order in the command bits saves a few branches in the Emit* 57 functions. */ 58 copy(cmd_depth[:], depth[24:][:24]) 59 60 copy(cmd_depth[24:][:], depth[:8]) 61 copy(cmd_depth[32:][:], depth[48:][:8]) 62 copy(cmd_depth[40:][:], depth[8:][:8]) 63 copy(cmd_depth[48:][:], depth[56:][:8]) 64 copy(cmd_depth[56:][:], depth[16:][:8]) 65 convertBitDepthsToSymbols(cmd_depth[:], 64, cmd_bits[:]) 66 copy(bits, cmd_bits[24:][:8]) 67 copy(bits[8:], cmd_bits[40:][:8]) 68 copy(bits[16:], cmd_bits[56:][:8]) 69 copy(bits[24:], cmd_bits[:24]) 70 copy(bits[48:], cmd_bits[32:][:8]) 71 copy(bits[56:], cmd_bits[48:][:8]) 72 convertBitDepthsToSymbols(depth[64:], 64, bits[64:]) 73 { 74 /* Create the bit length array for the full command alphabet. */ 75 var i uint 76 for i := 0; i < int(64); i++ { 77 cmd_depth[i] = 0 78 } /* only 64 first values were used */ 79 copy(cmd_depth[:], depth[24:][:8]) 80 copy(cmd_depth[64:][:], depth[32:][:8]) 81 copy(cmd_depth[128:][:], depth[40:][:8]) 82 copy(cmd_depth[192:][:], depth[48:][:8]) 83 copy(cmd_depth[384:][:], depth[56:][:8]) 84 for i = 0; i < 8; i++ { 85 cmd_depth[128+8*i] = depth[i] 86 cmd_depth[256+8*i] = depth[8+i] 87 cmd_depth[448+8*i] = depth[16+i] 88 } 89 90 storeHuffmanTree(cmd_depth[:], numCommandSymbols, tree[:], storage_ix, storage) 91 } 92 93 storeHuffmanTree(depth[64:], 64, tree[:], storage_ix, storage) 94 } 95 96 func emitInsertLen(insertlen uint32, commands *[]uint32) { 97 if insertlen < 6 { 98 (*commands)[0] = insertlen 99 } else if insertlen < 130 { 100 var tail uint32 = insertlen - 2 101 var nbits uint32 = log2FloorNonZero(uint(tail)) - 1 102 var prefix uint32 = tail >> nbits 103 var inscode uint32 = (nbits << 1) + prefix + 2 104 var extra uint32 = tail - (prefix << nbits) 105 (*commands)[0] = inscode | extra<<8 106 } else if insertlen < 2114 { 107 var tail uint32 = insertlen - 66 108 var nbits uint32 = log2FloorNonZero(uint(tail)) 109 var code uint32 = nbits + 10 110 var extra uint32 = tail - (1 << nbits) 111 (*commands)[0] = code | extra<<8 112 } else if insertlen < 6210 { 113 var extra uint32 = insertlen - 2114 114 (*commands)[0] = 21 | extra<<8 115 } else if insertlen < 22594 { 116 var extra uint32 = insertlen - 6210 117 (*commands)[0] = 22 | extra<<8 118 } else { 119 var extra uint32 = insertlen - 22594 120 (*commands)[0] = 23 | extra<<8 121 } 122 123 *commands = (*commands)[1:] 124 } 125 126 func emitCopyLen(copylen uint, commands *[]uint32) { 127 if copylen < 10 { 128 (*commands)[0] = uint32(copylen + 38) 129 } else if copylen < 134 { 130 var tail uint = copylen - 6 131 var nbits uint = uint(log2FloorNonZero(tail) - 1) 132 var prefix uint = tail >> nbits 133 var code uint = (nbits << 1) + prefix + 44 134 var extra uint = tail - (prefix << nbits) 135 (*commands)[0] = uint32(code | extra<<8) 136 } else if copylen < 2118 { 137 var tail uint = copylen - 70 138 var nbits uint = uint(log2FloorNonZero(tail)) 139 var code uint = nbits + 52 140 var extra uint = tail - (uint(1) << nbits) 141 (*commands)[0] = uint32(code | extra<<8) 142 } else { 143 var extra uint = copylen - 2118 144 (*commands)[0] = uint32(63 | extra<<8) 145 } 146 147 *commands = (*commands)[1:] 148 } 149 150 func emitCopyLenLastDistance(copylen uint, commands *[]uint32) { 151 if copylen < 12 { 152 (*commands)[0] = uint32(copylen + 20) 153 *commands = (*commands)[1:] 154 } else if copylen < 72 { 155 var tail uint = copylen - 8 156 var nbits uint = uint(log2FloorNonZero(tail) - 1) 157 var prefix uint = tail >> nbits 158 var code uint = (nbits << 1) + prefix + 28 159 var extra uint = tail - (prefix << nbits) 160 (*commands)[0] = uint32(code | extra<<8) 161 *commands = (*commands)[1:] 162 } else if copylen < 136 { 163 var tail uint = copylen - 8 164 var code uint = (tail >> 5) + 54 165 var extra uint = tail & 31 166 (*commands)[0] = uint32(code | extra<<8) 167 *commands = (*commands)[1:] 168 (*commands)[0] = 64 169 *commands = (*commands)[1:] 170 } else if copylen < 2120 { 171 var tail uint = copylen - 72 172 var nbits uint = uint(log2FloorNonZero(tail)) 173 var code uint = nbits + 52 174 var extra uint = tail - (uint(1) << nbits) 175 (*commands)[0] = uint32(code | extra<<8) 176 *commands = (*commands)[1:] 177 (*commands)[0] = 64 178 *commands = (*commands)[1:] 179 } else { 180 var extra uint = copylen - 2120 181 (*commands)[0] = uint32(63 | extra<<8) 182 *commands = (*commands)[1:] 183 (*commands)[0] = 64 184 *commands = (*commands)[1:] 185 } 186 } 187 188 func emitDistance(distance uint32, commands *[]uint32) { 189 var d uint32 = distance + 3 190 var nbits uint32 = log2FloorNonZero(uint(d)) - 1 191 var prefix uint32 = (d >> nbits) & 1 192 var offset uint32 = (2 + prefix) << nbits 193 var distcode uint32 = 2*(nbits-1) + prefix + 80 194 var extra uint32 = d - offset 195 (*commands)[0] = distcode | extra<<8 196 *commands = (*commands)[1:] 197 } 198 199 /* REQUIRES: len <= 1 << 24. */ 200 func storeMetaBlockHeader(len uint, is_uncompressed bool, storage_ix *uint, storage []byte) { 201 var nibbles uint = 6 202 203 /* ISLAST */ 204 writeBits(1, 0, storage_ix, storage) 205 206 if len <= 1<<16 { 207 nibbles = 4 208 } else if len <= 1<<20 { 209 nibbles = 5 210 } 211 212 writeBits(2, uint64(nibbles)-4, storage_ix, storage) 213 writeBits(nibbles*4, uint64(len)-1, storage_ix, storage) 214 215 /* ISUNCOMPRESSED */ 216 writeSingleBit(is_uncompressed, storage_ix, storage) 217 } 218 219 func createCommands(input []byte, block_size uint, input_size uint, base_ip_ptr []byte, table []int, table_bits uint, min_match uint, literals *[]byte, commands *[]uint32) { 220 var ip int = 0 221 var shift uint = 64 - table_bits 222 var ip_end int = int(block_size) 223 var base_ip int = -cap(base_ip_ptr) + cap(input) 224 var next_emit int = 0 225 var last_distance int = -1 226 /* "ip" is the input pointer. */ 227 228 const kInputMarginBytes uint = windowGap 229 230 /* "next_emit" is a pointer to the first byte that is not covered by a 231 previous copy. Bytes between "next_emit" and the start of the next copy or 232 the end of the input will be emitted as literal bytes. */ 233 if block_size >= kInputMarginBytes { 234 var len_limit uint = brotli_min_size_t(block_size-min_match, input_size-kInputMarginBytes) 235 var ip_limit int = int(len_limit) 236 /* For the last block, we need to keep a 16 bytes margin so that we can be 237 sure that all distances are at most window size - 16. 238 For all other blocks, we only need to keep a margin of 5 bytes so that 239 we don't go over the block size with a copy. */ 240 241 var next_hash uint32 242 ip++ 243 for next_hash = hash1(input[ip:], shift, min_match); ; { 244 var skip uint32 = 32 245 var next_ip int = ip 246 /* Step 1: Scan forward in the input looking for a 6-byte-long match. 247 If we get close to exhausting the input then goto emit_remainder. 248 249 Heuristic match skipping: If 32 bytes are scanned with no matches 250 found, start looking only at every other byte. If 32 more bytes are 251 scanned, look at every third byte, etc.. When a match is found, 252 immediately go back to looking at every byte. This is a small loss 253 (~5% performance, ~0.1% density) for compressible data due to more 254 bookkeeping, but for non-compressible data (such as JPEG) it's a huge 255 win since the compressor quickly "realizes" the data is incompressible 256 and doesn't bother looking for matches everywhere. 257 258 The "skip" variable keeps track of how many bytes there are since the 259 last match; dividing it by 32 (ie. right-shifting by five) gives the 260 number of bytes to move ahead for each iteration. */ 261 262 var candidate int 263 264 assert(next_emit < ip) 265 266 trawl: 267 for { 268 var hash uint32 = next_hash 269 var bytes_between_hash_lookups uint32 = skip >> 5 270 skip++ 271 ip = next_ip 272 assert(hash == hash1(input[ip:], shift, min_match)) 273 next_ip = int(uint32(ip) + bytes_between_hash_lookups) 274 if next_ip > ip_limit { 275 goto emit_remainder 276 } 277 278 next_hash = hash1(input[next_ip:], shift, min_match) 279 candidate = ip - last_distance 280 if isMatch1(input[ip:], base_ip_ptr[candidate-base_ip:], min_match) { 281 if candidate < ip { 282 table[hash] = int(ip - base_ip) 283 break 284 } 285 } 286 287 candidate = base_ip + table[hash] 288 assert(candidate >= base_ip) 289 assert(candidate < ip) 290 291 table[hash] = int(ip - base_ip) 292 if isMatch1(input[ip:], base_ip_ptr[candidate-base_ip:], min_match) { 293 break 294 } 295 } 296 297 /* Check copy distance. If candidate is not feasible, continue search. 298 Checking is done outside of hot loop to reduce overhead. */ 299 if ip-candidate > maxDistance_compress_fragment { 300 goto trawl 301 } 302 303 /* Step 2: Emit the found match together with the literal bytes from 304 "next_emit", and then see if we can find a next match immediately 305 afterwards. Repeat until we find no match for the input 306 without emitting some literal bytes. */ 307 { 308 var base int = ip 309 /* > 0 */ 310 var matched uint = min_match + findMatchLengthWithLimit(base_ip_ptr[uint(candidate-base_ip)+min_match:], input[uint(ip)+min_match:], uint(ip_end-ip)-min_match) 311 var distance int = int(base - candidate) 312 /* We have a 6-byte match at ip, and we need to emit bytes in 313 [next_emit, ip). */ 314 315 var insert int = int(base - next_emit) 316 ip += int(matched) 317 emitInsertLen(uint32(insert), commands) 318 copy(*literals, input[next_emit:][:uint(insert)]) 319 *literals = (*literals)[insert:] 320 if distance == last_distance { 321 (*commands)[0] = 64 322 *commands = (*commands)[1:] 323 } else { 324 emitDistance(uint32(distance), commands) 325 last_distance = distance 326 } 327 328 emitCopyLenLastDistance(matched, commands) 329 330 next_emit = ip 331 if ip >= ip_limit { 332 goto emit_remainder 333 } 334 { 335 var input_bytes uint64 336 var cur_hash uint32 337 /* We could immediately start working at ip now, but to improve 338 compression we first update "table" with the hashes of some 339 positions within the last copy. */ 340 341 var prev_hash uint32 342 if min_match == 4 { 343 input_bytes = binary.LittleEndian.Uint64(input[ip-3:]) 344 cur_hash = hashBytesAtOffset(input_bytes, 3, shift, min_match) 345 prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) 346 table[prev_hash] = int(ip - base_ip - 3) 347 prev_hash = hashBytesAtOffset(input_bytes, 1, shift, min_match) 348 table[prev_hash] = int(ip - base_ip - 2) 349 prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) 350 table[prev_hash] = int(ip - base_ip - 1) 351 } else { 352 input_bytes = binary.LittleEndian.Uint64(input[ip-5:]) 353 prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) 354 table[prev_hash] = int(ip - base_ip - 5) 355 prev_hash = hashBytesAtOffset(input_bytes, 1, shift, min_match) 356 table[prev_hash] = int(ip - base_ip - 4) 357 prev_hash = hashBytesAtOffset(input_bytes, 2, shift, min_match) 358 table[prev_hash] = int(ip - base_ip - 3) 359 input_bytes = binary.LittleEndian.Uint64(input[ip-2:]) 360 cur_hash = hashBytesAtOffset(input_bytes, 2, shift, min_match) 361 prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) 362 table[prev_hash] = int(ip - base_ip - 2) 363 prev_hash = hashBytesAtOffset(input_bytes, 1, shift, min_match) 364 table[prev_hash] = int(ip - base_ip - 1) 365 } 366 367 candidate = base_ip + table[cur_hash] 368 table[cur_hash] = int(ip - base_ip) 369 } 370 } 371 372 for ip-candidate <= maxDistance_compress_fragment && isMatch1(input[ip:], base_ip_ptr[candidate-base_ip:], min_match) { 373 var base int = ip 374 /* We have a 6-byte match at ip, and no need to emit any 375 literal bytes prior to ip. */ 376 377 var matched uint = min_match + findMatchLengthWithLimit(base_ip_ptr[uint(candidate-base_ip)+min_match:], input[uint(ip)+min_match:], uint(ip_end-ip)-min_match) 378 ip += int(matched) 379 last_distance = int(base - candidate) /* > 0 */ 380 emitCopyLen(matched, commands) 381 emitDistance(uint32(last_distance), commands) 382 383 next_emit = ip 384 if ip >= ip_limit { 385 goto emit_remainder 386 } 387 { 388 var input_bytes uint64 389 var cur_hash uint32 390 /* We could immediately start working at ip now, but to improve 391 compression we first update "table" with the hashes of some 392 positions within the last copy. */ 393 394 var prev_hash uint32 395 if min_match == 4 { 396 input_bytes = binary.LittleEndian.Uint64(input[ip-3:]) 397 cur_hash = hashBytesAtOffset(input_bytes, 3, shift, min_match) 398 prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) 399 table[prev_hash] = int(ip - base_ip - 3) 400 prev_hash = hashBytesAtOffset(input_bytes, 1, shift, min_match) 401 table[prev_hash] = int(ip - base_ip - 2) 402 prev_hash = hashBytesAtOffset(input_bytes, 2, shift, min_match) 403 table[prev_hash] = int(ip - base_ip - 1) 404 } else { 405 input_bytes = binary.LittleEndian.Uint64(input[ip-5:]) 406 prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) 407 table[prev_hash] = int(ip - base_ip - 5) 408 prev_hash = hashBytesAtOffset(input_bytes, 1, shift, min_match) 409 table[prev_hash] = int(ip - base_ip - 4) 410 prev_hash = hashBytesAtOffset(input_bytes, 2, shift, min_match) 411 table[prev_hash] = int(ip - base_ip - 3) 412 input_bytes = binary.LittleEndian.Uint64(input[ip-2:]) 413 cur_hash = hashBytesAtOffset(input_bytes, 2, shift, min_match) 414 prev_hash = hashBytesAtOffset(input_bytes, 0, shift, min_match) 415 table[prev_hash] = int(ip - base_ip - 2) 416 prev_hash = hashBytesAtOffset(input_bytes, 1, shift, min_match) 417 table[prev_hash] = int(ip - base_ip - 1) 418 } 419 420 candidate = base_ip + table[cur_hash] 421 table[cur_hash] = int(ip - base_ip) 422 } 423 } 424 425 ip++ 426 next_hash = hash1(input[ip:], shift, min_match) 427 } 428 } 429 430 emit_remainder: 431 assert(next_emit <= ip_end) 432 433 /* Emit the remaining bytes as literals. */ 434 if next_emit < ip_end { 435 var insert uint32 = uint32(ip_end - next_emit) 436 emitInsertLen(insert, commands) 437 copy(*literals, input[next_emit:][:insert]) 438 *literals = (*literals)[insert:] 439 } 440 } 441 442 var storeCommands_kNumExtraBits = [128]uint32{ 443 0, 444 0, 445 0, 446 0, 447 0, 448 0, 449 1, 450 1, 451 2, 452 2, 453 3, 454 3, 455 4, 456 4, 457 5, 458 5, 459 6, 460 7, 461 8, 462 9, 463 10, 464 12, 465 14, 466 24, 467 0, 468 0, 469 0, 470 0, 471 0, 472 0, 473 0, 474 0, 475 1, 476 1, 477 2, 478 2, 479 3, 480 3, 481 4, 482 4, 483 0, 484 0, 485 0, 486 0, 487 0, 488 0, 489 0, 490 0, 491 1, 492 1, 493 2, 494 2, 495 3, 496 3, 497 4, 498 4, 499 5, 500 5, 501 6, 502 7, 503 8, 504 9, 505 10, 506 24, 507 0, 508 0, 509 0, 510 0, 511 0, 512 0, 513 0, 514 0, 515 0, 516 0, 517 0, 518 0, 519 0, 520 0, 521 0, 522 0, 523 1, 524 1, 525 2, 526 2, 527 3, 528 3, 529 4, 530 4, 531 5, 532 5, 533 6, 534 6, 535 7, 536 7, 537 8, 538 8, 539 9, 540 9, 541 10, 542 10, 543 11, 544 11, 545 12, 546 12, 547 13, 548 13, 549 14, 550 14, 551 15, 552 15, 553 16, 554 16, 555 17, 556 17, 557 18, 558 18, 559 19, 560 19, 561 20, 562 20, 563 21, 564 21, 565 22, 566 22, 567 23, 568 23, 569 24, 570 24, 571 } 572 var storeCommands_kInsertOffset = [24]uint32{ 573 0, 574 1, 575 2, 576 3, 577 4, 578 5, 579 6, 580 8, 581 10, 582 14, 583 18, 584 26, 585 34, 586 50, 587 66, 588 98, 589 130, 590 194, 591 322, 592 578, 593 1090, 594 2114, 595 6210, 596 22594, 597 } 598 599 func storeCommands(literals []byte, num_literals uint, commands []uint32, num_commands uint, storage_ix *uint, storage []byte) { 600 var lit_depths [256]byte 601 var lit_bits [256]uint16 602 var lit_histo = [256]uint32{0} 603 var cmd_depths = [128]byte{0} 604 var cmd_bits = [128]uint16{0} 605 var cmd_histo = [128]uint32{0} 606 var i uint 607 for i = 0; i < num_literals; i++ { 608 lit_histo[literals[i]]++ 609 } 610 611 buildAndStoreHuffmanTreeFast(lit_histo[:], num_literals, /* max_bits = */ 612 8, lit_depths[:], lit_bits[:], storage_ix, storage) 613 614 for i = 0; i < num_commands; i++ { 615 var code uint32 = commands[i] & 0xFF 616 assert(code < 128) 617 cmd_histo[code]++ 618 } 619 620 cmd_histo[1] += 1 621 cmd_histo[2] += 1 622 cmd_histo[64] += 1 623 cmd_histo[84] += 1 624 buildAndStoreCommandPrefixCode(cmd_histo[:], cmd_depths[:], cmd_bits[:], storage_ix, storage) 625 626 for i = 0; i < num_commands; i++ { 627 var cmd uint32 = commands[i] 628 var code uint32 = cmd & 0xFF 629 var extra uint32 = cmd >> 8 630 assert(code < 128) 631 writeBits(uint(cmd_depths[code]), uint64(cmd_bits[code]), storage_ix, storage) 632 writeBits(uint(storeCommands_kNumExtraBits[code]), uint64(extra), storage_ix, storage) 633 if code < 24 { 634 var insert uint32 = storeCommands_kInsertOffset[code] + extra 635 var j uint32 636 for j = 0; j < insert; j++ { 637 var lit byte = literals[0] 638 writeBits(uint(lit_depths[lit]), uint64(lit_bits[lit]), storage_ix, storage) 639 literals = literals[1:] 640 } 641 } 642 } 643 } 644 645 /* Acceptable loss for uncompressible speedup is 2% */ 646 const minRatio = 0.98 647 648 const sampleRate = 43 649 650 func shouldCompress(input []byte, input_size uint, num_literals uint) bool { 651 var corpus_size float64 = float64(input_size) 652 if float64(num_literals) < minRatio*corpus_size { 653 return true 654 } else { 655 var literal_histo = [256]uint32{0} 656 var max_total_bit_cost float64 = corpus_size * 8 * minRatio / sampleRate 657 var i uint 658 for i = 0; i < input_size; i += sampleRate { 659 literal_histo[input[i]]++ 660 } 661 662 return bitsEntropy(literal_histo[:], 256) < max_total_bit_cost 663 } 664 } 665 666 func rewindBitPosition(new_storage_ix uint, storage_ix *uint, storage []byte) { 667 var bitpos uint = new_storage_ix & 7 668 var mask uint = (1 << bitpos) - 1 669 storage[new_storage_ix>>3] &= byte(mask) 670 *storage_ix = new_storage_ix 671 } 672 673 func emitUncompressedMetaBlock(input []byte, input_size uint, storage_ix *uint, storage []byte) { 674 storeMetaBlockHeader(input_size, true, storage_ix, storage) 675 *storage_ix = (*storage_ix + 7) &^ 7 676 copy(storage[*storage_ix>>3:], input[:input_size]) 677 *storage_ix += input_size << 3 678 storage[*storage_ix>>3] = 0 679 } 680 681 func compressFragmentTwoPassImpl(input []byte, input_size uint, is_last bool, command_buf []uint32, literal_buf []byte, table []int, table_bits uint, min_match uint, storage_ix *uint, storage []byte) { 682 /* Save the start of the first block for position and distance computations. 683 */ 684 var base_ip []byte = input 685 686 for input_size > 0 { 687 var block_size uint = brotli_min_size_t(input_size, kCompressFragmentTwoPassBlockSize) 688 var commands []uint32 = command_buf 689 var literals []byte = literal_buf 690 var num_literals uint 691 createCommands(input, block_size, input_size, base_ip, table, table_bits, min_match, &literals, &commands) 692 num_literals = uint(-cap(literals) + cap(literal_buf)) 693 if shouldCompress(input, block_size, num_literals) { 694 var num_commands uint = uint(-cap(commands) + cap(command_buf)) 695 storeMetaBlockHeader(block_size, false, storage_ix, storage) 696 697 /* No block splits, no contexts. */ 698 writeBits(13, 0, storage_ix, storage) 699 700 storeCommands(literal_buf, num_literals, command_buf, num_commands, storage_ix, storage) 701 } else { 702 /* Since we did not find many backward references and the entropy of 703 the data is close to 8 bits, we can simply emit an uncompressed block. 704 This makes compression speed of uncompressible data about 3x faster. */ 705 emitUncompressedMetaBlock(input, block_size, storage_ix, storage) 706 } 707 708 input = input[block_size:] 709 input_size -= block_size 710 } 711 } 712 713 /* Compresses "input" string to the "*storage" buffer as one or more complete 714 meta-blocks, and updates the "*storage_ix" bit position. 715 716 If "is_last" is 1, emits an additional empty last meta-block. 717 718 REQUIRES: "input_size" is greater than zero, or "is_last" is 1. 719 REQUIRES: "input_size" is less or equal to maximal metablock size (1 << 24). 720 REQUIRES: "command_buf" and "literal_buf" point to at least 721 kCompressFragmentTwoPassBlockSize long arrays. 722 REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. 723 REQUIRES: "table_size" is a power of two 724 OUTPUT: maximal copy distance <= |input_size| 725 OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */ 726 func compressFragmentTwoPass(input []byte, input_size uint, is_last bool, command_buf []uint32, literal_buf []byte, table []int, table_size uint, storage_ix *uint, storage []byte) { 727 var initial_storage_ix uint = *storage_ix 728 var table_bits uint = uint(log2FloorNonZero(table_size)) 729 var min_match uint 730 if table_bits <= 15 { 731 min_match = 4 732 } else { 733 min_match = 6 734 } 735 compressFragmentTwoPassImpl(input, input_size, is_last, command_buf, literal_buf, table, table_bits, min_match, storage_ix, storage) 736 737 /* If output is larger than single uncompressed block, rewrite it. */ 738 if *storage_ix-initial_storage_ix > 31+(input_size<<3) { 739 rewindBitPosition(initial_storage_ix, storage_ix, storage) 740 emitUncompressedMetaBlock(input, input_size, storage_ix, storage) 741 } 742 743 if is_last { 744 writeBits(1, 1, storage_ix, storage) /* islast */ 745 writeBits(1, 1, storage_ix, storage) /* isempty */ 746 *storage_ix = (*storage_ix + 7) &^ 7 747 } 748 }