| OLD | NEW |
| (Empty) | |
| 1 /* Copyright 2013 Google Inc. All Rights Reserved. |
| 2 |
| 3 Distributed under MIT license. |
| 4 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT |
| 5 */ |
| 6 |
| 7 // Function to find backward reference copies. |
| 8 |
| 9 #include "./backward_references.h" |
| 10 |
| 11 #include <algorithm> |
| 12 #include <limits> |
| 13 #include <vector> |
| 14 |
| 15 #include "./command.h" |
| 16 #include "./fast_log.h" |
| 17 #include "./literal_cost.h" |
| 18 |
| 19 namespace brotli { |
| 20 |
| 21 // The maximum length for which the zopflification uses distinct distances. |
| 22 static const uint16_t kMaxZopfliLen = 325; |
| 23 |
| 24 // Histogram based cost model for zopflification. |
| 25 class ZopfliCostModel { |
| 26 public: |
| 27 ZopfliCostModel(void) : min_cost_cmd_(kInfinity) {} |
| 28 |
| 29 void SetFromCommands(size_t num_bytes, |
| 30 size_t position, |
| 31 const uint8_t* ringbuffer, |
| 32 size_t ringbuffer_mask, |
| 33 const Command* commands, |
| 34 size_t num_commands, |
| 35 size_t last_insert_len) { |
| 36 std::vector<uint32_t> histogram_literal(256, 0); |
| 37 std::vector<uint32_t> histogram_cmd(kNumCommandPrefixes, 0); |
| 38 std::vector<uint32_t> histogram_dist(kNumDistancePrefixes, 0); |
| 39 |
| 40 size_t pos = position - last_insert_len; |
| 41 for (size_t i = 0; i < num_commands; i++) { |
| 42 size_t inslength = commands[i].insert_len_; |
| 43 size_t copylength = commands[i].copy_len(); |
| 44 size_t distcode = commands[i].dist_prefix_; |
| 45 size_t cmdcode = commands[i].cmd_prefix_; |
| 46 |
| 47 histogram_cmd[cmdcode]++; |
| 48 if (cmdcode >= 128) histogram_dist[distcode]++; |
| 49 |
| 50 for (size_t j = 0; j < inslength; j++) { |
| 51 histogram_literal[ringbuffer[(pos + j) & ringbuffer_mask]]++; |
| 52 } |
| 53 |
| 54 pos += inslength + copylength; |
| 55 } |
| 56 |
| 57 std::vector<float> cost_literal; |
| 58 Set(histogram_literal, &cost_literal); |
| 59 Set(histogram_cmd, &cost_cmd_); |
| 60 Set(histogram_dist, &cost_dist_); |
| 61 |
| 62 for (uint32_t i = 0; i < kNumCommandPrefixes; ++i) { |
| 63 min_cost_cmd_ = std::min(min_cost_cmd_, cost_cmd_[i]); |
| 64 } |
| 65 |
| 66 literal_costs_.resize(num_bytes + 1); |
| 67 literal_costs_[0] = 0.0; |
| 68 for (size_t i = 0; i < num_bytes; ++i) { |
| 69 literal_costs_[i + 1] = literal_costs_[i] + |
| 70 cost_literal[ringbuffer[(position + i) & ringbuffer_mask]]; |
| 71 } |
| 72 } |
| 73 |
| 74 void SetFromLiteralCosts(size_t num_bytes, |
| 75 size_t position, |
| 76 const uint8_t* ringbuffer, |
| 77 size_t ringbuffer_mask) { |
| 78 literal_costs_.resize(num_bytes + 2); |
| 79 EstimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask, |
| 80 ringbuffer, &literal_costs_[1]); |
| 81 literal_costs_[0] = 0.0; |
| 82 for (size_t i = 0; i < num_bytes; ++i) { |
| 83 literal_costs_[i + 1] += literal_costs_[i]; |
| 84 } |
| 85 cost_cmd_.resize(kNumCommandPrefixes); |
| 86 cost_dist_.resize(kNumDistancePrefixes); |
| 87 for (uint32_t i = 0; i < kNumCommandPrefixes; ++i) { |
| 88 cost_cmd_[i] = static_cast<float>(FastLog2(11 + i)); |
| 89 } |
| 90 for (uint32_t i = 0; i < kNumDistancePrefixes; ++i) { |
| 91 cost_dist_[i] = static_cast<float>(FastLog2(20 + i)); |
| 92 } |
| 93 min_cost_cmd_ = static_cast<float>(FastLog2(11)); |
| 94 } |
| 95 |
| 96 float GetCommandCost( |
| 97 size_t dist_code, size_t length_code, size_t insert_length) const { |
| 98 uint16_t inscode = GetInsertLengthCode(insert_length); |
| 99 uint16_t copycode = GetCopyLengthCode(length_code); |
| 100 uint16_t cmdcode = CombineLengthCodes(inscode, copycode, dist_code == 0); |
| 101 uint16_t dist_symbol; |
| 102 uint32_t distextra; |
| 103 PrefixEncodeCopyDistance(dist_code, 0, 0, &dist_symbol, &distextra); |
| 104 uint32_t distnumextra = distextra >> 24; |
| 105 |
| 106 float result = static_cast<float>( |
| 107 GetInsertExtra(inscode) + GetCopyExtra(copycode) + distnumextra); |
| 108 result += cost_cmd_[cmdcode]; |
| 109 if (cmdcode >= 128) result += cost_dist_[dist_symbol]; |
| 110 return result; |
| 111 } |
| 112 |
| 113 float GetLiteralCosts(size_t from, size_t to) const { |
| 114 return literal_costs_[to] - literal_costs_[from]; |
| 115 } |
| 116 |
| 117 float GetMinCostCmd(void) const { |
| 118 return min_cost_cmd_; |
| 119 } |
| 120 |
| 121 private: |
| 122 void Set(const std::vector<uint32_t>& histogram, std::vector<float>* cost) { |
| 123 cost->resize(histogram.size()); |
| 124 size_t sum = 0; |
| 125 for (size_t i = 0; i < histogram.size(); i++) { |
| 126 sum += histogram[i]; |
| 127 } |
| 128 float log2sum = static_cast<float>(FastLog2(sum)); |
| 129 for (size_t i = 0; i < histogram.size(); i++) { |
| 130 if (histogram[i] == 0) { |
| 131 (*cost)[i] = log2sum + 2; |
| 132 continue; |
| 133 } |
| 134 |
| 135 // Shannon bits for this symbol. |
| 136 (*cost)[i] = log2sum - static_cast<float>(FastLog2(histogram[i])); |
| 137 |
| 138 // Cannot be coded with less than 1 bit |
| 139 if ((*cost)[i] < 1) (*cost)[i] = 1; |
| 140 } |
| 141 } |
| 142 |
| 143 std::vector<float> cost_cmd_; // The insert and copy length symbols. |
| 144 std::vector<float> cost_dist_; |
| 145 // Cumulative costs of literals per position in the stream. |
| 146 std::vector<float> literal_costs_; |
| 147 float min_cost_cmd_; |
| 148 }; |
| 149 |
| 150 inline size_t ComputeDistanceCode(size_t distance, |
| 151 size_t max_distance, |
| 152 int quality, |
| 153 const int* dist_cache) { |
| 154 if (distance <= max_distance) { |
| 155 if (distance == static_cast<size_t>(dist_cache[0])) { |
| 156 return 0; |
| 157 } else if (distance == static_cast<size_t>(dist_cache[1])) { |
| 158 return 1; |
| 159 } else if (distance == static_cast<size_t>(dist_cache[2])) { |
| 160 return 2; |
| 161 } else if (distance == static_cast<size_t>(dist_cache[3])) { |
| 162 return 3; |
| 163 } else if (quality > 3 && distance >= 6) { |
| 164 for (size_t k = 4; k < kNumDistanceShortCodes; ++k) { |
| 165 size_t idx = kDistanceCacheIndex[k]; |
| 166 size_t candidate = |
| 167 static_cast<size_t>(dist_cache[idx] + kDistanceCacheOffset[k]); |
| 168 static const size_t kLimits[16] = { 0, 0, 0, 0, |
| 169 6, 6, 11, 11, |
| 170 11, 11, 11, 11, |
| 171 12, 12, 12, 12 }; |
| 172 if (distance == candidate && distance >= kLimits[k]) { |
| 173 return k; |
| 174 } |
| 175 } |
| 176 } |
| 177 } |
| 178 return distance + 15; |
| 179 } |
| 180 |
| 181 // REQUIRES: len >= 2, start_pos <= pos |
| 182 // REQUIRES: cost < kInfinity, nodes[start_pos].cost < kInfinity |
| 183 // Maintains the "ZopfliNode array invariant". |
| 184 inline void UpdateZopfliNode(ZopfliNode* nodes, size_t pos, size_t start_pos, |
| 185 size_t len, size_t len_code, size_t dist, |
| 186 size_t short_code, float cost) { |
| 187 ZopfliNode& next = nodes[pos + len]; |
| 188 next.length = static_cast<uint32_t>(len | ((len + 9u - len_code) << 24)); |
| 189 next.distance = static_cast<uint32_t>(dist | (short_code << 25)); |
| 190 next.insert_length = static_cast<uint32_t>(pos - start_pos); |
| 191 next.cost = cost; |
| 192 } |
| 193 |
| 194 // Maintains the smallest 2^k cost difference together with their positions |
| 195 class StartPosQueue { |
| 196 public: |
| 197 struct PosData { |
| 198 size_t pos; |
| 199 int distance_cache[4]; |
| 200 float costdiff; |
| 201 }; |
| 202 |
| 203 explicit StartPosQueue(int bits) |
| 204 : mask_((1u << bits) - 1), q_(1 << bits), idx_(0) {} |
| 205 |
| 206 void Clear(void) { |
| 207 idx_ = 0; |
| 208 } |
| 209 |
| 210 void Push(const StartPosQueue::PosData& posdata) { |
| 211 size_t offset = ~idx_ & mask_; |
| 212 ++idx_; |
| 213 size_t len = size(); |
| 214 q_[offset] = posdata; |
| 215 /* Restore the sorted order. In the list of |len| items at most |len - 1| |
| 216 adjacent element comparisons / swaps are required. */ |
| 217 for (size_t i = 1; i < len; ++i) { |
| 218 if (q_[offset & mask_].costdiff > q_[(offset + 1) & mask_].costdiff) { |
| 219 std::swap(q_[offset & mask_], q_[(offset + 1) & mask_]); |
| 220 } |
| 221 ++offset; |
| 222 } |
| 223 } |
| 224 |
| 225 size_t size(void) const { return std::min(idx_, mask_ + 1); } |
| 226 |
| 227 const StartPosQueue::PosData& GetStartPosData(size_t k) const { |
| 228 return q_[(k - idx_) & mask_]; |
| 229 } |
| 230 |
| 231 private: |
| 232 const size_t mask_; |
| 233 std::vector<PosData> q_; |
| 234 size_t idx_; |
| 235 }; |
| 236 |
| 237 // Returns the minimum possible copy length that can improve the cost of any |
| 238 // future position. |
| 239 static size_t ComputeMinimumCopyLength(const StartPosQueue& queue, |
| 240 const ZopfliNode* nodes, |
| 241 const ZopfliCostModel& model, |
| 242 const size_t num_bytes, |
| 243 const size_t pos) { |
| 244 // Compute the minimum possible cost of reaching any future position. |
| 245 const size_t start0 = queue.GetStartPosData(0).pos; |
| 246 float min_cost = (nodes[start0].cost + |
| 247 model.GetLiteralCosts(start0, pos) + |
| 248 model.GetMinCostCmd()); |
| 249 size_t len = 2; |
| 250 size_t next_len_bucket = 4; |
| 251 size_t next_len_offset = 10; |
| 252 while (pos + len <= num_bytes && nodes[pos + len].cost <= min_cost) { |
| 253 // We already reached (pos + len) with no more cost than the minimum |
| 254 // possible cost of reaching anything from this pos, so there is no point in |
| 255 // looking for lengths <= len. |
| 256 ++len; |
| 257 if (len == next_len_offset) { |
| 258 // We reached the next copy length code bucket, so we add one more |
| 259 // extra bit to the minimum cost. |
| 260 min_cost += static_cast<float>(1.0); |
| 261 next_len_offset += next_len_bucket; |
| 262 next_len_bucket *= 2; |
| 263 } |
| 264 } |
| 265 return len; |
| 266 } |
| 267 |
| 268 // Fills in dist_cache[0..3] with the last four distances (as defined by |
| 269 // Section 4. of the Spec) that would be used at (block_start + pos) if we |
| 270 // used the shortest path of commands from block_start, computed from |
| 271 // nodes[0..pos]. The last four distances at block_start are in |
| 272 // starting_dist_cach[0..3]. |
| 273 // REQUIRES: nodes[pos].cost < kInfinity |
| 274 // REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". |
| 275 static void ComputeDistanceCache(const size_t block_start, |
| 276 const size_t pos, |
| 277 const size_t max_backward, |
| 278 const int* starting_dist_cache, |
| 279 const ZopfliNode* nodes, |
| 280 int* dist_cache) { |
| 281 int idx = 0; |
| 282 size_t p = pos; |
| 283 // Because of prerequisite, does at most (pos + 1) / 2 iterations. |
| 284 while (idx < 4 && p > 0) { |
| 285 const size_t clen = nodes[p].copy_length(); |
| 286 const size_t ilen = nodes[p].insert_length; |
| 287 const size_t dist = nodes[p].copy_distance(); |
| 288 // Since block_start + p is the end position of the command, the copy part |
| 289 // starts from block_start + p - clen. Distances that are greater than this |
| 290 // or greater than max_backward are static dictionary references, and do |
| 291 // not update the last distances. Also distance code 0 (last distance) |
| 292 // does not update the last distances. |
| 293 if (dist + clen <= block_start + p && dist <= max_backward && |
| 294 nodes[p].distance_code() > 0) { |
| 295 dist_cache[idx++] = static_cast<int>(dist); |
| 296 } |
| 297 // Because of prerequisite, p >= clen + ilen >= 2. |
| 298 p -= clen + ilen; |
| 299 } |
| 300 for (; idx < 4; ++idx) { |
| 301 dist_cache[idx] = *starting_dist_cache++; |
| 302 } |
| 303 } |
| 304 |
| 305 static void UpdateNodes(const size_t num_bytes, |
| 306 const size_t block_start, |
| 307 const size_t pos, |
| 308 const uint8_t* ringbuffer, |
| 309 const size_t ringbuffer_mask, |
| 310 const size_t max_backward_limit, |
| 311 const int* starting_dist_cache, |
| 312 const size_t num_matches, |
| 313 const BackwardMatch* matches, |
| 314 const ZopfliCostModel* model, |
| 315 StartPosQueue* queue, |
| 316 ZopfliNode* nodes) { |
| 317 size_t cur_ix = block_start + pos; |
| 318 size_t cur_ix_masked = cur_ix & ringbuffer_mask; |
| 319 size_t max_distance = std::min(cur_ix, max_backward_limit); |
| 320 |
| 321 if (nodes[pos].cost <= model->GetLiteralCosts(0, pos)) { |
| 322 StartPosQueue::PosData posdata; |
| 323 posdata.pos = pos; |
| 324 posdata.costdiff = nodes[pos].cost - model->GetLiteralCosts(0, pos); |
| 325 ComputeDistanceCache(block_start, pos, max_backward_limit, |
| 326 starting_dist_cache, nodes, posdata.distance_cache); |
| 327 queue->Push(posdata); |
| 328 } |
| 329 |
| 330 const size_t min_len = ComputeMinimumCopyLength( |
| 331 *queue, nodes, *model, num_bytes, pos); |
| 332 |
| 333 // Go over the command starting positions in order of increasing cost |
| 334 // difference. |
| 335 for (size_t k = 0; k < 5 && k < queue->size(); ++k) { |
| 336 const StartPosQueue::PosData& posdata = queue->GetStartPosData(k); |
| 337 const size_t start = posdata.pos; |
| 338 const float start_costdiff = posdata.costdiff; |
| 339 |
| 340 // Look for last distance matches using the distance cache from this |
| 341 // starting position. |
| 342 size_t best_len = min_len - 1; |
| 343 for (size_t j = 0; j < kNumDistanceShortCodes; ++j) { |
| 344 const size_t idx = kDistanceCacheIndex[j]; |
| 345 const size_t backward = static_cast<size_t>(posdata.distance_cache[idx] + |
| 346 kDistanceCacheOffset[j]); |
| 347 size_t prev_ix = cur_ix - backward; |
| 348 if (prev_ix >= cur_ix) { |
| 349 continue; |
| 350 } |
| 351 if (PREDICT_FALSE(backward > max_distance)) { |
| 352 continue; |
| 353 } |
| 354 prev_ix &= ringbuffer_mask; |
| 355 |
| 356 if (cur_ix_masked + best_len > ringbuffer_mask || |
| 357 prev_ix + best_len > ringbuffer_mask || |
| 358 ringbuffer[cur_ix_masked + best_len] != |
| 359 ringbuffer[prev_ix + best_len]) { |
| 360 continue; |
| 361 } |
| 362 const size_t len = |
| 363 FindMatchLengthWithLimit(&ringbuffer[prev_ix], |
| 364 &ringbuffer[cur_ix_masked], |
| 365 num_bytes - pos); |
| 366 for (size_t l = best_len + 1; l <= len; ++l) { |
| 367 const size_t inslen = pos - start; |
| 368 float cmd_cost = model->GetCommandCost(j, l, inslen); |
| 369 float cost = start_costdiff + cmd_cost + model->GetLiteralCosts(0, pos); |
| 370 if (cost < nodes[pos + l].cost) { |
| 371 UpdateZopfliNode(&nodes[0], pos, start, l, l, backward, j + 1, cost); |
| 372 } |
| 373 best_len = l; |
| 374 } |
| 375 } |
| 376 |
| 377 // At higher iterations look only for new last distance matches, since |
| 378 // looking only for new command start positions with the same distances |
| 379 // does not help much. |
| 380 if (k >= 2) continue; |
| 381 |
| 382 // Loop through all possible copy lengths at this position. |
| 383 size_t len = min_len; |
| 384 for (size_t j = 0; j < num_matches; ++j) { |
| 385 BackwardMatch match = matches[j]; |
| 386 size_t dist = match.distance; |
| 387 bool is_dictionary_match = dist > max_distance; |
| 388 // We already tried all possible last distance matches, so we can use |
| 389 // normal distance code here. |
| 390 size_t dist_code = dist + 15; |
| 391 // Try all copy lengths up until the maximum copy length corresponding |
| 392 // to this distance. If the distance refers to the static dictionary, or |
| 393 // the maximum length is long enough, try only one maximum length. |
| 394 size_t max_len = match.length(); |
| 395 if (len < max_len && (is_dictionary_match || max_len > kMaxZopfliLen)) { |
| 396 len = max_len; |
| 397 } |
| 398 for (; len <= max_len; ++len) { |
| 399 size_t len_code = is_dictionary_match ? match.length_code() : len; |
| 400 const size_t inslen = pos - start; |
| 401 float cmd_cost = model->GetCommandCost(dist_code, len_code, inslen); |
| 402 float cost = start_costdiff + cmd_cost + model->GetLiteralCosts(0, pos); |
| 403 if (cost < nodes[pos + len].cost) { |
| 404 UpdateZopfliNode(&nodes[0], pos, start, len, len_code, dist, 0, cost); |
| 405 } |
| 406 } |
| 407 } |
| 408 } |
| 409 } |
| 410 |
| 411 static void ComputeShortestPathFromNodes(size_t num_bytes, |
| 412 const ZopfliNode* nodes, |
| 413 std::vector<uint32_t>* path) { |
| 414 std::vector<uint32_t> backwards(num_bytes / 2 + 1); |
| 415 size_t index = num_bytes; |
| 416 while (nodes[index].cost == kInfinity) --index; |
| 417 size_t num_commands = 0; |
| 418 while (index != 0) { |
| 419 size_t len = nodes[index].command_length(); |
| 420 backwards[num_commands++] = static_cast<uint32_t>(len); |
| 421 index -= len; |
| 422 } |
| 423 path->resize(num_commands); |
| 424 for (size_t i = num_commands, j = 0; i > 0; --i, ++j) { |
| 425 (*path)[j] = backwards[i - 1]; |
| 426 } |
| 427 } |
| 428 |
| 429 void ZopfliCreateCommands(const size_t num_bytes, |
| 430 const size_t block_start, |
| 431 const size_t max_backward_limit, |
| 432 const std::vector<uint32_t>& path, |
| 433 const ZopfliNode* nodes, |
| 434 int* dist_cache, |
| 435 size_t* last_insert_len, |
| 436 Command* commands, |
| 437 size_t* num_literals) { |
| 438 size_t pos = 0; |
| 439 for (size_t i = 0; i < path.size(); i++) { |
| 440 const ZopfliNode& next = nodes[pos + path[i]]; |
| 441 size_t copy_length = next.copy_length(); |
| 442 size_t insert_length = next.insert_length; |
| 443 pos += insert_length; |
| 444 if (i == 0) { |
| 445 insert_length += *last_insert_len; |
| 446 *last_insert_len = 0; |
| 447 } |
| 448 size_t distance = next.copy_distance(); |
| 449 size_t len_code = next.length_code(); |
| 450 size_t max_distance = std::min(block_start + pos, max_backward_limit); |
| 451 bool is_dictionary = (distance > max_distance); |
| 452 size_t dist_code = next.distance_code(); |
| 453 |
| 454 Command cmd(insert_length, copy_length, len_code, dist_code); |
| 455 commands[i] = cmd; |
| 456 |
| 457 if (!is_dictionary && dist_code > 0) { |
| 458 dist_cache[3] = dist_cache[2]; |
| 459 dist_cache[2] = dist_cache[1]; |
| 460 dist_cache[1] = dist_cache[0]; |
| 461 dist_cache[0] = static_cast<int>(distance); |
| 462 } |
| 463 |
| 464 *num_literals += insert_length; |
| 465 pos += copy_length; |
| 466 } |
| 467 *last_insert_len += num_bytes - pos; |
| 468 } |
| 469 |
| 470 static void ZopfliIterate(size_t num_bytes, |
| 471 size_t position, |
| 472 const uint8_t* ringbuffer, |
| 473 size_t ringbuffer_mask, |
| 474 const size_t max_backward_limit, |
| 475 const int* dist_cache, |
| 476 const ZopfliCostModel& model, |
| 477 const std::vector<uint32_t>& num_matches, |
| 478 const std::vector<BackwardMatch>& matches, |
| 479 ZopfliNode* nodes, |
| 480 std::vector<uint32_t>* path) { |
| 481 nodes[0].length = 0; |
| 482 nodes[0].cost = 0; |
| 483 StartPosQueue queue(3); |
| 484 size_t cur_match_pos = 0; |
| 485 for (size_t i = 0; i + 3 < num_bytes; i++) { |
| 486 UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask, |
| 487 max_backward_limit, dist_cache, num_matches[i], |
| 488 &matches[cur_match_pos], &model, &queue, &nodes[0]); |
| 489 cur_match_pos += num_matches[i]; |
| 490 // The zopflification can be too slow in case of very long lengths, so in |
| 491 // such case skip it all, it does not cost a lot of compression ratio. |
| 492 if (num_matches[i] == 1 && |
| 493 matches[cur_match_pos - 1].length() > kMaxZopfliLen) { |
| 494 i += matches[cur_match_pos - 1].length() - 1; |
| 495 queue.Clear(); |
| 496 } |
| 497 } |
| 498 ComputeShortestPathFromNodes(num_bytes, &nodes[0], path); |
| 499 } |
| 500 |
| 501 |
| 502 void ZopfliComputeShortestPath(size_t num_bytes, |
| 503 size_t position, |
| 504 const uint8_t* ringbuffer, |
| 505 size_t ringbuffer_mask, |
| 506 const size_t max_backward_limit, |
| 507 const int* dist_cache, |
| 508 Hashers::H10* hasher, |
| 509 ZopfliNode* nodes, |
| 510 std::vector<uint32_t>* path) { |
| 511 nodes[0].length = 0; |
| 512 nodes[0].cost = 0; |
| 513 ZopfliCostModel* model = new ZopfliCostModel; |
| 514 model->SetFromLiteralCosts(num_bytes, position, |
| 515 ringbuffer, ringbuffer_mask); |
| 516 StartPosQueue queue(3); |
| 517 BackwardMatch matches[Hashers::H10::kMaxNumMatches]; |
| 518 for (size_t i = 0; i + 3 < num_bytes; i++) { |
| 519 const size_t max_distance = std::min(position + i, max_backward_limit); |
| 520 size_t num_matches = hasher->FindAllMatches( |
| 521 ringbuffer, ringbuffer_mask, position + i, num_bytes - i, max_distance, |
| 522 matches); |
| 523 if (num_matches > 0 && |
| 524 matches[num_matches - 1].length() > kMaxZopfliLen) { |
| 525 matches[0] = matches[num_matches - 1]; |
| 526 num_matches = 1; |
| 527 } |
| 528 UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask, |
| 529 max_backward_limit, dist_cache, num_matches, matches, |
| 530 model, &queue, nodes); |
| 531 if (num_matches == 1 && matches[0].length() > kMaxZopfliLen) { |
| 532 for (size_t j = 1; j < matches[0].length() && i + 4 < num_bytes; ++j) { |
| 533 ++i; |
| 534 if (matches[0].length() - j < 64 && |
| 535 num_bytes - i >= kMaxTreeCompLength) { |
| 536 hasher->Store(ringbuffer, ringbuffer_mask, position + i); |
| 537 } |
| 538 } |
| 539 queue.Clear(); |
| 540 } |
| 541 } |
| 542 delete model; |
| 543 ComputeShortestPathFromNodes(num_bytes, nodes, path); |
| 544 } |
| 545 |
| 546 template<typename Hasher> |
| 547 void CreateBackwardReferences(size_t num_bytes, |
| 548 size_t position, |
| 549 bool is_last, |
| 550 const uint8_t* ringbuffer, |
| 551 size_t ringbuffer_mask, |
| 552 const int quality, |
| 553 const int lgwin, |
| 554 Hasher* hasher, |
| 555 int* dist_cache, |
| 556 size_t* last_insert_len, |
| 557 Command* commands, |
| 558 size_t* num_commands, |
| 559 size_t* num_literals) { |
| 560 // Set maximum distance, see section 9.1. of the spec. |
| 561 const size_t max_backward_limit = (1 << lgwin) - 16; |
| 562 |
| 563 // Choose which init method is faster. |
| 564 // memset is about 100 times faster than hasher->InitForData(). |
| 565 const size_t kMaxBytesForPartialHashInit = Hasher::kHashMapSize >> 7; |
| 566 if (position == 0 && is_last && num_bytes <= kMaxBytesForPartialHashInit) { |
| 567 hasher->InitForData(ringbuffer, num_bytes); |
| 568 } else { |
| 569 hasher->Init(); |
| 570 } |
| 571 if (num_bytes >= 3 && position >= 3) { |
| 572 // Prepare the hashes for three last bytes of the last write. |
| 573 // These could not be calculated before, since they require knowledge |
| 574 // of both the previous and the current block. |
| 575 hasher->Store(&ringbuffer[(position - 3) & ringbuffer_mask], |
| 576 static_cast<uint32_t>(position - 3)); |
| 577 hasher->Store(&ringbuffer[(position - 2) & ringbuffer_mask], |
| 578 static_cast<uint32_t>(position - 2)); |
| 579 hasher->Store(&ringbuffer[(position - 1) & ringbuffer_mask], |
| 580 static_cast<uint32_t>(position - 1)); |
| 581 } |
| 582 const Command * const orig_commands = commands; |
| 583 size_t insert_length = *last_insert_len; |
| 584 size_t i = position & ringbuffer_mask; |
| 585 const size_t i_diff = position - i; |
| 586 const size_t i_end = i + num_bytes; |
| 587 |
| 588 // For speed up heuristics for random data. |
| 589 const size_t random_heuristics_window_size = quality < 9 ? 64 : 512; |
| 590 size_t apply_random_heuristics = i + random_heuristics_window_size; |
| 591 |
| 592 // Minimum score to accept a backward reference. |
| 593 const double kMinScore = 4.0; |
| 594 |
| 595 while (i + Hasher::kHashTypeLength - 1 < i_end) { |
| 596 size_t max_length = i_end - i; |
| 597 size_t max_distance = std::min(i + i_diff, max_backward_limit); |
| 598 size_t best_len = 0; |
| 599 size_t best_len_code = 0; |
| 600 size_t best_dist = 0; |
| 601 double best_score = kMinScore; |
| 602 bool match_found = hasher->FindLongestMatch( |
| 603 ringbuffer, ringbuffer_mask, |
| 604 dist_cache, static_cast<uint32_t>(i + i_diff), max_length, max_distance, |
| 605 &best_len, &best_len_code, &best_dist, &best_score); |
| 606 if (match_found) { |
| 607 // Found a match. Let's look for something even better ahead. |
| 608 int delayed_backward_references_in_row = 0; |
| 609 for (;;) { |
| 610 --max_length; |
| 611 size_t best_len_2 = |
| 612 quality < 5 ? std::min(best_len - 1, max_length) : 0; |
| 613 size_t best_len_code_2 = 0; |
| 614 size_t best_dist_2 = 0; |
| 615 double best_score_2 = kMinScore; |
| 616 max_distance = std::min(i + i_diff + 1, max_backward_limit); |
| 617 match_found = hasher->FindLongestMatch( |
| 618 ringbuffer, ringbuffer_mask, |
| 619 dist_cache, static_cast<uint32_t>(i + i_diff + 1), |
| 620 max_length, max_distance, |
| 621 &best_len_2, &best_len_code_2, &best_dist_2, &best_score_2); |
| 622 double cost_diff_lazy = 7.0; |
| 623 if (match_found && best_score_2 >= best_score + cost_diff_lazy) { |
| 624 // Ok, let's just write one byte for now and start a match from the |
| 625 // next byte. |
| 626 ++i; |
| 627 ++insert_length; |
| 628 best_len = best_len_2; |
| 629 best_len_code = best_len_code_2; |
| 630 best_dist = best_dist_2; |
| 631 best_score = best_score_2; |
| 632 if (++delayed_backward_references_in_row < 4) { |
| 633 continue; |
| 634 } |
| 635 } |
| 636 break; |
| 637 } |
| 638 apply_random_heuristics = |
| 639 i + 2 * best_len + random_heuristics_window_size; |
| 640 max_distance = std::min(i + i_diff, max_backward_limit); |
| 641 // The first 16 codes are special shortcodes, and the minimum offset is 1. |
| 642 size_t distance_code = |
| 643 ComputeDistanceCode(best_dist, max_distance, quality, dist_cache); |
| 644 if (best_dist <= max_distance && distance_code > 0) { |
| 645 dist_cache[3] = dist_cache[2]; |
| 646 dist_cache[2] = dist_cache[1]; |
| 647 dist_cache[1] = dist_cache[0]; |
| 648 dist_cache[0] = static_cast<int>(best_dist); |
| 649 } |
| 650 Command cmd(insert_length, best_len, best_len_code, distance_code); |
| 651 *commands++ = cmd; |
| 652 *num_literals += insert_length; |
| 653 insert_length = 0; |
| 654 // Put the hash keys into the table, if there are enough |
| 655 // bytes left. |
| 656 for (size_t j = 2; j < best_len; ++j) { |
| 657 hasher->Store(&ringbuffer[i + j], |
| 658 static_cast<uint32_t>(i + i_diff + j)); |
| 659 } |
| 660 i += best_len; |
| 661 } else { |
| 662 ++insert_length; |
| 663 ++i; |
| 664 // If we have not seen matches for a long time, we can skip some |
| 665 // match lookups. Unsuccessful match lookups are very very expensive |
| 666 // and this kind of a heuristic speeds up compression quite |
| 667 // a lot. |
| 668 if (i > apply_random_heuristics) { |
| 669 // Going through uncompressible data, jump. |
| 670 if (i > apply_random_heuristics + 4 * random_heuristics_window_size) { |
| 671 // It is quite a long time since we saw a copy, so we assume |
| 672 // that this data is not compressible, and store hashes less |
| 673 // often. Hashes of non compressible data are less likely to |
| 674 // turn out to be useful in the future, too, so we store less of |
| 675 // them to not to flood out the hash table of good compressible |
| 676 // data. |
| 677 size_t i_jump = std::min(i + 16, i_end - 4); |
| 678 for (; i < i_jump; i += 4) { |
| 679 hasher->Store(ringbuffer + i, static_cast<uint32_t>(i + i_diff)); |
| 680 insert_length += 4; |
| 681 } |
| 682 } else { |
| 683 size_t i_jump = std::min(i + 8, i_end - 3); |
| 684 for (; i < i_jump; i += 2) { |
| 685 hasher->Store(ringbuffer + i, static_cast<uint32_t>(i + i_diff)); |
| 686 insert_length += 2; |
| 687 } |
| 688 } |
| 689 } |
| 690 } |
| 691 } |
| 692 insert_length += i_end - i; |
| 693 *last_insert_len = insert_length; |
| 694 *num_commands += static_cast<size_t>(commands - orig_commands); |
| 695 } |
| 696 |
| 697 void CreateBackwardReferences(size_t num_bytes, |
| 698 size_t position, |
| 699 bool is_last, |
| 700 const uint8_t* ringbuffer, |
| 701 size_t ringbuffer_mask, |
| 702 const int quality, |
| 703 const int lgwin, |
| 704 Hashers* hashers, |
| 705 int hash_type, |
| 706 int* dist_cache, |
| 707 size_t* last_insert_len, |
| 708 Command* commands, |
| 709 size_t* num_commands, |
| 710 size_t* num_literals) { |
| 711 bool zopflify = quality > 9; |
| 712 if (zopflify) { |
| 713 Hashers::H10* hasher = hashers->hash_h10; |
| 714 hasher->Init(lgwin, position, num_bytes, is_last); |
| 715 hasher->StitchToPreviousBlock(num_bytes, position, |
| 716 ringbuffer, ringbuffer_mask); |
| 717 // Set maximum distance, see section 9.1. of the spec. |
| 718 const size_t max_backward_limit = (1 << lgwin) - 16; |
| 719 if (quality == 10) { |
| 720 std::vector<ZopfliNode> nodes(num_bytes + 1); |
| 721 std::vector<uint32_t> path; |
| 722 ZopfliComputeShortestPath(num_bytes, position, |
| 723 ringbuffer, ringbuffer_mask, |
| 724 max_backward_limit, dist_cache, hasher, |
| 725 &nodes[0], &path); |
| 726 ZopfliCreateCommands(num_bytes, position, max_backward_limit, path, |
| 727 &nodes[0], dist_cache, last_insert_len, commands, |
| 728 num_literals); |
| 729 *num_commands += path.size(); |
| 730 return; |
| 731 } |
| 732 std::vector<uint32_t> num_matches(num_bytes); |
| 733 std::vector<BackwardMatch> matches(4 * num_bytes); |
| 734 size_t cur_match_pos = 0; |
| 735 for (size_t i = 0; i + 3 < num_bytes; ++i) { |
| 736 size_t max_distance = std::min(position + i, max_backward_limit); |
| 737 size_t max_length = num_bytes - i; |
| 738 // Ensure that we have enough free slots. |
| 739 if (matches.size() < cur_match_pos + Hashers::H10::kMaxNumMatches) { |
| 740 matches.resize(cur_match_pos + Hashers::H10::kMaxNumMatches); |
| 741 } |
| 742 size_t num_found_matches = hasher->FindAllMatches( |
| 743 ringbuffer, ringbuffer_mask, position + i, max_length, max_distance, |
| 744 &matches[cur_match_pos]); |
| 745 const size_t cur_match_end = cur_match_pos + num_found_matches; |
| 746 for (size_t j = cur_match_pos; j + 1 < cur_match_end; ++j) { |
| 747 assert(matches[j].length() < matches[j + 1].length()); |
| 748 assert(matches[j].distance > max_distance || |
| 749 matches[j].distance <= matches[j + 1].distance); |
| 750 } |
| 751 num_matches[i] = static_cast<uint32_t>(num_found_matches); |
| 752 if (num_found_matches > 0) { |
| 753 const size_t match_len = matches[cur_match_end - 1].length(); |
| 754 if (match_len > kMaxZopfliLen) { |
| 755 matches[cur_match_pos++] = matches[cur_match_end - 1]; |
| 756 num_matches[i] = 1; |
| 757 for (size_t j = 1; j < match_len; ++j) { |
| 758 ++i; |
| 759 if (match_len - j < 64 && num_bytes - i >= kMaxTreeCompLength) { |
| 760 hasher->Store(ringbuffer, ringbuffer_mask, position + i); |
| 761 } |
| 762 num_matches[i] = 0; |
| 763 } |
| 764 } else { |
| 765 cur_match_pos = cur_match_end; |
| 766 } |
| 767 } |
| 768 } |
| 769 size_t orig_num_literals = *num_literals; |
| 770 size_t orig_last_insert_len = *last_insert_len; |
| 771 int orig_dist_cache[4] = { |
| 772 dist_cache[0], dist_cache[1], dist_cache[2], dist_cache[3] |
| 773 }; |
| 774 size_t orig_num_commands = *num_commands; |
| 775 static const size_t kIterations = 2; |
| 776 for (size_t i = 0; i < kIterations; i++) { |
| 777 ZopfliCostModel model; |
| 778 if (i == 0) { |
| 779 model.SetFromLiteralCosts(num_bytes, position, |
| 780 ringbuffer, ringbuffer_mask); |
| 781 } else { |
| 782 model.SetFromCommands(num_bytes, position, |
| 783 ringbuffer, ringbuffer_mask, |
| 784 commands, *num_commands - orig_num_commands, |
| 785 orig_last_insert_len); |
| 786 } |
| 787 *num_commands = orig_num_commands; |
| 788 *num_literals = orig_num_literals; |
| 789 *last_insert_len = orig_last_insert_len; |
| 790 memcpy(dist_cache, orig_dist_cache, 4 * sizeof(dist_cache[0])); |
| 791 std::vector<ZopfliNode> nodes(num_bytes + 1); |
| 792 std::vector<uint32_t> path; |
| 793 ZopfliIterate(num_bytes, position, ringbuffer, ringbuffer_mask, |
| 794 max_backward_limit, dist_cache, model, num_matches, matches, |
| 795 &nodes[0], &path); |
| 796 ZopfliCreateCommands(num_bytes, position, max_backward_limit, path, |
| 797 &nodes[0], dist_cache, last_insert_len, commands, |
| 798 num_literals); |
| 799 *num_commands += path.size(); |
| 800 } |
| 801 return; |
| 802 } |
| 803 |
| 804 switch (hash_type) { |
| 805 case 2: |
| 806 CreateBackwardReferences<Hashers::H2>( |
| 807 num_bytes, position, is_last, ringbuffer, ringbuffer_mask, |
| 808 quality, lgwin, hashers->hash_h2, dist_cache, |
| 809 last_insert_len, commands, num_commands, num_literals); |
| 810 break; |
| 811 case 3: |
| 812 CreateBackwardReferences<Hashers::H3>( |
| 813 num_bytes, position, is_last, ringbuffer, ringbuffer_mask, |
| 814 quality, lgwin, hashers->hash_h3, dist_cache, |
| 815 last_insert_len, commands, num_commands, num_literals); |
| 816 break; |
| 817 case 4: |
| 818 CreateBackwardReferences<Hashers::H4>( |
| 819 num_bytes, position, is_last, ringbuffer, ringbuffer_mask, |
| 820 quality, lgwin, hashers->hash_h4, dist_cache, |
| 821 last_insert_len, commands, num_commands, num_literals); |
| 822 break; |
| 823 case 5: |
| 824 CreateBackwardReferences<Hashers::H5>( |
| 825 num_bytes, position, is_last, ringbuffer, ringbuffer_mask, |
| 826 quality, lgwin, hashers->hash_h5, dist_cache, |
| 827 last_insert_len, commands, num_commands, num_literals); |
| 828 break; |
| 829 case 6: |
| 830 CreateBackwardReferences<Hashers::H6>( |
| 831 num_bytes, position, is_last, ringbuffer, ringbuffer_mask, |
| 832 quality, lgwin, hashers->hash_h6, dist_cache, |
| 833 last_insert_len, commands, num_commands, num_literals); |
| 834 break; |
| 835 case 7: |
| 836 CreateBackwardReferences<Hashers::H7>( |
| 837 num_bytes, position, is_last, ringbuffer, ringbuffer_mask, |
| 838 quality, lgwin, hashers->hash_h7, dist_cache, |
| 839 last_insert_len, commands, num_commands, num_literals); |
| 840 break; |
| 841 case 8: |
| 842 CreateBackwardReferences<Hashers::H8>( |
| 843 num_bytes, position, is_last, ringbuffer, ringbuffer_mask, |
| 844 quality, lgwin, hashers->hash_h8, dist_cache, |
| 845 last_insert_len, commands, num_commands, num_literals); |
| 846 break; |
| 847 case 9: |
| 848 CreateBackwardReferences<Hashers::H9>( |
| 849 num_bytes, position, is_last, ringbuffer, ringbuffer_mask, |
| 850 quality, lgwin, hashers->hash_h9, dist_cache, |
| 851 last_insert_len, commands, num_commands, num_literals); |
| 852 break; |
| 853 default: |
| 854 break; |
| 855 } |
| 856 } |
| 857 |
| 858 } // namespace brotli |
| OLD | NEW |