1 /* Copyright 2015 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 /* Algorithms for distributing the literals and commands of a metablock between
8 block types and contexts. */
9
10 #include "./metablock.h"
11
12 #include "../common/constants.h"
13 #include <brotli/types.h>
14 #include "./bit_cost.h"
15 #include "./block_splitter.h"
16 #include "./cluster.h"
17 #include "./context.h"
18 #include "./entropy_encode.h"
19 #include "./histogram.h"
20 #include "./memory.h"
21 #include "./port.h"
22 #include "./quality.h"
23
24 #if defined(__cplusplus) || defined(c_plusplus)
25 extern "C" {
26 #endif
27
BrotliBuildMetaBlock(MemoryManager * m,const uint8_t * ringbuffer,const size_t pos,const size_t mask,const BrotliEncoderParams * params,uint8_t prev_byte,uint8_t prev_byte2,const Command * cmds,size_t num_commands,ContextType literal_context_mode,MetaBlockSplit * mb)28 void BrotliBuildMetaBlock(MemoryManager* m,
29 const uint8_t* ringbuffer,
30 const size_t pos,
31 const size_t mask,
32 const BrotliEncoderParams* params,
33 uint8_t prev_byte,
34 uint8_t prev_byte2,
35 const Command* cmds,
36 size_t num_commands,
37 ContextType literal_context_mode,
38 MetaBlockSplit* mb) {
39 /* Histogram ids need to fit in one byte. */
40 static const size_t kMaxNumberOfHistograms = 256;
41 HistogramDistance* distance_histograms;
42 HistogramLiteral* literal_histograms;
43 ContextType* literal_context_modes = NULL;
44 size_t literal_histograms_size;
45 size_t distance_histograms_size;
46 size_t i;
47 size_t literal_context_multiplier = 1;
48
49 BrotliSplitBlock(m, cmds, num_commands,
50 ringbuffer, pos, mask, params,
51 &mb->literal_split,
52 &mb->command_split,
53 &mb->distance_split);
54 if (BROTLI_IS_OOM(m)) return;
55
56 if (!params->disable_literal_context_modeling) {
57 literal_context_multiplier = 1 << BROTLI_LITERAL_CONTEXT_BITS;
58 literal_context_modes =
59 BROTLI_ALLOC(m, ContextType, mb->literal_split.num_types);
60 if (BROTLI_IS_OOM(m)) return;
61 for (i = 0; i < mb->literal_split.num_types; ++i) {
62 literal_context_modes[i] = literal_context_mode;
63 }
64 }
65
66 literal_histograms_size =
67 mb->literal_split.num_types * literal_context_multiplier;
68 literal_histograms =
69 BROTLI_ALLOC(m, HistogramLiteral, literal_histograms_size);
70 if (BROTLI_IS_OOM(m)) return;
71 ClearHistogramsLiteral(literal_histograms, literal_histograms_size);
72
73 distance_histograms_size =
74 mb->distance_split.num_types << BROTLI_DISTANCE_CONTEXT_BITS;
75 distance_histograms =
76 BROTLI_ALLOC(m, HistogramDistance, distance_histograms_size);
77 if (BROTLI_IS_OOM(m)) return;
78 ClearHistogramsDistance(distance_histograms, distance_histograms_size);
79
80 assert(mb->command_histograms == 0);
81 mb->command_histograms_size = mb->command_split.num_types;
82 mb->command_histograms =
83 BROTLI_ALLOC(m, HistogramCommand, mb->command_histograms_size);
84 if (BROTLI_IS_OOM(m)) return;
85 ClearHistogramsCommand(mb->command_histograms, mb->command_histograms_size);
86
87 BrotliBuildHistogramsWithContext(cmds, num_commands,
88 &mb->literal_split, &mb->command_split, &mb->distance_split,
89 ringbuffer, pos, mask, prev_byte, prev_byte2, literal_context_modes,
90 literal_histograms, mb->command_histograms, distance_histograms);
91 BROTLI_FREE(m, literal_context_modes);
92
93 assert(mb->literal_context_map == 0);
94 mb->literal_context_map_size =
95 mb->literal_split.num_types << BROTLI_LITERAL_CONTEXT_BITS;
96 mb->literal_context_map =
97 BROTLI_ALLOC(m, uint32_t, mb->literal_context_map_size);
98 if (BROTLI_IS_OOM(m)) return;
99
100 assert(mb->literal_histograms == 0);
101 mb->literal_histograms_size = mb->literal_context_map_size;
102 mb->literal_histograms =
103 BROTLI_ALLOC(m, HistogramLiteral, mb->literal_histograms_size);
104 if (BROTLI_IS_OOM(m)) return;
105
106 BrotliClusterHistogramsLiteral(m, literal_histograms, literal_histograms_size,
107 kMaxNumberOfHistograms, mb->literal_histograms,
108 &mb->literal_histograms_size, mb->literal_context_map);
109 if (BROTLI_IS_OOM(m)) return;
110 BROTLI_FREE(m, literal_histograms);
111
112 if (params->disable_literal_context_modeling) {
113 /* Distribute assignment to all contexts. */
114 for (i = mb->literal_split.num_types; i != 0;) {
115 size_t j = 0;
116 i--;
117 for (; j < (1 << BROTLI_LITERAL_CONTEXT_BITS); j++) {
118 mb->literal_context_map[(i << BROTLI_LITERAL_CONTEXT_BITS) + j] =
119 mb->literal_context_map[i];
120 }
121 }
122 }
123
124 assert(mb->distance_context_map == 0);
125 mb->distance_context_map_size =
126 mb->distance_split.num_types << BROTLI_DISTANCE_CONTEXT_BITS;
127 mb->distance_context_map =
128 BROTLI_ALLOC(m, uint32_t, mb->distance_context_map_size);
129 if (BROTLI_IS_OOM(m)) return;
130
131 assert(mb->distance_histograms == 0);
132 mb->distance_histograms_size = mb->distance_context_map_size;
133 mb->distance_histograms =
134 BROTLI_ALLOC(m, HistogramDistance, mb->distance_histograms_size);
135 if (BROTLI_IS_OOM(m)) return;
136
137 BrotliClusterHistogramsDistance(m, distance_histograms,
138 mb->distance_context_map_size,
139 kMaxNumberOfHistograms,
140 mb->distance_histograms,
141 &mb->distance_histograms_size,
142 mb->distance_context_map);
143 if (BROTLI_IS_OOM(m)) return;
144 BROTLI_FREE(m, distance_histograms);
145 }
146
147 #define FN(X) X ## Literal
148 #include "./metablock_inc.h" /* NOLINT(build/include) */
149 #undef FN
150
151 #define FN(X) X ## Command
152 #include "./metablock_inc.h" /* NOLINT(build/include) */
153 #undef FN
154
155 #define FN(X) X ## Distance
156 #include "./metablock_inc.h" /* NOLINT(build/include) */
157 #undef FN
158
159 #define BROTLI_MAX_STATIC_CONTEXTS 13
160
161 /* Greedy block splitter for one block category (literal, command or distance).
162 Gathers histograms for all context buckets. */
163 typedef struct ContextBlockSplitter {
164 /* Alphabet size of particular block category. */
165 size_t alphabet_size_;
166 size_t num_contexts_;
167 size_t max_block_types_;
168 /* We collect at least this many symbols for each block. */
169 size_t min_block_size_;
170 /* We merge histograms A and B if
171 entropy(A+B) < entropy(A) + entropy(B) + split_threshold_,
172 where A is the current histogram and B is the histogram of the last or the
173 second last block type. */
174 double split_threshold_;
175
176 size_t num_blocks_;
177 BlockSplit* split_; /* not owned */
178 HistogramLiteral* histograms_; /* not owned */
179 size_t* histograms_size_; /* not owned */
180
181 /* The number of symbols that we want to collect before deciding on whether
182 or not to merge the block with a previous one or emit a new block. */
183 size_t target_block_size_;
184 /* The number of symbols in the current histogram. */
185 size_t block_size_;
186 /* Offset of the current histogram. */
187 size_t curr_histogram_ix_;
188 /* Offset of the histograms of the previous two block types. */
189 size_t last_histogram_ix_[2];
190 /* Entropy of the previous two block types. */
191 double last_entropy_[2 * BROTLI_MAX_STATIC_CONTEXTS];
192 /* The number of times we merged the current block with the last one. */
193 size_t merge_last_count_;
194 } ContextBlockSplitter;
195
InitContextBlockSplitter(MemoryManager * m,ContextBlockSplitter * self,size_t alphabet_size,size_t num_contexts,size_t min_block_size,double split_threshold,size_t num_symbols,BlockSplit * split,HistogramLiteral ** histograms,size_t * histograms_size)196 static void InitContextBlockSplitter(
197 MemoryManager* m, ContextBlockSplitter* self, size_t alphabet_size,
198 size_t num_contexts, size_t min_block_size, double split_threshold,
199 size_t num_symbols, BlockSplit* split, HistogramLiteral** histograms,
200 size_t* histograms_size) {
201 size_t max_num_blocks = num_symbols / min_block_size + 1;
202 size_t max_num_types;
203 assert(num_contexts <= BROTLI_MAX_STATIC_CONTEXTS);
204
205 self->alphabet_size_ = alphabet_size;
206 self->num_contexts_ = num_contexts;
207 self->max_block_types_ = BROTLI_MAX_NUMBER_OF_BLOCK_TYPES / num_contexts;
208 self->min_block_size_ = min_block_size;
209 self->split_threshold_ = split_threshold;
210 self->num_blocks_ = 0;
211 self->split_ = split;
212 self->histograms_size_ = histograms_size;
213 self->target_block_size_ = min_block_size;
214 self->block_size_ = 0;
215 self->curr_histogram_ix_ = 0;
216 self->merge_last_count_ = 0;
217
218 /* We have to allocate one more histogram than the maximum number of block
219 types for the current histogram when the meta-block is too big. */
220 max_num_types =
221 BROTLI_MIN(size_t, max_num_blocks, self->max_block_types_ + 1);
222 BROTLI_ENSURE_CAPACITY(m, uint8_t,
223 split->types, split->types_alloc_size, max_num_blocks);
224 BROTLI_ENSURE_CAPACITY(m, uint32_t,
225 split->lengths, split->lengths_alloc_size, max_num_blocks);
226 if (BROTLI_IS_OOM(m)) return;
227 split->num_blocks = max_num_blocks;
228 if (BROTLI_IS_OOM(m)) return;
229 assert(*histograms == 0);
230 *histograms_size = max_num_types * num_contexts;
231 *histograms = BROTLI_ALLOC(m, HistogramLiteral, *histograms_size);
232 self->histograms_ = *histograms;
233 if (BROTLI_IS_OOM(m)) return;
234 /* Clear only current histogram. */
235 ClearHistogramsLiteral(&self->histograms_[0], num_contexts);
236 self->last_histogram_ix_[0] = self->last_histogram_ix_[1] = 0;
237 }
238
239 /* Does either of three things:
240 (1) emits the current block with a new block type;
241 (2) emits the current block with the type of the second last block;
242 (3) merges the current block with the last block. */
ContextBlockSplitterFinishBlock(ContextBlockSplitter * self,MemoryManager * m,BROTLI_BOOL is_final)243 static void ContextBlockSplitterFinishBlock(
244 ContextBlockSplitter* self, MemoryManager* m, BROTLI_BOOL is_final) {
245 BlockSplit* split = self->split_;
246 const size_t num_contexts = self->num_contexts_;
247 double* last_entropy = self->last_entropy_;
248 HistogramLiteral* histograms = self->histograms_;
249
250 if (self->block_size_ < self->min_block_size_) {
251 self->block_size_ = self->min_block_size_;
252 }
253 if (self->num_blocks_ == 0) {
254 size_t i;
255 /* Create first block. */
256 split->lengths[0] = (uint32_t)self->block_size_;
257 split->types[0] = 0;
258
259 for (i = 0; i < num_contexts; ++i) {
260 last_entropy[i] =
261 BitsEntropy(histograms[i].data_, self->alphabet_size_);
262 last_entropy[num_contexts + i] = last_entropy[i];
263 }
264 ++self->num_blocks_;
265 ++split->num_types;
266 self->curr_histogram_ix_ += num_contexts;
267 if (self->curr_histogram_ix_ < *self->histograms_size_) {
268 ClearHistogramsLiteral(
269 &self->histograms_[self->curr_histogram_ix_], self->num_contexts_);
270 }
271 self->block_size_ = 0;
272 } else if (self->block_size_ > 0) {
273 /* Try merging the set of histograms for the current block type with the
274 respective set of histograms for the last and second last block types.
275 Decide over the split based on the total reduction of entropy across
276 all contexts. */
277 double entropy[BROTLI_MAX_STATIC_CONTEXTS];
278 HistogramLiteral* combined_histo =
279 BROTLI_ALLOC(m, HistogramLiteral, 2 * num_contexts);
280 double combined_entropy[2 * BROTLI_MAX_STATIC_CONTEXTS];
281 double diff[2] = { 0.0 };
282 size_t i;
283 if (BROTLI_IS_OOM(m)) return;
284 for (i = 0; i < num_contexts; ++i) {
285 size_t curr_histo_ix = self->curr_histogram_ix_ + i;
286 size_t j;
287 entropy[i] = BitsEntropy(histograms[curr_histo_ix].data_,
288 self->alphabet_size_);
289 for (j = 0; j < 2; ++j) {
290 size_t jx = j * num_contexts + i;
291 size_t last_histogram_ix = self->last_histogram_ix_[j] + i;
292 combined_histo[jx] = histograms[curr_histo_ix];
293 HistogramAddHistogramLiteral(&combined_histo[jx],
294 &histograms[last_histogram_ix]);
295 combined_entropy[jx] = BitsEntropy(
296 &combined_histo[jx].data_[0], self->alphabet_size_);
297 diff[j] += combined_entropy[jx] - entropy[i] - last_entropy[jx];
298 }
299 }
300
301 if (split->num_types < self->max_block_types_ &&
302 diff[0] > self->split_threshold_ &&
303 diff[1] > self->split_threshold_) {
304 /* Create new block. */
305 split->lengths[self->num_blocks_] = (uint32_t)self->block_size_;
306 split->types[self->num_blocks_] = (uint8_t)split->num_types;
307 self->last_histogram_ix_[1] = self->last_histogram_ix_[0];
308 self->last_histogram_ix_[0] = split->num_types * num_contexts;
309 for (i = 0; i < num_contexts; ++i) {
310 last_entropy[num_contexts + i] = last_entropy[i];
311 last_entropy[i] = entropy[i];
312 }
313 ++self->num_blocks_;
314 ++split->num_types;
315 self->curr_histogram_ix_ += num_contexts;
316 if (self->curr_histogram_ix_ < *self->histograms_size_) {
317 ClearHistogramsLiteral(
318 &self->histograms_[self->curr_histogram_ix_], self->num_contexts_);
319 }
320 self->block_size_ = 0;
321 self->merge_last_count_ = 0;
322 self->target_block_size_ = self->min_block_size_;
323 } else if (diff[1] < diff[0] - 20.0) {
324 /* Combine this block with second last block. */
325 split->lengths[self->num_blocks_] = (uint32_t)self->block_size_;
326 split->types[self->num_blocks_] = split->types[self->num_blocks_ - 2];
327 BROTLI_SWAP(size_t, self->last_histogram_ix_, 0, 1);
328 for (i = 0; i < num_contexts; ++i) {
329 histograms[self->last_histogram_ix_[0] + i] =
330 combined_histo[num_contexts + i];
331 last_entropy[num_contexts + i] = last_entropy[i];
332 last_entropy[i] = combined_entropy[num_contexts + i];
333 HistogramClearLiteral(&histograms[self->curr_histogram_ix_ + i]);
334 }
335 ++self->num_blocks_;
336 self->block_size_ = 0;
337 self->merge_last_count_ = 0;
338 self->target_block_size_ = self->min_block_size_;
339 } else {
340 /* Combine this block with last block. */
341 split->lengths[self->num_blocks_ - 1] += (uint32_t)self->block_size_;
342 for (i = 0; i < num_contexts; ++i) {
343 histograms[self->last_histogram_ix_[0] + i] = combined_histo[i];
344 last_entropy[i] = combined_entropy[i];
345 if (split->num_types == 1) {
346 last_entropy[num_contexts + i] = last_entropy[i];
347 }
348 HistogramClearLiteral(&histograms[self->curr_histogram_ix_ + i]);
349 }
350 self->block_size_ = 0;
351 if (++self->merge_last_count_ > 1) {
352 self->target_block_size_ += self->min_block_size_;
353 }
354 }
355 BROTLI_FREE(m, combined_histo);
356 }
357 if (is_final) {
358 *self->histograms_size_ = split->num_types * num_contexts;
359 split->num_blocks = self->num_blocks_;
360 }
361 }
362
363 /* Adds the next symbol to the current block type and context. When the
364 current block reaches the target size, decides on merging the block. */
ContextBlockSplitterAddSymbol(ContextBlockSplitter * self,MemoryManager * m,size_t symbol,size_t context)365 static void ContextBlockSplitterAddSymbol(
366 ContextBlockSplitter* self, MemoryManager* m,
367 size_t symbol, size_t context) {
368 HistogramAddLiteral(&self->histograms_[self->curr_histogram_ix_ + context],
369 symbol);
370 ++self->block_size_;
371 if (self->block_size_ == self->target_block_size_) {
372 ContextBlockSplitterFinishBlock(self, m, /* is_final = */ BROTLI_FALSE);
373 if (BROTLI_IS_OOM(m)) return;
374 }
375 }
376
MapStaticContexts(MemoryManager * m,size_t num_contexts,const uint32_t * static_context_map,MetaBlockSplit * mb)377 static void MapStaticContexts(MemoryManager* m,
378 size_t num_contexts,
379 const uint32_t* static_context_map,
380 MetaBlockSplit* mb) {
381 size_t i;
382 assert(mb->literal_context_map == 0);
383 mb->literal_context_map_size =
384 mb->literal_split.num_types << BROTLI_LITERAL_CONTEXT_BITS;
385 mb->literal_context_map =
386 BROTLI_ALLOC(m, uint32_t, mb->literal_context_map_size);
387 if (BROTLI_IS_OOM(m)) return;
388
389 for (i = 0; i < mb->literal_split.num_types; ++i) {
390 uint32_t offset = (uint32_t)(i * num_contexts);
391 size_t j;
392 for (j = 0; j < (1u << BROTLI_LITERAL_CONTEXT_BITS); ++j) {
393 mb->literal_context_map[(i << BROTLI_LITERAL_CONTEXT_BITS) + j] =
394 offset + static_context_map[j];
395 }
396 }
397 }
398
BrotliBuildMetaBlockGreedyInternal(MemoryManager * m,const uint8_t * ringbuffer,size_t pos,size_t mask,uint8_t prev_byte,uint8_t prev_byte2,ContextType literal_context_mode,const size_t num_contexts,const uint32_t * static_context_map,const Command * commands,size_t n_commands,MetaBlockSplit * mb)399 static BROTLI_INLINE void BrotliBuildMetaBlockGreedyInternal(
400 MemoryManager* m, const uint8_t* ringbuffer, size_t pos, size_t mask,
401 uint8_t prev_byte, uint8_t prev_byte2, ContextType literal_context_mode,
402 const size_t num_contexts, const uint32_t* static_context_map,
403 const Command *commands, size_t n_commands, MetaBlockSplit* mb) {
404 union {
405 BlockSplitterLiteral plain;
406 ContextBlockSplitter ctx;
407 } lit_blocks;
408 BlockSplitterCommand cmd_blocks;
409 BlockSplitterDistance dist_blocks;
410 size_t num_literals = 0;
411 size_t i;
412 for (i = 0; i < n_commands; ++i) {
413 num_literals += commands[i].insert_len_;
414 }
415
416 if (num_contexts == 1) {
417 InitBlockSplitterLiteral(m, &lit_blocks.plain, 256, 512, 400.0,
418 num_literals, &mb->literal_split, &mb->literal_histograms,
419 &mb->literal_histograms_size);
420 } else {
421 InitContextBlockSplitter(m, &lit_blocks.ctx, 256, num_contexts, 512, 400.0,
422 num_literals, &mb->literal_split, &mb->literal_histograms,
423 &mb->literal_histograms_size);
424 }
425 if (BROTLI_IS_OOM(m)) return;
426 InitBlockSplitterCommand(m, &cmd_blocks, BROTLI_NUM_COMMAND_SYMBOLS, 1024,
427 500.0, n_commands, &mb->command_split, &mb->command_histograms,
428 &mb->command_histograms_size);
429 if (BROTLI_IS_OOM(m)) return;
430 InitBlockSplitterDistance(m, &dist_blocks, 64, 512, 100.0, n_commands,
431 &mb->distance_split, &mb->distance_histograms,
432 &mb->distance_histograms_size);
433 if (BROTLI_IS_OOM(m)) return;
434
435 for (i = 0; i < n_commands; ++i) {
436 const Command cmd = commands[i];
437 size_t j;
438 BlockSplitterAddSymbolCommand(&cmd_blocks, cmd.cmd_prefix_);
439 for (j = cmd.insert_len_; j != 0; --j) {
440 uint8_t literal = ringbuffer[pos & mask];
441 if (num_contexts == 1) {
442 BlockSplitterAddSymbolLiteral(&lit_blocks.plain, literal);
443 } else {
444 size_t context = Context(prev_byte, prev_byte2, literal_context_mode);
445 ContextBlockSplitterAddSymbol(&lit_blocks.ctx, m, literal,
446 static_context_map[context]);
447 if (BROTLI_IS_OOM(m)) return;
448 }
449 prev_byte2 = prev_byte;
450 prev_byte = literal;
451 ++pos;
452 }
453 pos += CommandCopyLen(&cmd);
454 if (CommandCopyLen(&cmd)) {
455 prev_byte2 = ringbuffer[(pos - 2) & mask];
456 prev_byte = ringbuffer[(pos - 1) & mask];
457 if (cmd.cmd_prefix_ >= 128) {
458 BlockSplitterAddSymbolDistance(&dist_blocks, cmd.dist_prefix_);
459 }
460 }
461 }
462
463 if (num_contexts == 1) {
464 BlockSplitterFinishBlockLiteral(
465 &lit_blocks.plain, /* is_final = */ BROTLI_TRUE);
466 } else {
467 ContextBlockSplitterFinishBlock(
468 &lit_blocks.ctx, m, /* is_final = */ BROTLI_TRUE);
469 if (BROTLI_IS_OOM(m)) return;
470 }
471 BlockSplitterFinishBlockCommand(&cmd_blocks, /* is_final = */ BROTLI_TRUE);
472 BlockSplitterFinishBlockDistance(&dist_blocks, /* is_final = */ BROTLI_TRUE);
473
474 if (num_contexts > 1) {
475 MapStaticContexts(m, num_contexts, static_context_map, mb);
476 }
477 }
478
BrotliBuildMetaBlockGreedy(MemoryManager * m,const uint8_t * ringbuffer,size_t pos,size_t mask,uint8_t prev_byte,uint8_t prev_byte2,ContextType literal_context_mode,size_t num_contexts,const uint32_t * static_context_map,const Command * commands,size_t n_commands,MetaBlockSplit * mb)479 void BrotliBuildMetaBlockGreedy(MemoryManager* m,
480 const uint8_t* ringbuffer,
481 size_t pos,
482 size_t mask,
483 uint8_t prev_byte,
484 uint8_t prev_byte2,
485 ContextType literal_context_mode,
486 size_t num_contexts,
487 const uint32_t* static_context_map,
488 const Command* commands,
489 size_t n_commands,
490 MetaBlockSplit* mb) {
491 if (num_contexts == 1) {
492 BrotliBuildMetaBlockGreedyInternal(m, ringbuffer, pos, mask, prev_byte,
493 prev_byte2, literal_context_mode, 1, NULL, commands, n_commands, mb);
494 } else {
495 BrotliBuildMetaBlockGreedyInternal(m, ringbuffer, pos, mask, prev_byte,
496 prev_byte2, literal_context_mode, num_contexts, static_context_map,
497 commands, n_commands, mb);
498 }
499 }
500
BrotliOptimizeHistograms(size_t num_direct_distance_codes,size_t distance_postfix_bits,MetaBlockSplit * mb)501 void BrotliOptimizeHistograms(size_t num_direct_distance_codes,
502 size_t distance_postfix_bits,
503 MetaBlockSplit* mb) {
504 uint8_t good_for_rle[BROTLI_NUM_COMMAND_SYMBOLS];
505 size_t num_distance_codes;
506 size_t i;
507 for (i = 0; i < mb->literal_histograms_size; ++i) {
508 BrotliOptimizeHuffmanCountsForRle(256, mb->literal_histograms[i].data_,
509 good_for_rle);
510 }
511 for (i = 0; i < mb->command_histograms_size; ++i) {
512 BrotliOptimizeHuffmanCountsForRle(BROTLI_NUM_COMMAND_SYMBOLS,
513 mb->command_histograms[i].data_,
514 good_for_rle);
515 }
516 num_distance_codes = BROTLI_NUM_DISTANCE_SHORT_CODES +
517 num_direct_distance_codes +
518 ((2 * BROTLI_MAX_DISTANCE_BITS) << distance_postfix_bits);
519 for (i = 0; i < mb->distance_histograms_size; ++i) {
520 BrotliOptimizeHuffmanCountsForRle(num_distance_codes,
521 mb->distance_histograms[i].data_,
522 good_for_rle);
523 }
524 }
525
526 #if defined(__cplusplus) || defined(c_plusplus)
527 } /* extern "C" */
528 #endif
529