github.com/andybalholm/brotli@v1.0.6/backward_references_hq.go (about) 1 package brotli 2 3 import "math" 4 5 type zopfliNode struct { 6 length uint32 7 distance uint32 8 dcode_insert_length uint32 9 u struct { 10 cost float32 11 next uint32 12 shortcut uint32 13 } 14 } 15 16 const maxEffectiveDistanceAlphabetSize = 544 17 18 const kInfinity float32 = 1.7e38 /* ~= 2 ^ 127 */ 19 20 var kDistanceCacheIndex = []uint32{0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1} 21 22 var kDistanceCacheOffset = []int{0, 0, 0, 0, -1, 1, -2, 2, -3, 3, -1, 1, -2, 2, -3, 3} 23 24 func initZopfliNodes(array []zopfliNode, length uint) { 25 var stub zopfliNode 26 var i uint 27 stub.length = 1 28 stub.distance = 0 29 stub.dcode_insert_length = 0 30 stub.u.cost = kInfinity 31 for i = 0; i < length; i++ { 32 array[i] = stub 33 } 34 } 35 36 func zopfliNodeCopyLength(self *zopfliNode) uint32 { 37 return self.length & 0x1FFFFFF 38 } 39 40 func zopfliNodeLengthCode(self *zopfliNode) uint32 { 41 var modifier uint32 = self.length >> 25 42 return zopfliNodeCopyLength(self) + 9 - modifier 43 } 44 45 func zopfliNodeCopyDistance(self *zopfliNode) uint32 { 46 return self.distance 47 } 48 49 func zopfliNodeDistanceCode(self *zopfliNode) uint32 { 50 var short_code uint32 = self.dcode_insert_length >> 27 51 if short_code == 0 { 52 return zopfliNodeCopyDistance(self) + numDistanceShortCodes - 1 53 } else { 54 return short_code - 1 55 } 56 } 57 58 func zopfliNodeCommandLength(self *zopfliNode) uint32 { 59 return zopfliNodeCopyLength(self) + (self.dcode_insert_length & 0x7FFFFFF) 60 } 61 62 /* Histogram based cost model for zopflification. */ 63 type zopfliCostModel struct { 64 cost_cmd_ [numCommandSymbols]float32 65 cost_dist_ []float32 66 distance_histogram_size uint32 67 literal_costs_ []float32 68 min_cost_cmd_ float32 69 num_bytes_ uint 70 } 71 72 func initZopfliCostModel(self *zopfliCostModel, dist *distanceParams, num_bytes uint) { 73 var distance_histogram_size uint32 = dist.alphabet_size 74 if distance_histogram_size > maxEffectiveDistanceAlphabetSize { 75 distance_histogram_size = maxEffectiveDistanceAlphabetSize 76 } 77 78 self.num_bytes_ = num_bytes 79 self.literal_costs_ = make([]float32, (num_bytes + 2)) 80 self.cost_dist_ = make([]float32, (dist.alphabet_size)) 81 self.distance_histogram_size = distance_histogram_size 82 } 83 84 func cleanupZopfliCostModel(self *zopfliCostModel) { 85 self.literal_costs_ = nil 86 self.cost_dist_ = nil 87 } 88 89 func setCost(histogram []uint32, histogram_size uint, literal_histogram bool, cost []float32) { 90 var sum uint = 0 91 var missing_symbol_sum uint 92 var log2sum float32 93 var missing_symbol_cost float32 94 var i uint 95 for i = 0; i < histogram_size; i++ { 96 sum += uint(histogram[i]) 97 } 98 99 log2sum = float32(fastLog2(sum)) 100 missing_symbol_sum = sum 101 if !literal_histogram { 102 for i = 0; i < histogram_size; i++ { 103 if histogram[i] == 0 { 104 missing_symbol_sum++ 105 } 106 } 107 } 108 109 missing_symbol_cost = float32(fastLog2(missing_symbol_sum)) + 2 110 for i = 0; i < histogram_size; i++ { 111 if histogram[i] == 0 { 112 cost[i] = missing_symbol_cost 113 continue 114 } 115 116 /* Shannon bits for this symbol. */ 117 cost[i] = log2sum - float32(fastLog2(uint(histogram[i]))) 118 119 /* Cannot be coded with less than 1 bit */ 120 if cost[i] < 1 { 121 cost[i] = 1 122 } 123 } 124 } 125 126 func zopfliCostModelSetFromCommands(self *zopfliCostModel, position uint, ringbuffer []byte, ringbuffer_mask uint, commands []command, last_insert_len uint) { 127 var histogram_literal [numLiteralSymbols]uint32 128 var histogram_cmd [numCommandSymbols]uint32 129 var histogram_dist [maxEffectiveDistanceAlphabetSize]uint32 130 var cost_literal [numLiteralSymbols]float32 131 var pos uint = position - last_insert_len 132 var min_cost_cmd float32 = kInfinity 133 var cost_cmd []float32 = self.cost_cmd_[:] 134 var literal_costs []float32 135 136 histogram_literal = [numLiteralSymbols]uint32{} 137 histogram_cmd = [numCommandSymbols]uint32{} 138 histogram_dist = [maxEffectiveDistanceAlphabetSize]uint32{} 139 140 for i := range commands { 141 var inslength uint = uint(commands[i].insert_len_) 142 var copylength uint = uint(commandCopyLen(&commands[i])) 143 var distcode uint = uint(commands[i].dist_prefix_) & 0x3FF 144 var cmdcode uint = uint(commands[i].cmd_prefix_) 145 var j uint 146 147 histogram_cmd[cmdcode]++ 148 if cmdcode >= 128 { 149 histogram_dist[distcode]++ 150 } 151 152 for j = 0; j < inslength; j++ { 153 histogram_literal[ringbuffer[(pos+j)&ringbuffer_mask]]++ 154 } 155 156 pos += inslength + copylength 157 } 158 159 setCost(histogram_literal[:], numLiteralSymbols, true, cost_literal[:]) 160 setCost(histogram_cmd[:], numCommandSymbols, false, cost_cmd) 161 setCost(histogram_dist[:], uint(self.distance_histogram_size), false, self.cost_dist_) 162 163 for i := 0; i < numCommandSymbols; i++ { 164 min_cost_cmd = brotli_min_float(min_cost_cmd, cost_cmd[i]) 165 } 166 167 self.min_cost_cmd_ = min_cost_cmd 168 { 169 literal_costs = self.literal_costs_ 170 var literal_carry float32 = 0.0 171 num_bytes := int(self.num_bytes_) 172 literal_costs[0] = 0.0 173 for i := 0; i < num_bytes; i++ { 174 literal_carry += cost_literal[ringbuffer[(position+uint(i))&ringbuffer_mask]] 175 literal_costs[i+1] = literal_costs[i] + literal_carry 176 literal_carry -= literal_costs[i+1] - literal_costs[i] 177 } 178 } 179 } 180 181 func zopfliCostModelSetFromLiteralCosts(self *zopfliCostModel, position uint, ringbuffer []byte, ringbuffer_mask uint) { 182 var literal_costs []float32 = self.literal_costs_ 183 var literal_carry float32 = 0.0 184 var cost_dist []float32 = self.cost_dist_ 185 var cost_cmd []float32 = self.cost_cmd_[:] 186 var num_bytes uint = self.num_bytes_ 187 var i uint 188 estimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask, ringbuffer, literal_costs[1:]) 189 literal_costs[0] = 0.0 190 for i = 0; i < num_bytes; i++ { 191 literal_carry += literal_costs[i+1] 192 literal_costs[i+1] = literal_costs[i] + literal_carry 193 literal_carry -= literal_costs[i+1] - literal_costs[i] 194 } 195 196 for i = 0; i < numCommandSymbols; i++ { 197 cost_cmd[i] = float32(fastLog2(uint(11 + uint32(i)))) 198 } 199 200 for i = 0; uint32(i) < self.distance_histogram_size; i++ { 201 cost_dist[i] = float32(fastLog2(uint(20 + uint32(i)))) 202 } 203 204 self.min_cost_cmd_ = float32(fastLog2(11)) 205 } 206 207 func zopfliCostModelGetCommandCost(self *zopfliCostModel, cmdcode uint16) float32 { 208 return self.cost_cmd_[cmdcode] 209 } 210 211 func zopfliCostModelGetDistanceCost(self *zopfliCostModel, distcode uint) float32 { 212 return self.cost_dist_[distcode] 213 } 214 215 func zopfliCostModelGetLiteralCosts(self *zopfliCostModel, from uint, to uint) float32 { 216 return self.literal_costs_[to] - self.literal_costs_[from] 217 } 218 219 func zopfliCostModelGetMinCostCmd(self *zopfliCostModel) float32 { 220 return self.min_cost_cmd_ 221 } 222 223 /* REQUIRES: len >= 2, start_pos <= pos */ 224 /* REQUIRES: cost < kInfinity, nodes[start_pos].cost < kInfinity */ 225 /* Maintains the "ZopfliNode array invariant". */ 226 func updateZopfliNode(nodes []zopfliNode, pos uint, start_pos uint, len uint, len_code uint, dist uint, short_code uint, cost float32) { 227 var next *zopfliNode = &nodes[pos+len] 228 next.length = uint32(len | (len+9-len_code)<<25) 229 next.distance = uint32(dist) 230 next.dcode_insert_length = uint32(short_code<<27 | (pos - start_pos)) 231 next.u.cost = cost 232 } 233 234 type posData struct { 235 pos uint 236 distance_cache [4]int 237 costdiff float32 238 cost float32 239 } 240 241 /* Maintains the smallest 8 cost difference together with their positions */ 242 type startPosQueue struct { 243 q_ [8]posData 244 idx_ uint 245 } 246 247 func initStartPosQueue(self *startPosQueue) { 248 self.idx_ = 0 249 } 250 251 func startPosQueueSize(self *startPosQueue) uint { 252 return brotli_min_size_t(self.idx_, 8) 253 } 254 255 func startPosQueuePush(self *startPosQueue, posdata *posData) { 256 var offset uint = ^(self.idx_) & 7 257 self.idx_++ 258 var len uint = startPosQueueSize(self) 259 var i uint 260 var q []posData = self.q_[:] 261 q[offset] = *posdata 262 263 /* Restore the sorted order. In the list of |len| items at most |len - 1| 264 adjacent element comparisons / swaps are required. */ 265 for i = 1; i < len; i++ { 266 if q[offset&7].costdiff > q[(offset+1)&7].costdiff { 267 var tmp posData = q[offset&7] 268 q[offset&7] = q[(offset+1)&7] 269 q[(offset+1)&7] = tmp 270 } 271 272 offset++ 273 } 274 } 275 276 func startPosQueueAt(self *startPosQueue, k uint) *posData { 277 return &self.q_[(k-self.idx_)&7] 278 } 279 280 /* Returns the minimum possible copy length that can improve the cost of any */ 281 /* future position. */ 282 func computeMinimumCopyLength(start_cost float32, nodes []zopfliNode, num_bytes uint, pos uint) uint { 283 var min_cost float32 = start_cost 284 var len uint = 2 285 var next_len_bucket uint = 4 286 /* Compute the minimum possible cost of reaching any future position. */ 287 288 var next_len_offset uint = 10 289 for pos+len <= num_bytes && nodes[pos+len].u.cost <= min_cost { 290 /* We already reached (pos + len) with no more cost than the minimum 291 possible cost of reaching anything from this pos, so there is no point in 292 looking for lengths <= len. */ 293 len++ 294 295 if len == next_len_offset { 296 /* We reached the next copy length code bucket, so we add one more 297 extra bit to the minimum cost. */ 298 min_cost += 1.0 299 300 next_len_offset += next_len_bucket 301 next_len_bucket *= 2 302 } 303 } 304 305 return uint(len) 306 } 307 308 /* REQUIRES: nodes[pos].cost < kInfinity 309 REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */ 310 func computeDistanceShortcut(block_start uint, pos uint, max_backward_limit uint, gap uint, nodes []zopfliNode) uint32 { 311 var clen uint = uint(zopfliNodeCopyLength(&nodes[pos])) 312 var ilen uint = uint(nodes[pos].dcode_insert_length & 0x7FFFFFF) 313 var dist uint = uint(zopfliNodeCopyDistance(&nodes[pos])) 314 315 /* Since |block_start + pos| is the end position of the command, the copy part 316 starts from |block_start + pos - clen|. Distances that are greater than 317 this or greater than |max_backward_limit| + |gap| are static dictionary 318 references, and do not update the last distances. 319 Also distance code 0 (last distance) does not update the last distances. */ 320 if pos == 0 { 321 return 0 322 } else if dist+clen <= block_start+pos+gap && dist <= max_backward_limit+gap && zopfliNodeDistanceCode(&nodes[pos]) > 0 { 323 return uint32(pos) 324 } else { 325 return nodes[pos-clen-ilen].u.shortcut 326 } 327 } 328 329 /* Fills in dist_cache[0..3] with the last four distances (as defined by 330 Section 4. of the Spec) that would be used at (block_start + pos) if we 331 used the shortest path of commands from block_start, computed from 332 nodes[0..pos]. The last four distances at block_start are in 333 starting_dist_cache[0..3]. 334 REQUIRES: nodes[pos].cost < kInfinity 335 REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */ 336 func computeDistanceCache(pos uint, starting_dist_cache []int, nodes []zopfliNode, dist_cache []int) { 337 var idx int = 0 338 var p uint = uint(nodes[pos].u.shortcut) 339 for idx < 4 && p > 0 { 340 var ilen uint = uint(nodes[p].dcode_insert_length & 0x7FFFFFF) 341 var clen uint = uint(zopfliNodeCopyLength(&nodes[p])) 342 var dist uint = uint(zopfliNodeCopyDistance(&nodes[p])) 343 dist_cache[idx] = int(dist) 344 idx++ 345 346 /* Because of prerequisite, p >= clen + ilen >= 2. */ 347 p = uint(nodes[p-clen-ilen].u.shortcut) 348 } 349 350 for ; idx < 4; idx++ { 351 dist_cache[idx] = starting_dist_cache[0] 352 starting_dist_cache = starting_dist_cache[1:] 353 } 354 } 355 356 /* Maintains "ZopfliNode array invariant" and pushes node to the queue, if it 357 is eligible. */ 358 func evaluateNode(block_start uint, pos uint, max_backward_limit uint, gap uint, starting_dist_cache []int, model *zopfliCostModel, queue *startPosQueue, nodes []zopfliNode) { 359 /* Save cost, because ComputeDistanceCache invalidates it. */ 360 var node_cost float32 = nodes[pos].u.cost 361 nodes[pos].u.shortcut = computeDistanceShortcut(block_start, pos, max_backward_limit, gap, nodes) 362 if node_cost <= zopfliCostModelGetLiteralCosts(model, 0, pos) { 363 var posdata posData 364 posdata.pos = pos 365 posdata.cost = node_cost 366 posdata.costdiff = node_cost - zopfliCostModelGetLiteralCosts(model, 0, pos) 367 computeDistanceCache(pos, starting_dist_cache, nodes, posdata.distance_cache[:]) 368 startPosQueuePush(queue, &posdata) 369 } 370 } 371 372 /* Returns longest copy length. */ 373 func updateNodes(num_bytes uint, block_start uint, pos uint, ringbuffer []byte, ringbuffer_mask uint, params *encoderParams, max_backward_limit uint, starting_dist_cache []int, num_matches uint, matches []backwardMatch, model *zopfliCostModel, queue *startPosQueue, nodes []zopfliNode) uint { 374 var cur_ix uint = block_start + pos 375 var cur_ix_masked uint = cur_ix & ringbuffer_mask 376 var max_distance uint = brotli_min_size_t(cur_ix, max_backward_limit) 377 var max_len uint = num_bytes - pos 378 var max_zopfli_len uint = maxZopfliLen(params) 379 var max_iters uint = maxZopfliCandidates(params) 380 var min_len uint 381 var result uint = 0 382 var k uint 383 var gap uint = 0 384 385 evaluateNode(block_start, pos, max_backward_limit, gap, starting_dist_cache, model, queue, nodes) 386 { 387 var posdata *posData = startPosQueueAt(queue, 0) 388 var min_cost float32 = (posdata.cost + zopfliCostModelGetMinCostCmd(model) + zopfliCostModelGetLiteralCosts(model, posdata.pos, pos)) 389 min_len = computeMinimumCopyLength(min_cost, nodes, num_bytes, pos) 390 } 391 392 /* Go over the command starting positions in order of increasing cost 393 difference. */ 394 for k = 0; k < max_iters && k < startPosQueueSize(queue); k++ { 395 var posdata *posData = startPosQueueAt(queue, k) 396 var start uint = posdata.pos 397 var inscode uint16 = getInsertLengthCode(pos - start) 398 var start_costdiff float32 = posdata.costdiff 399 var base_cost float32 = start_costdiff + float32(getInsertExtra(inscode)) + zopfliCostModelGetLiteralCosts(model, 0, pos) 400 var best_len uint = min_len - 1 401 var j uint = 0 402 /* Look for last distance matches using the distance cache from this 403 starting position. */ 404 for ; j < numDistanceShortCodes && best_len < max_len; j++ { 405 var idx uint = uint(kDistanceCacheIndex[j]) 406 var backward uint = uint(posdata.distance_cache[idx] + kDistanceCacheOffset[j]) 407 var prev_ix uint = cur_ix - backward 408 var len uint = 0 409 var continuation byte = ringbuffer[cur_ix_masked+best_len] 410 if cur_ix_masked+best_len > ringbuffer_mask { 411 break 412 } 413 414 if backward > max_distance+gap { 415 /* Word dictionary -> ignore. */ 416 continue 417 } 418 419 if backward <= max_distance { 420 /* Regular backward reference. */ 421 if prev_ix >= cur_ix { 422 continue 423 } 424 425 prev_ix &= ringbuffer_mask 426 if prev_ix+best_len > ringbuffer_mask || continuation != ringbuffer[prev_ix+best_len] { 427 continue 428 } 429 430 len = findMatchLengthWithLimit(ringbuffer[prev_ix:], ringbuffer[cur_ix_masked:], max_len) 431 } else { 432 continue 433 } 434 { 435 var dist_cost float32 = base_cost + zopfliCostModelGetDistanceCost(model, j) 436 var l uint 437 for l = best_len + 1; l <= len; l++ { 438 var copycode uint16 = getCopyLengthCode(l) 439 var cmdcode uint16 = combineLengthCodes(inscode, copycode, j == 0) 440 var tmp float32 441 if cmdcode < 128 { 442 tmp = base_cost 443 } else { 444 tmp = dist_cost 445 } 446 var cost float32 = tmp + float32(getCopyExtra(copycode)) + zopfliCostModelGetCommandCost(model, cmdcode) 447 if cost < nodes[pos+l].u.cost { 448 updateZopfliNode(nodes, pos, start, l, l, backward, j+1, cost) 449 result = brotli_max_size_t(result, l) 450 } 451 452 best_len = l 453 } 454 } 455 } 456 457 /* At higher iterations look only for new last distance matches, since 458 looking only for new command start positions with the same distances 459 does not help much. */ 460 if k >= 2 { 461 continue 462 } 463 { 464 /* Loop through all possible copy lengths at this position. */ 465 var len uint = min_len 466 for j = 0; j < num_matches; j++ { 467 var match backwardMatch = matches[j] 468 var dist uint = uint(match.distance) 469 var is_dictionary_match bool = (dist > max_distance+gap) 470 var dist_code uint = dist + numDistanceShortCodes - 1 471 var dist_symbol uint16 472 var distextra uint32 473 var distnumextra uint32 474 var dist_cost float32 475 var max_match_len uint 476 /* We already tried all possible last distance matches, so we can use 477 normal distance code here. */ 478 prefixEncodeCopyDistance(dist_code, uint(params.dist.num_direct_distance_codes), uint(params.dist.distance_postfix_bits), &dist_symbol, &distextra) 479 480 distnumextra = uint32(dist_symbol) >> 10 481 dist_cost = base_cost + float32(distnumextra) + zopfliCostModelGetDistanceCost(model, uint(dist_symbol)&0x3FF) 482 483 /* Try all copy lengths up until the maximum copy length corresponding 484 to this distance. If the distance refers to the static dictionary, or 485 the maximum length is long enough, try only one maximum length. */ 486 max_match_len = backwardMatchLength(&match) 487 488 if len < max_match_len && (is_dictionary_match || max_match_len > max_zopfli_len) { 489 len = max_match_len 490 } 491 492 for ; len <= max_match_len; len++ { 493 var len_code uint 494 if is_dictionary_match { 495 len_code = backwardMatchLengthCode(&match) 496 } else { 497 len_code = len 498 } 499 var copycode uint16 = getCopyLengthCode(len_code) 500 var cmdcode uint16 = combineLengthCodes(inscode, copycode, false) 501 var cost float32 = dist_cost + float32(getCopyExtra(copycode)) + zopfliCostModelGetCommandCost(model, cmdcode) 502 if cost < nodes[pos+len].u.cost { 503 updateZopfliNode(nodes, pos, start, uint(len), len_code, dist, 0, cost) 504 if len > result { 505 result = len 506 } 507 } 508 } 509 } 510 } 511 } 512 513 return result 514 } 515 516 func computeShortestPathFromNodes(num_bytes uint, nodes []zopfliNode) uint { 517 var index uint = num_bytes 518 var num_commands uint = 0 519 for nodes[index].dcode_insert_length&0x7FFFFFF == 0 && nodes[index].length == 1 { 520 index-- 521 } 522 nodes[index].u.next = math.MaxUint32 523 for index != 0 { 524 var len uint = uint(zopfliNodeCommandLength(&nodes[index])) 525 index -= uint(len) 526 nodes[index].u.next = uint32(len) 527 num_commands++ 528 } 529 530 return num_commands 531 } 532 533 /* REQUIRES: nodes != NULL and len(nodes) >= num_bytes + 1 */ 534 func zopfliCreateCommands(num_bytes uint, block_start uint, nodes []zopfliNode, dist_cache []int, last_insert_len *uint, params *encoderParams, commands *[]command, num_literals *uint) { 535 var max_backward_limit uint = maxBackwardLimit(params.lgwin) 536 var pos uint = 0 537 var offset uint32 = nodes[0].u.next 538 var i uint 539 var gap uint = 0 540 for i = 0; offset != math.MaxUint32; i++ { 541 var next *zopfliNode = &nodes[uint32(pos)+offset] 542 var copy_length uint = uint(zopfliNodeCopyLength(next)) 543 var insert_length uint = uint(next.dcode_insert_length & 0x7FFFFFF) 544 pos += insert_length 545 offset = next.u.next 546 if i == 0 { 547 insert_length += *last_insert_len 548 *last_insert_len = 0 549 } 550 { 551 var distance uint = uint(zopfliNodeCopyDistance(next)) 552 var len_code uint = uint(zopfliNodeLengthCode(next)) 553 var max_distance uint = brotli_min_size_t(block_start+pos, max_backward_limit) 554 var is_dictionary bool = (distance > max_distance+gap) 555 var dist_code uint = uint(zopfliNodeDistanceCode(next)) 556 *commands = append(*commands, makeCommand(¶ms.dist, insert_length, copy_length, int(len_code)-int(copy_length), dist_code)) 557 558 if !is_dictionary && dist_code > 0 { 559 dist_cache[3] = dist_cache[2] 560 dist_cache[2] = dist_cache[1] 561 dist_cache[1] = dist_cache[0] 562 dist_cache[0] = int(distance) 563 } 564 } 565 566 *num_literals += insert_length 567 pos += copy_length 568 } 569 570 *last_insert_len += num_bytes - pos 571 } 572 573 func zopfliIterate(num_bytes uint, position uint, ringbuffer []byte, ringbuffer_mask uint, params *encoderParams, gap uint, dist_cache []int, model *zopfliCostModel, num_matches []uint32, matches []backwardMatch, nodes []zopfliNode) uint { 574 var max_backward_limit uint = maxBackwardLimit(params.lgwin) 575 var max_zopfli_len uint = maxZopfliLen(params) 576 var queue startPosQueue 577 var cur_match_pos uint = 0 578 var i uint 579 nodes[0].length = 0 580 nodes[0].u.cost = 0 581 initStartPosQueue(&queue) 582 for i = 0; i+3 < num_bytes; i++ { 583 var skip uint = updateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask, params, max_backward_limit, dist_cache, uint(num_matches[i]), matches[cur_match_pos:], model, &queue, nodes) 584 if skip < longCopyQuickStep { 585 skip = 0 586 } 587 cur_match_pos += uint(num_matches[i]) 588 if num_matches[i] == 1 && backwardMatchLength(&matches[cur_match_pos-1]) > max_zopfli_len { 589 skip = brotli_max_size_t(backwardMatchLength(&matches[cur_match_pos-1]), skip) 590 } 591 592 if skip > 1 { 593 skip-- 594 for skip != 0 { 595 i++ 596 if i+3 >= num_bytes { 597 break 598 } 599 evaluateNode(position, i, max_backward_limit, gap, dist_cache, model, &queue, nodes) 600 cur_match_pos += uint(num_matches[i]) 601 skip-- 602 } 603 } 604 } 605 606 return computeShortestPathFromNodes(num_bytes, nodes) 607 } 608 609 /* Computes the shortest path of commands from position to at most 610 position + num_bytes. 611 612 On return, path->size() is the number of commands found and path[i] is the 613 length of the i-th command (copy length plus insert length). 614 Note that the sum of the lengths of all commands can be less than num_bytes. 615 616 On return, the nodes[0..num_bytes] array will have the following 617 "ZopfliNode array invariant": 618 For each i in [1..num_bytes], if nodes[i].cost < kInfinity, then 619 (1) nodes[i].copy_length() >= 2 620 (2) nodes[i].command_length() <= i and 621 (3) nodes[i - nodes[i].command_length()].cost < kInfinity 622 623 REQUIRES: nodes != nil and len(nodes) >= num_bytes + 1 */ 624 func zopfliComputeShortestPath(num_bytes uint, position uint, ringbuffer []byte, ringbuffer_mask uint, params *encoderParams, dist_cache []int, hasher *h10, nodes []zopfliNode) uint { 625 var max_backward_limit uint = maxBackwardLimit(params.lgwin) 626 var max_zopfli_len uint = maxZopfliLen(params) 627 var model zopfliCostModel 628 var queue startPosQueue 629 var matches [2 * (maxNumMatchesH10 + 64)]backwardMatch 630 var store_end uint 631 if num_bytes >= hasher.StoreLookahead() { 632 store_end = position + num_bytes - hasher.StoreLookahead() + 1 633 } else { 634 store_end = position 635 } 636 var i uint 637 var gap uint = 0 638 var lz_matches_offset uint = 0 639 nodes[0].length = 0 640 nodes[0].u.cost = 0 641 initZopfliCostModel(&model, ¶ms.dist, num_bytes) 642 zopfliCostModelSetFromLiteralCosts(&model, position, ringbuffer, ringbuffer_mask) 643 initStartPosQueue(&queue) 644 for i = 0; i+hasher.HashTypeLength()-1 < num_bytes; i++ { 645 var pos uint = position + i 646 var max_distance uint = brotli_min_size_t(pos, max_backward_limit) 647 var skip uint 648 var num_matches uint 649 num_matches = findAllMatchesH10(hasher, ¶ms.dictionary, ringbuffer, ringbuffer_mask, pos, num_bytes-i, max_distance, gap, params, matches[lz_matches_offset:]) 650 if num_matches > 0 && backwardMatchLength(&matches[num_matches-1]) > max_zopfli_len { 651 matches[0] = matches[num_matches-1] 652 num_matches = 1 653 } 654 655 skip = updateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask, params, max_backward_limit, dist_cache, num_matches, matches[:], &model, &queue, nodes) 656 if skip < longCopyQuickStep { 657 skip = 0 658 } 659 if num_matches == 1 && backwardMatchLength(&matches[0]) > max_zopfli_len { 660 skip = brotli_max_size_t(backwardMatchLength(&matches[0]), skip) 661 } 662 663 if skip > 1 { 664 /* Add the tail of the copy to the hasher. */ 665 hasher.StoreRange(ringbuffer, ringbuffer_mask, pos+1, brotli_min_size_t(pos+skip, store_end)) 666 667 skip-- 668 for skip != 0 { 669 i++ 670 if i+hasher.HashTypeLength()-1 >= num_bytes { 671 break 672 } 673 evaluateNode(position, i, max_backward_limit, gap, dist_cache, &model, &queue, nodes) 674 skip-- 675 } 676 } 677 } 678 679 cleanupZopfliCostModel(&model) 680 return computeShortestPathFromNodes(num_bytes, nodes) 681 } 682 683 func createZopfliBackwardReferences(num_bytes uint, position uint, ringbuffer []byte, ringbuffer_mask uint, params *encoderParams, hasher *h10, dist_cache []int, last_insert_len *uint, commands *[]command, num_literals *uint) { 684 var nodes []zopfliNode 685 nodes = make([]zopfliNode, (num_bytes + 1)) 686 initZopfliNodes(nodes, num_bytes+1) 687 zopfliComputeShortestPath(num_bytes, position, ringbuffer, ringbuffer_mask, params, dist_cache, hasher, nodes) 688 zopfliCreateCommands(num_bytes, position, nodes, dist_cache, last_insert_len, params, commands, num_literals) 689 nodes = nil 690 } 691 692 func createHqZopfliBackwardReferences(num_bytes uint, position uint, ringbuffer []byte, ringbuffer_mask uint, params *encoderParams, hasher hasherHandle, dist_cache []int, last_insert_len *uint, commands *[]command, num_literals *uint) { 693 var max_backward_limit uint = maxBackwardLimit(params.lgwin) 694 var num_matches []uint32 = make([]uint32, num_bytes) 695 var matches_size uint = 4 * num_bytes 696 var store_end uint 697 if num_bytes >= hasher.StoreLookahead() { 698 store_end = position + num_bytes - hasher.StoreLookahead() + 1 699 } else { 700 store_end = position 701 } 702 var cur_match_pos uint = 0 703 var i uint 704 var orig_num_literals uint 705 var orig_last_insert_len uint 706 var orig_dist_cache [4]int 707 var orig_num_commands int 708 var model zopfliCostModel 709 var nodes []zopfliNode 710 var matches []backwardMatch = make([]backwardMatch, matches_size) 711 var gap uint = 0 712 var shadow_matches uint = 0 713 var new_array []backwardMatch 714 for i = 0; i+hasher.HashTypeLength()-1 < num_bytes; i++ { 715 var pos uint = position + i 716 var max_distance uint = brotli_min_size_t(pos, max_backward_limit) 717 var max_length uint = num_bytes - i 718 var num_found_matches uint 719 var cur_match_end uint 720 var j uint 721 722 /* Ensure that we have enough free slots. */ 723 if matches_size < cur_match_pos+maxNumMatchesH10+shadow_matches { 724 var new_size uint = matches_size 725 if new_size == 0 { 726 new_size = cur_match_pos + maxNumMatchesH10 + shadow_matches 727 } 728 729 for new_size < cur_match_pos+maxNumMatchesH10+shadow_matches { 730 new_size *= 2 731 } 732 733 new_array = make([]backwardMatch, new_size) 734 if matches_size != 0 { 735 copy(new_array, matches[:matches_size]) 736 } 737 738 matches = new_array 739 matches_size = new_size 740 } 741 742 num_found_matches = findAllMatchesH10(hasher.(*h10), ¶ms.dictionary, ringbuffer, ringbuffer_mask, pos, max_length, max_distance, gap, params, matches[cur_match_pos+shadow_matches:]) 743 cur_match_end = cur_match_pos + num_found_matches 744 for j = cur_match_pos; j+1 < cur_match_end; j++ { 745 assert(backwardMatchLength(&matches[j]) <= backwardMatchLength(&matches[j+1])) 746 } 747 748 num_matches[i] = uint32(num_found_matches) 749 if num_found_matches > 0 { 750 var match_len uint = backwardMatchLength(&matches[cur_match_end-1]) 751 if match_len > maxZopfliLenQuality11 { 752 var skip uint = match_len - 1 753 matches[cur_match_pos] = matches[cur_match_end-1] 754 cur_match_pos++ 755 num_matches[i] = 1 756 757 /* Add the tail of the copy to the hasher. */ 758 hasher.StoreRange(ringbuffer, ringbuffer_mask, pos+1, brotli_min_size_t(pos+match_len, store_end)) 759 var pos uint = i 760 for i := 0; i < int(skip); i++ { 761 num_matches[pos+1:][i] = 0 762 } 763 i += skip 764 } else { 765 cur_match_pos = cur_match_end 766 } 767 } 768 } 769 770 orig_num_literals = *num_literals 771 orig_last_insert_len = *last_insert_len 772 copy(orig_dist_cache[:], dist_cache[:4]) 773 orig_num_commands = len(*commands) 774 nodes = make([]zopfliNode, (num_bytes + 1)) 775 initZopfliCostModel(&model, ¶ms.dist, num_bytes) 776 for i = 0; i < 2; i++ { 777 initZopfliNodes(nodes, num_bytes+1) 778 if i == 0 { 779 zopfliCostModelSetFromLiteralCosts(&model, position, ringbuffer, ringbuffer_mask) 780 } else { 781 zopfliCostModelSetFromCommands(&model, position, ringbuffer, ringbuffer_mask, (*commands)[orig_num_commands:], orig_last_insert_len) 782 } 783 784 *commands = (*commands)[:orig_num_commands] 785 *num_literals = orig_num_literals 786 *last_insert_len = orig_last_insert_len 787 copy(dist_cache, orig_dist_cache[:4]) 788 zopfliIterate(num_bytes, position, ringbuffer, ringbuffer_mask, params, gap, dist_cache, &model, num_matches, matches, nodes) 789 zopfliCreateCommands(num_bytes, position, nodes, dist_cache, last_insert_len, params, commands, num_literals) 790 } 791 792 cleanupZopfliCostModel(&model) 793 nodes = nil 794 matches = nil 795 num_matches = nil 796 }