]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/C/BrotliCompress/enc/backward_references.c
BaseTools: Copy Brotli algorithm 3rd party source code for tool
[mirror_edk2.git] / BaseTools / Source / C / BrotliCompress / enc / backward_references.c
CommitLineData
11b7501a
SB
1/* Copyright 2013 Google Inc. All Rights Reserved.\r
2\r
3 Distributed under MIT license.\r
4 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT\r
5*/\r
6\r
7/* Function to find backward reference copies. */\r
8\r
9#include "./backward_references.h"\r
10\r
11#include <math.h> /* INFINITY */\r
12#include <string.h> /* memcpy, memset */\r
13\r
14#include "../common/constants.h"\r
15#include "../common/types.h"\r
16#include "./command.h"\r
17#include "./fast_log.h"\r
18#include "./find_match_length.h"\r
19#include "./literal_cost.h"\r
20#include "./memory.h"\r
21#include "./port.h"\r
22#include "./prefix.h"\r
23#include "./quality.h"\r
24\r
25#if defined(__cplusplus) || defined(c_plusplus)\r
26extern "C" {\r
27#endif\r
28\r
29#ifdef INFINITY\r
30static const float kInfinity = INFINITY;\r
31#else\r
32static const float kInfinity = 3.4028e38f;\r
33#endif\r
34\r
35void BrotliInitZopfliNodes(ZopfliNode* array, size_t length) {\r
36 ZopfliNode stub;\r
37 size_t i;\r
38 stub.length = 1;\r
39 stub.distance = 0;\r
40 stub.insert_length = 0;\r
41 stub.u.cost = kInfinity;\r
42 for (i = 0; i < length; ++i) array[i] = stub;\r
43}\r
44\r
45static BROTLI_INLINE uint32_t ZopfliNodeCopyLength(const ZopfliNode* self) {\r
46 return self->length & 0xffffff;\r
47}\r
48\r
49static BROTLI_INLINE uint32_t ZopfliNodeLengthCode(const ZopfliNode* self) {\r
50 const uint32_t modifier = self->length >> 24;\r
51 return ZopfliNodeCopyLength(self) + 9u - modifier;\r
52}\r
53\r
54static BROTLI_INLINE uint32_t ZopfliNodeCopyDistance(const ZopfliNode* self) {\r
55 return self->distance & 0x1ffffff;\r
56}\r
57\r
58static BROTLI_INLINE uint32_t ZopfliNodeDistanceCode(const ZopfliNode* self) {\r
59 const uint32_t short_code = self->distance >> 25;\r
60 return short_code == 0 ? ZopfliNodeCopyDistance(self) + 15 : short_code - 1;\r
61}\r
62\r
63static BROTLI_INLINE uint32_t ZopfliNodeCommandLength(const ZopfliNode* self) {\r
64 return ZopfliNodeCopyLength(self) + self->insert_length;\r
65}\r
66\r
67/* Histogram based cost model for zopflification. */\r
68typedef struct ZopfliCostModel {\r
69 /* The insert and copy length symbols. */\r
70 float cost_cmd_[BROTLI_NUM_COMMAND_SYMBOLS];\r
71 float cost_dist_[BROTLI_NUM_DISTANCE_SYMBOLS];\r
72 /* Cumulative costs of literals per position in the stream. */\r
73 float* literal_costs_;\r
74 float min_cost_cmd_;\r
75 size_t num_bytes_;\r
76} ZopfliCostModel;\r
77\r
78static void InitZopfliCostModel(\r
79 MemoryManager* m, ZopfliCostModel* self, size_t num_bytes) {\r
80 self->num_bytes_ = num_bytes;\r
81 self->literal_costs_ = BROTLI_ALLOC(m, float, num_bytes + 2);\r
82 if (BROTLI_IS_OOM(m)) return;\r
83}\r
84\r
85static void CleanupZopfliCostModel(MemoryManager* m, ZopfliCostModel* self) {\r
86 BROTLI_FREE(m, self->literal_costs_);\r
87}\r
88\r
89static void SetCost(const uint32_t* histogram, size_t histogram_size,\r
90 float* cost) {\r
91 size_t sum = 0;\r
92 float log2sum;\r
93 size_t i;\r
94 for (i = 0; i < histogram_size; i++) {\r
95 sum += histogram[i];\r
96 }\r
97 log2sum = (float)FastLog2(sum);\r
98 for (i = 0; i < histogram_size; i++) {\r
99 if (histogram[i] == 0) {\r
100 cost[i] = log2sum + 2;\r
101 continue;\r
102 }\r
103\r
104 /* Shannon bits for this symbol. */\r
105 cost[i] = log2sum - (float)FastLog2(histogram[i]);\r
106\r
107 /* Cannot be coded with less than 1 bit */\r
108 if (cost[i] < 1) cost[i] = 1;\r
109 }\r
110}\r
111\r
112static void ZopfliCostModelSetFromCommands(ZopfliCostModel* self,\r
113 size_t position,\r
114 const uint8_t* ringbuffer,\r
115 size_t ringbuffer_mask,\r
116 const Command* commands,\r
117 size_t num_commands,\r
118 size_t last_insert_len) {\r
119 uint32_t histogram_literal[BROTLI_NUM_LITERAL_SYMBOLS];\r
120 uint32_t histogram_cmd[BROTLI_NUM_COMMAND_SYMBOLS];\r
121 uint32_t histogram_dist[BROTLI_NUM_DISTANCE_SYMBOLS];\r
122 float cost_literal[BROTLI_NUM_LITERAL_SYMBOLS];\r
123 size_t pos = position - last_insert_len;\r
124 float min_cost_cmd = kInfinity;\r
125 size_t i;\r
126 float* cost_cmd = self->cost_cmd_;\r
127\r
128 memset(histogram_literal, 0, sizeof(histogram_literal));\r
129 memset(histogram_cmd, 0, sizeof(histogram_cmd));\r
130 memset(histogram_dist, 0, sizeof(histogram_dist));\r
131\r
132 for (i = 0; i < num_commands; i++) {\r
133 size_t inslength = commands[i].insert_len_;\r
134 size_t copylength = CommandCopyLen(&commands[i]);\r
135 size_t distcode = commands[i].dist_prefix_;\r
136 size_t cmdcode = commands[i].cmd_prefix_;\r
137 size_t j;\r
138\r
139 histogram_cmd[cmdcode]++;\r
140 if (cmdcode >= 128) histogram_dist[distcode]++;\r
141\r
142 for (j = 0; j < inslength; j++) {\r
143 histogram_literal[ringbuffer[(pos + j) & ringbuffer_mask]]++;\r
144 }\r
145\r
146 pos += inslength + copylength;\r
147 }\r
148\r
149 SetCost(histogram_literal, BROTLI_NUM_LITERAL_SYMBOLS, cost_literal);\r
150 SetCost(histogram_cmd, BROTLI_NUM_COMMAND_SYMBOLS, cost_cmd);\r
151 SetCost(histogram_dist, BROTLI_NUM_DISTANCE_SYMBOLS, self->cost_dist_);\r
152\r
153 for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) {\r
154 min_cost_cmd = BROTLI_MIN(float, min_cost_cmd, cost_cmd[i]);\r
155 }\r
156 self->min_cost_cmd_ = min_cost_cmd;\r
157\r
158 {\r
159 float* literal_costs = self->literal_costs_;\r
160 size_t num_bytes = self->num_bytes_;\r
161 literal_costs[0] = 0.0;\r
162 for (i = 0; i < num_bytes; ++i) {\r
163 literal_costs[i + 1] = literal_costs[i] +\r
164 cost_literal[ringbuffer[(position + i) & ringbuffer_mask]];\r
165 }\r
166 }\r
167}\r
168\r
169static void ZopfliCostModelSetFromLiteralCosts(ZopfliCostModel* self,\r
170 size_t position,\r
171 const uint8_t* ringbuffer,\r
172 size_t ringbuffer_mask) {\r
173 float* literal_costs = self->literal_costs_;\r
174 float* cost_dist = self->cost_dist_;\r
175 float* cost_cmd = self->cost_cmd_;\r
176 size_t num_bytes = self->num_bytes_;\r
177 size_t i;\r
178 BrotliEstimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask,\r
179 ringbuffer, &literal_costs[1]);\r
180 literal_costs[0] = 0.0;\r
181 for (i = 0; i < num_bytes; ++i) {\r
182 literal_costs[i + 1] += literal_costs[i];\r
183 }\r
184 for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) {\r
185 cost_cmd[i] = (float)FastLog2(11 + (uint32_t)i);\r
186 }\r
187 for (i = 0; i < BROTLI_NUM_DISTANCE_SYMBOLS; ++i) {\r
188 cost_dist[i] = (float)FastLog2(20 + (uint32_t)i);\r
189 }\r
190 self->min_cost_cmd_ = (float)FastLog2(11);\r
191}\r
192\r
193static BROTLI_INLINE float ZopfliCostModelGetCommandCost(\r
194 const ZopfliCostModel* self, uint16_t cmdcode) {\r
195 return self->cost_cmd_[cmdcode];\r
196}\r
197\r
198static BROTLI_INLINE float ZopfliCostModelGetDistanceCost(\r
199 const ZopfliCostModel* self, size_t distcode) {\r
200 return self->cost_dist_[distcode];\r
201}\r
202\r
203static BROTLI_INLINE float ZopfliCostModelGetLiteralCosts(\r
204 const ZopfliCostModel* self, size_t from, size_t to) {\r
205 return self->literal_costs_[to] - self->literal_costs_[from];\r
206}\r
207\r
208static BROTLI_INLINE float ZopfliCostModelGetMinCostCmd(\r
209 const ZopfliCostModel* self) {\r
210 return self->min_cost_cmd_;\r
211}\r
212\r
213static BROTLI_INLINE size_t ComputeDistanceCode(size_t distance,\r
214 size_t max_distance,\r
215 const int* dist_cache) {\r
216 if (distance <= max_distance) {\r
217 size_t distance_plus_3 = distance + 3;\r
218 size_t offset0 = distance_plus_3 - (size_t)dist_cache[0];\r
219 size_t offset1 = distance_plus_3 - (size_t)dist_cache[1];\r
220 if (distance == (size_t)dist_cache[0]) {\r
221 return 0;\r
222 } else if (distance == (size_t)dist_cache[1]) {\r
223 return 1;\r
224 } else if (offset0 < 7) {\r
225 return (0x9750468 >> (4 * offset0)) & 0xF;\r
226 } else if (offset1 < 7) {\r
227 return (0xFDB1ACE >> (4 * offset1)) & 0xF;\r
228 } else if (distance == (size_t)dist_cache[2]) {\r
229 return 2;\r
230 } else if (distance == (size_t)dist_cache[3]) {\r
231 return 3;\r
232 }\r
233 }\r
234 return distance + 15;\r
235}\r
236\r
237/* REQUIRES: len >= 2, start_pos <= pos */\r
238/* REQUIRES: cost < kInfinity, nodes[start_pos].cost < kInfinity */\r
239/* Maintains the "ZopfliNode array invariant". */\r
240static BROTLI_INLINE void UpdateZopfliNode(ZopfliNode* nodes, size_t pos,\r
241 size_t start_pos, size_t len, size_t len_code, size_t dist,\r
242 size_t short_code, float cost) {\r
243 ZopfliNode* next = &nodes[pos + len];\r
244 next->length = (uint32_t)(len | ((len + 9u - len_code) << 24));\r
245 next->distance = (uint32_t)(dist | (short_code << 25));\r
246 next->insert_length = (uint32_t)(pos - start_pos);\r
247 next->u.cost = cost;\r
248}\r
249\r
250typedef struct PosData {\r
251 size_t pos;\r
252 int distance_cache[4];\r
253 float costdiff;\r
254 float cost;\r
255} PosData;\r
256\r
257/* Maintains the smallest 8 cost difference together with their positions */\r
258typedef struct StartPosQueue {\r
259 PosData q_[8];\r
260 size_t idx_;\r
261} StartPosQueue;\r
262\r
263static BROTLI_INLINE void InitStartPosQueue(StartPosQueue* self) {\r
264 self->idx_ = 0;\r
265}\r
266\r
267static size_t StartPosQueueSize(const StartPosQueue* self) {\r
268 return BROTLI_MIN(size_t, self->idx_, 8);\r
269}\r
270\r
271static void StartPosQueuePush(StartPosQueue* self, const PosData* posdata) {\r
272 size_t offset = ~(self->idx_++) & 7;\r
273 size_t len = StartPosQueueSize(self);\r
274 size_t i;\r
275 PosData* q = self->q_;\r
276 q[offset] = *posdata;\r
277 /* Restore the sorted order. In the list of |len| items at most |len - 1|\r
278 adjacent element comparisons / swaps are required. */\r
279 for (i = 1; i < len; ++i) {\r
280 if (q[offset & 7].costdiff > q[(offset + 1) & 7].costdiff) {\r
281 BROTLI_SWAP(PosData, q, offset & 7, (offset + 1) & 7);\r
282 }\r
283 ++offset;\r
284 }\r
285}\r
286\r
287static const PosData* StartPosQueueAt(const StartPosQueue* self, size_t k) {\r
288 return &self->q_[(k - self->idx_) & 7];\r
289}\r
290\r
291/* Returns the minimum possible copy length that can improve the cost of any */\r
292/* future position. */\r
293static size_t ComputeMinimumCopyLength(const float start_cost,\r
294 const ZopfliNode* nodes,\r
295 const size_t num_bytes,\r
296 const size_t pos) {\r
297 /* Compute the minimum possible cost of reaching any future position. */\r
298 float min_cost = start_cost;\r
299 size_t len = 2;\r
300 size_t next_len_bucket = 4;\r
301 size_t next_len_offset = 10;\r
302 while (pos + len <= num_bytes && nodes[pos + len].u.cost <= min_cost) {\r
303 /* We already reached (pos + len) with no more cost than the minimum\r
304 possible cost of reaching anything from this pos, so there is no point in\r
305 looking for lengths <= len. */\r
306 ++len;\r
307 if (len == next_len_offset) {\r
308 /* We reached the next copy length code bucket, so we add one more\r
309 extra bit to the minimum cost. */\r
310 min_cost += 1.0f;\r
311 next_len_offset += next_len_bucket;\r
312 next_len_bucket *= 2;\r
313 }\r
314 }\r
315 return len;\r
316}\r
317\r
318/* REQUIRES: nodes[pos].cost < kInfinity\r
319 REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */\r
320static uint32_t ComputeDistanceShortcut(const size_t block_start,\r
321 const size_t pos,\r
322 const size_t max_backward,\r
323 const ZopfliNode* nodes) {\r
324 const size_t clen = ZopfliNodeCopyLength(&nodes[pos]);\r
325 const size_t ilen = nodes[pos].insert_length;\r
326 const size_t dist = ZopfliNodeCopyDistance(&nodes[pos]);\r
327 /* Since |block_start + pos| is the end position of the command, the copy part\r
328 starts from |block_start + pos - clen|. Distances that are greater than\r
329 this or greater than |max_backward| are static dictionary references, and\r
330 do not update the last distances. Also distance code 0 (last distance)\r
331 does not update the last distances. */\r
332 if (pos == 0) {\r
333 return 0;\r
334 } else if (dist + clen <= block_start + pos &&\r
335 dist <= max_backward &&\r
336 ZopfliNodeDistanceCode(&nodes[pos]) > 0) {\r
337 return (uint32_t)pos;\r
338 } else {\r
339 return nodes[pos - clen - ilen].u.shortcut;\r
340 }\r
341}\r
342\r
343/* Fills in dist_cache[0..3] with the last four distances (as defined by\r
344 Section 4. of the Spec) that would be used at (block_start + pos) if we\r
345 used the shortest path of commands from block_start, computed from\r
346 nodes[0..pos]. The last four distances at block_start are in\r
347 starting_dist_cach[0..3].\r
348 REQUIRES: nodes[pos].cost < kInfinity\r
349 REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */\r
350static void ComputeDistanceCache(const size_t pos,\r
351 const int* starting_dist_cache,\r
352 const ZopfliNode* nodes,\r
353 int* dist_cache) {\r
354 int idx = 0;\r
355 size_t p = nodes[pos].u.shortcut;\r
356 while (idx < 4 && p > 0) {\r
357 const size_t ilen = nodes[p].insert_length;\r
358 const size_t clen = ZopfliNodeCopyLength(&nodes[p]);\r
359 const size_t dist = ZopfliNodeCopyDistance(&nodes[p]);\r
360 dist_cache[idx++] = (int)dist;\r
361 /* Because of prerequisite, p >= clen + ilen >= 2. */\r
362 p = nodes[p - clen - ilen].u.shortcut;\r
363 }\r
364 for (; idx < 4; ++idx) {\r
365 dist_cache[idx] = *starting_dist_cache++;\r
366 }\r
367}\r
368\r
369static void UpdateNodes(const size_t num_bytes,\r
370 const size_t block_start,\r
371 const size_t pos,\r
372 const uint8_t* ringbuffer,\r
373 const size_t ringbuffer_mask,\r
374 const BrotliEncoderParams* params,\r
375 const size_t max_backward_limit,\r
376 const int* starting_dist_cache,\r
377 const size_t num_matches,\r
378 const BackwardMatch* matches,\r
379 const ZopfliCostModel* model,\r
380 StartPosQueue* queue,\r
381 ZopfliNode* nodes) {\r
382 const size_t cur_ix = block_start + pos;\r
383 const size_t cur_ix_masked = cur_ix & ringbuffer_mask;\r
384 const size_t max_distance = BROTLI_MIN(size_t, cur_ix, max_backward_limit);\r
385 const size_t max_len = num_bytes - pos;\r
386 const size_t max_zopfli_len = MaxZopfliLen(params);\r
387 const size_t max_iters = MaxZopfliCandidates(params);\r
388 size_t min_len;\r
389 size_t k;\r
390\r
391 {\r
392 /* Save cost, because ComputeDistanceCache invalidates it. */\r
393 float node_cost = nodes[pos].u.cost;\r
394 nodes[pos].u.shortcut = ComputeDistanceShortcut(\r
395 block_start, pos, max_backward_limit, nodes);\r
396 if (node_cost <= ZopfliCostModelGetLiteralCosts(model, 0, pos)) {\r
397 PosData posdata;\r
398 posdata.pos = pos;\r
399 posdata.cost = node_cost;\r
400 posdata.costdiff = node_cost -\r
401 ZopfliCostModelGetLiteralCosts(model, 0, pos);\r
402 ComputeDistanceCache(\r
403 pos, starting_dist_cache, nodes, posdata.distance_cache);\r
404 StartPosQueuePush(queue, &posdata);\r
405 }\r
406 }\r
407\r
408 {\r
409 const PosData* posdata = StartPosQueueAt(queue, 0);\r
410 float min_cost = (posdata->cost + ZopfliCostModelGetMinCostCmd(model) +\r
411 ZopfliCostModelGetLiteralCosts(model, posdata->pos, pos));\r
412 min_len = ComputeMinimumCopyLength(min_cost, nodes, num_bytes, pos);\r
413 }\r
414\r
415 /* Go over the command starting positions in order of increasing cost\r
416 difference. */\r
417 for (k = 0; k < max_iters && k < StartPosQueueSize(queue); ++k) {\r
418 const PosData* posdata = StartPosQueueAt(queue, k);\r
419 const size_t start = posdata->pos;\r
420 const uint16_t inscode = GetInsertLengthCode(pos - start);\r
421 const float start_costdiff = posdata->costdiff;\r
422 const float base_cost = start_costdiff + (float)GetInsertExtra(inscode) +\r
423 ZopfliCostModelGetLiteralCosts(model, 0, pos);\r
424\r
425 /* Look for last distance matches using the distance cache from this\r
426 starting position. */\r
427 size_t best_len = min_len - 1;\r
428 size_t j = 0;\r
429 for (; j < BROTLI_NUM_DISTANCE_SHORT_CODES && best_len < max_len; ++j) {\r
430 const size_t idx = kDistanceCacheIndex[j];\r
431 const size_t backward =\r
432 (size_t)(posdata->distance_cache[idx] + kDistanceCacheOffset[j]);\r
433 size_t prev_ix = cur_ix - backward;\r
434 if (prev_ix >= cur_ix) {\r
435 continue;\r
436 }\r
437 if (PREDICT_FALSE(backward > max_distance)) {\r
438 continue;\r
439 }\r
440 prev_ix &= ringbuffer_mask;\r
441\r
442 if (cur_ix_masked + best_len > ringbuffer_mask ||\r
443 prev_ix + best_len > ringbuffer_mask ||\r
444 ringbuffer[cur_ix_masked + best_len] !=\r
445 ringbuffer[prev_ix + best_len]) {\r
446 continue;\r
447 }\r
448 {\r
449 const size_t len =\r
450 FindMatchLengthWithLimit(&ringbuffer[prev_ix],\r
451 &ringbuffer[cur_ix_masked],\r
452 max_len);\r
453 const float dist_cost = base_cost +\r
454 ZopfliCostModelGetDistanceCost(model, j);\r
455 size_t l;\r
456 for (l = best_len + 1; l <= len; ++l) {\r
457 const uint16_t copycode = GetCopyLengthCode(l);\r
458 const uint16_t cmdcode =\r
459 CombineLengthCodes(inscode, copycode, j == 0);\r
460 const float cost = (cmdcode < 128 ? base_cost : dist_cost) +\r
461 (float)GetCopyExtra(copycode) +\r
462 ZopfliCostModelGetCommandCost(model, cmdcode);\r
463 if (cost < nodes[pos + l].u.cost) {\r
464 UpdateZopfliNode(nodes, pos, start, l, l, backward, j + 1, cost);\r
465 }\r
466 best_len = l;\r
467 }\r
468 }\r
469 }\r
470\r
471 /* At higher iterations look only for new last distance matches, since\r
472 looking only for new command start positions with the same distances\r
473 does not help much. */\r
474 if (k >= 2) continue;\r
475\r
476 {\r
477 /* Loop through all possible copy lengths at this position. */\r
478 size_t len = min_len;\r
479 for (j = 0; j < num_matches; ++j) {\r
480 BackwardMatch match = matches[j];\r
481 size_t dist = match.distance;\r
482 BROTLI_BOOL is_dictionary_match = TO_BROTLI_BOOL(dist > max_distance);\r
483 /* We already tried all possible last distance matches, so we can use\r
484 normal distance code here. */\r
485 size_t dist_code = dist + 15;\r
486 uint16_t dist_symbol;\r
487 uint32_t distextra;\r
488 uint32_t distnumextra;\r
489 float dist_cost;\r
490 size_t max_match_len;\r
491 PrefixEncodeCopyDistance(dist_code, 0, 0, &dist_symbol, &distextra);\r
492 distnumextra = distextra >> 24;\r
493 dist_cost = base_cost + (float)distnumextra +\r
494 ZopfliCostModelGetDistanceCost(model, dist_symbol);\r
495\r
496 /* Try all copy lengths up until the maximum copy length corresponding\r
497 to this distance. If the distance refers to the static dictionary, or\r
498 the maximum length is long enough, try only one maximum length. */\r
499 max_match_len = BackwardMatchLength(&match);\r
500 if (len < max_match_len &&\r
501 (is_dictionary_match || max_match_len > max_zopfli_len)) {\r
502 len = max_match_len;\r
503 }\r
504 for (; len <= max_match_len; ++len) {\r
505 const size_t len_code =\r
506 is_dictionary_match ? BackwardMatchLengthCode(&match) : len;\r
507 const uint16_t copycode = GetCopyLengthCode(len_code);\r
508 const uint16_t cmdcode = CombineLengthCodes(inscode, copycode, 0);\r
509 const float cost = dist_cost + (float)GetCopyExtra(copycode) +\r
510 ZopfliCostModelGetCommandCost(model, cmdcode);\r
511 if (cost < nodes[pos + len].u.cost) {\r
512 UpdateZopfliNode(nodes, pos, start, len, len_code, dist, 0, cost);\r
513 }\r
514 }\r
515 }\r
516 }\r
517 }\r
518}\r
519\r
520static size_t ComputeShortestPathFromNodes(size_t num_bytes,\r
521 ZopfliNode* nodes) {\r
522 size_t index = num_bytes;\r
523 size_t num_commands = 0;\r
524 while (nodes[index].insert_length == 0 && nodes[index].length == 1) --index;\r
525 nodes[index].u.next = BROTLI_UINT32_MAX;\r
526 while (index != 0) {\r
527 size_t len = ZopfliNodeCommandLength(&nodes[index]);\r
528 index -= len;\r
529 nodes[index].u.next = (uint32_t)len;\r
530 num_commands++;\r
531 }\r
532 return num_commands;\r
533}\r
534\r
535void BrotliZopfliCreateCommands(const size_t num_bytes,\r
536 const size_t block_start,\r
537 const size_t max_backward_limit,\r
538 const ZopfliNode* nodes,\r
539 int* dist_cache,\r
540 size_t* last_insert_len,\r
541 Command* commands,\r
542 size_t* num_literals) {\r
543 size_t pos = 0;\r
544 uint32_t offset = nodes[0].u.next;\r
545 size_t i;\r
546 for (i = 0; offset != BROTLI_UINT32_MAX; i++) {\r
547 const ZopfliNode* next = &nodes[pos + offset];\r
548 size_t copy_length = ZopfliNodeCopyLength(next);\r
549 size_t insert_length = next->insert_length;\r
550 pos += insert_length;\r
551 offset = next->u.next;\r
552 if (i == 0) {\r
553 insert_length += *last_insert_len;\r
554 *last_insert_len = 0;\r
555 }\r
556 {\r
557 size_t distance = ZopfliNodeCopyDistance(next);\r
558 size_t len_code = ZopfliNodeLengthCode(next);\r
559 size_t max_distance =\r
560 BROTLI_MIN(size_t, block_start + pos, max_backward_limit);\r
561 BROTLI_BOOL is_dictionary = TO_BROTLI_BOOL(distance > max_distance);\r
562 size_t dist_code = ZopfliNodeDistanceCode(next);\r
563\r
564 InitCommand(\r
565 &commands[i], insert_length, copy_length, len_code, dist_code);\r
566\r
567 if (!is_dictionary && dist_code > 0) {\r
568 dist_cache[3] = dist_cache[2];\r
569 dist_cache[2] = dist_cache[1];\r
570 dist_cache[1] = dist_cache[0];\r
571 dist_cache[0] = (int)distance;\r
572 }\r
573 }\r
574\r
575 *num_literals += insert_length;\r
576 pos += copy_length;\r
577 }\r
578 *last_insert_len += num_bytes - pos;\r
579}\r
580\r
581static size_t ZopfliIterate(size_t num_bytes,\r
582 size_t position,\r
583 const uint8_t* ringbuffer,\r
584 size_t ringbuffer_mask,\r
585 const BrotliEncoderParams* params,\r
586 const size_t max_backward_limit,\r
587 const int* dist_cache,\r
588 const ZopfliCostModel* model,\r
589 const uint32_t* num_matches,\r
590 const BackwardMatch* matches,\r
591 ZopfliNode* nodes) {\r
592 const size_t max_zopfli_len = MaxZopfliLen(params);\r
593 StartPosQueue queue;\r
594 size_t cur_match_pos = 0;\r
595 size_t i;\r
596 nodes[0].length = 0;\r
597 nodes[0].u.cost = 0;\r
598 InitStartPosQueue(&queue);\r
599 for (i = 0; i + 3 < num_bytes; i++) {\r
600 UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask,\r
601 params, max_backward_limit, dist_cache, num_matches[i],\r
602 &matches[cur_match_pos], model, &queue, nodes);\r
603 cur_match_pos += num_matches[i];\r
604 /* The zopflification can be too slow in case of very long lengths, so in\r
605 such case skip it all, it does not cost a lot of compression ratio. */\r
606 if (num_matches[i] == 1 &&\r
607 BackwardMatchLength(&matches[cur_match_pos - 1]) > max_zopfli_len) {\r
608 i += BackwardMatchLength(&matches[cur_match_pos - 1]) - 1;\r
609 InitStartPosQueue(&queue);\r
610 }\r
611 }\r
612 return ComputeShortestPathFromNodes(num_bytes, nodes);\r
613}\r
614\r
615\r
616size_t BrotliZopfliComputeShortestPath(MemoryManager* m,\r
617 size_t num_bytes,\r
618 size_t position,\r
619 const uint8_t* ringbuffer,\r
620 size_t ringbuffer_mask,\r
621 const BrotliEncoderParams* params,\r
622 const size_t max_backward_limit,\r
623 const int* dist_cache,\r
624 H10* hasher,\r
625 ZopfliNode* nodes) {\r
626 const size_t max_zopfli_len = MaxZopfliLen(params);\r
627 ZopfliCostModel model;\r
628 StartPosQueue queue;\r
629 BackwardMatch matches[MAX_NUM_MATCHES_H10];\r
630 const size_t store_end = num_bytes >= StoreLookaheadH10() ?\r
631 position + num_bytes - StoreLookaheadH10() + 1 : position;\r
632 size_t i;\r
633 nodes[0].length = 0;\r
634 nodes[0].u.cost = 0;\r
635 InitZopfliCostModel(m, &model, num_bytes);\r
636 if (BROTLI_IS_OOM(m)) return 0;\r
637 ZopfliCostModelSetFromLiteralCosts(\r
638 &model, position, ringbuffer, ringbuffer_mask);\r
639 InitStartPosQueue(&queue);\r
640 for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; i++) {\r
641 const size_t pos = position + i;\r
642 const size_t max_distance = BROTLI_MIN(size_t, pos, max_backward_limit);\r
643 size_t num_matches = FindAllMatchesH10(hasher, ringbuffer, ringbuffer_mask,\r
644 pos, num_bytes - i, max_distance, params, matches);\r
645 if (num_matches > 0 &&\r
646 BackwardMatchLength(&matches[num_matches - 1]) > max_zopfli_len) {\r
647 matches[0] = matches[num_matches - 1];\r
648 num_matches = 1;\r
649 }\r
650 UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask,\r
651 params, max_backward_limit, dist_cache, num_matches, matches,\r
652 &model, &queue, nodes);\r
653 if (num_matches == 1 && BackwardMatchLength(&matches[0]) > max_zopfli_len) {\r
654 /* Add the tail of the copy to the hasher. */\r
655 StoreRangeH10(hasher, ringbuffer, ringbuffer_mask, pos + 1, BROTLI_MIN(\r
656 size_t, pos + BackwardMatchLength(&matches[0]), store_end));\r
657 i += BackwardMatchLength(&matches[0]) - 1;\r
658 InitStartPosQueue(&queue);\r
659 }\r
660 }\r
661 CleanupZopfliCostModel(m, &model);\r
662 return ComputeShortestPathFromNodes(num_bytes, nodes);\r
663}\r
664\r
665#define EXPAND_CAT(a, b) CAT(a, b)\r
666#define CAT(a, b) a ## b\r
667#define FN(X) EXPAND_CAT(X, HASHER())\r
668\r
669#define HASHER() H2\r
670/* NOLINTNEXTLINE(build/include) */\r
671#include "./backward_references_inc.h"\r
672#undef HASHER\r
673\r
674#define HASHER() H3\r
675/* NOLINTNEXTLINE(build/include) */\r
676#include "./backward_references_inc.h"\r
677#undef HASHER\r
678\r
679#define HASHER() H4\r
680/* NOLINTNEXTLINE(build/include) */\r
681#include "./backward_references_inc.h"\r
682#undef HASHER\r
683\r
684#define HASHER() H5\r
685/* NOLINTNEXTLINE(build/include) */\r
686#include "./backward_references_inc.h"\r
687#undef HASHER\r
688\r
689#define HASHER() H6\r
690/* NOLINTNEXTLINE(build/include) */\r
691#include "./backward_references_inc.h"\r
692#undef HASHER\r
693\r
694#define HASHER() H7\r
695/* NOLINTNEXTLINE(build/include) */\r
696#include "./backward_references_inc.h"\r
697#undef HASHER\r
698\r
699#define HASHER() H8\r
700/* NOLINTNEXTLINE(build/include) */\r
701#include "./backward_references_inc.h"\r
702#undef HASHER\r
703\r
704#define HASHER() H9\r
705/* NOLINTNEXTLINE(build/include) */\r
706#include "./backward_references_inc.h"\r
707#undef HASHER\r
708\r
709#define HASHER() H40\r
710/* NOLINTNEXTLINE(build/include) */\r
711#include "./backward_references_inc.h"\r
712#undef HASHER\r
713\r
714#define HASHER() H41\r
715/* NOLINTNEXTLINE(build/include) */\r
716#include "./backward_references_inc.h"\r
717#undef HASHER\r
718\r
719#define HASHER() H42\r
720/* NOLINTNEXTLINE(build/include) */\r
721#include "./backward_references_inc.h"\r
722#undef HASHER\r
723\r
724#undef FN\r
725#undef CAT\r
726#undef EXPAND_CAT\r
727\r
728static BROTLI_NOINLINE void CreateZopfliBackwardReferences(\r
729 MemoryManager* m, size_t num_bytes, size_t position, BROTLI_BOOL is_last,\r
730 const uint8_t* ringbuffer, size_t ringbuffer_mask,\r
731 const BrotliEncoderParams* params, H10* hasher, int* dist_cache,\r
732 size_t* last_insert_len, Command* commands, size_t* num_commands,\r
733 size_t* num_literals) {\r
734 const size_t max_backward_limit = MaxBackwardLimit(params->lgwin);\r
735 ZopfliNode* nodes;\r
736 InitH10(m, hasher, ringbuffer, params, position, num_bytes, is_last);\r
737 if (BROTLI_IS_OOM(m)) return;\r
738 StitchToPreviousBlockH10(hasher, num_bytes, position,\r
739 ringbuffer, ringbuffer_mask);\r
740 nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1);\r
741 if (BROTLI_IS_OOM(m)) return;\r
742 BrotliInitZopfliNodes(nodes, num_bytes + 1);\r
743 *num_commands += BrotliZopfliComputeShortestPath(m, num_bytes, position,\r
744 ringbuffer, ringbuffer_mask, params, max_backward_limit,\r
745 dist_cache, hasher, nodes);\r
746 if (BROTLI_IS_OOM(m)) return;\r
747 BrotliZopfliCreateCommands(num_bytes, position, max_backward_limit, nodes,\r
748 dist_cache, last_insert_len, commands, num_literals);\r
749 BROTLI_FREE(m, nodes);\r
750}\r
751\r
752static BROTLI_NOINLINE void CreateHqZopfliBackwardReferences(\r
753 MemoryManager* m, size_t num_bytes, size_t position, BROTLI_BOOL is_last,\r
754 const uint8_t* ringbuffer, size_t ringbuffer_mask,\r
755 const BrotliEncoderParams* params, H10* hasher, int* dist_cache,\r
756 size_t* last_insert_len, Command* commands, size_t* num_commands,\r
757 size_t* num_literals) {\r
758 const size_t max_backward_limit = MaxBackwardLimit(params->lgwin);\r
759 uint32_t* num_matches = BROTLI_ALLOC(m, uint32_t, num_bytes);\r
760 size_t matches_size = 4 * num_bytes;\r
761 const size_t store_end = num_bytes >= StoreLookaheadH10() ?\r
762 position + num_bytes - StoreLookaheadH10() + 1 : position;\r
763 size_t cur_match_pos = 0;\r
764 size_t i;\r
765 size_t orig_num_literals;\r
766 size_t orig_last_insert_len;\r
767 int orig_dist_cache[4];\r
768 size_t orig_num_commands;\r
769 ZopfliCostModel model;\r
770 ZopfliNode* nodes;\r
771 BackwardMatch* matches = BROTLI_ALLOC(m, BackwardMatch, matches_size);\r
772 if (BROTLI_IS_OOM(m)) return;\r
773 InitH10(m, hasher, ringbuffer, params, position, num_bytes, is_last);\r
774 if (BROTLI_IS_OOM(m)) return;\r
775 StitchToPreviousBlockH10(hasher, num_bytes, position,\r
776 ringbuffer, ringbuffer_mask);\r
777 for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; ++i) {\r
778 const size_t pos = position + i;\r
779 size_t max_distance = BROTLI_MIN(size_t, pos, max_backward_limit);\r
780 size_t max_length = num_bytes - i;\r
781 size_t num_found_matches;\r
782 size_t cur_match_end;\r
783 size_t j;\r
784 /* Ensure that we have enough free slots. */\r
785 BROTLI_ENSURE_CAPACITY(m, BackwardMatch, matches, matches_size,\r
786 cur_match_pos + MAX_NUM_MATCHES_H10);\r
787 if (BROTLI_IS_OOM(m)) return;\r
788 num_found_matches = FindAllMatchesH10(hasher, ringbuffer, ringbuffer_mask,\r
789 pos, max_length, max_distance, params, &matches[cur_match_pos]);\r
790 cur_match_end = cur_match_pos + num_found_matches;\r
791 for (j = cur_match_pos; j + 1 < cur_match_end; ++j) {\r
792 assert(BackwardMatchLength(&matches[j]) <\r
793 BackwardMatchLength(&matches[j + 1]));\r
794 assert(matches[j].distance > max_distance ||\r
795 matches[j].distance <= matches[j + 1].distance);\r
796 }\r
797 num_matches[i] = (uint32_t)num_found_matches;\r
798 if (num_found_matches > 0) {\r
799 const size_t match_len = BackwardMatchLength(&matches[cur_match_end - 1]);\r
800 if (match_len > MAX_ZOPFLI_LEN_QUALITY_11) {\r
801 const size_t skip = match_len - 1;\r
802 matches[cur_match_pos++] = matches[cur_match_end - 1];\r
803 num_matches[i] = 1;\r
804 /* Add the tail of the copy to the hasher. */\r
805 StoreRangeH10(hasher, ringbuffer, ringbuffer_mask, pos + 1,\r
806 BROTLI_MIN(size_t, pos + match_len, store_end));\r
807 memset(&num_matches[i + 1], 0, skip * sizeof(num_matches[0]));\r
808 i += skip;\r
809 } else {\r
810 cur_match_pos = cur_match_end;\r
811 }\r
812 }\r
813 }\r
814 orig_num_literals = *num_literals;\r
815 orig_last_insert_len = *last_insert_len;\r
816 memcpy(orig_dist_cache, dist_cache, 4 * sizeof(dist_cache[0]));\r
817 orig_num_commands = *num_commands;\r
818 nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1);\r
819 if (BROTLI_IS_OOM(m)) return;\r
820 InitZopfliCostModel(m, &model, num_bytes);\r
821 if (BROTLI_IS_OOM(m)) return;\r
822 for (i = 0; i < 2; i++) {\r
823 BrotliInitZopfliNodes(nodes, num_bytes + 1);\r
824 if (i == 0) {\r
825 ZopfliCostModelSetFromLiteralCosts(\r
826 &model, position, ringbuffer, ringbuffer_mask);\r
827 } else {\r
828 ZopfliCostModelSetFromCommands(&model, position, ringbuffer,\r
829 ringbuffer_mask, commands, *num_commands - orig_num_commands,\r
830 orig_last_insert_len);\r
831 }\r
832 *num_commands = orig_num_commands;\r
833 *num_literals = orig_num_literals;\r
834 *last_insert_len = orig_last_insert_len;\r
835 memcpy(dist_cache, orig_dist_cache, 4 * sizeof(dist_cache[0]));\r
836 *num_commands += ZopfliIterate(num_bytes, position, ringbuffer,\r
837 ringbuffer_mask, params, max_backward_limit, dist_cache,\r
838 &model, num_matches, matches, nodes);\r
839 BrotliZopfliCreateCommands(num_bytes, position, max_backward_limit,\r
840 nodes, dist_cache, last_insert_len, commands, num_literals);\r
841 }\r
842 CleanupZopfliCostModel(m, &model);\r
843 BROTLI_FREE(m, nodes);\r
844 BROTLI_FREE(m, matches);\r
845 BROTLI_FREE(m, num_matches);\r
846}\r
847\r
848void BrotliCreateBackwardReferences(MemoryManager* m,\r
849 size_t num_bytes,\r
850 size_t position,\r
851 BROTLI_BOOL is_last,\r
852 const uint8_t* ringbuffer,\r
853 size_t ringbuffer_mask,\r
854 const BrotliEncoderParams* params,\r
855 Hashers* hashers,\r
856 int* dist_cache,\r
857 size_t* last_insert_len,\r
858 Command* commands,\r
859 size_t* num_commands,\r
860 size_t* num_literals) {\r
861 if (params->quality == ZOPFLIFICATION_QUALITY) {\r
862 CreateZopfliBackwardReferences(\r
863 m, num_bytes, position, is_last, ringbuffer, ringbuffer_mask,\r
864 params, hashers->h10, dist_cache,\r
865 last_insert_len, commands, num_commands, num_literals);\r
866 return;\r
867 } else if (params->quality == HQ_ZOPFLIFICATION_QUALITY) {\r
868 CreateHqZopfliBackwardReferences(\r
869 m, num_bytes, position, is_last, ringbuffer, ringbuffer_mask,\r
870 params, hashers->h10, dist_cache,\r
871 last_insert_len, commands, num_commands, num_literals);\r
872 return;\r
873 }\r
874\r
875 switch (ChooseHasher(params)) {\r
876#define _CASE(N) \\r
877 case N: \\r
878 CreateBackwardReferencesH ## N(m, num_bytes, position, is_last, \\r
879 ringbuffer, ringbuffer_mask, params, hashers->h ## N, dist_cache, \\r
880 last_insert_len, commands, num_commands, num_literals); \\r
881 break;\r
882 FOR_GENERIC_HASHERS(_CASE)\r
883#undef _CASE\r
884 default:\r
885 break;\r
886 }\r
887 if (BROTLI_IS_OOM(m)) return;\r
888}\r
889\r
890#if defined(__cplusplus) || defined(c_plusplus)\r
891} /* extern "C" */\r
892#endif\r