| 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 |