brotli 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/ext/brotli/brotli.cc +114 -24
  3. data/ext/brotli/brotli.h +0 -1
  4. data/ext/brotli/extconf.rb +30 -23
  5. data/lib/brotli/version.rb +1 -1
  6. data/vendor/brotli/LICENSE +1 -1
  7. data/vendor/brotli/dec/Makefile +1 -1
  8. data/vendor/brotli/dec/bit_reader.c +3 -3
  9. data/vendor/brotli/dec/bit_reader.h +25 -27
  10. data/vendor/brotli/dec/context.h +4 -4
  11. data/vendor/brotli/dec/decode.c +410 -486
  12. data/vendor/brotli/dec/decode.h +101 -105
  13. data/vendor/brotli/dec/dictionary.c +1 -1
  14. data/vendor/brotli/dec/dictionary.h +7 -8
  15. data/vendor/brotli/dec/huffman.c +103 -105
  16. data/vendor/brotli/dec/huffman.h +18 -18
  17. data/vendor/brotli/dec/port.h +52 -40
  18. data/vendor/brotli/dec/prefix.h +2 -0
  19. data/vendor/brotli/dec/state.c +13 -19
  20. data/vendor/brotli/dec/state.h +25 -39
  21. data/vendor/brotli/dec/transform.h +38 -44
  22. data/vendor/brotli/dec/types.h +2 -2
  23. data/vendor/brotli/enc/Makefile +1 -1
  24. data/vendor/brotli/enc/backward_references.cc +455 -359
  25. data/vendor/brotli/enc/backward_references.h +79 -3
  26. data/vendor/brotli/enc/bit_cost.h +54 -32
  27. data/vendor/brotli/enc/block_splitter.cc +285 -193
  28. data/vendor/brotli/enc/block_splitter.h +4 -12
  29. data/vendor/brotli/enc/brotli_bit_stream.cc +623 -324
  30. data/vendor/brotli/enc/brotli_bit_stream.h +76 -37
  31. data/vendor/brotli/enc/cluster.h +161 -120
  32. data/vendor/brotli/enc/command.h +60 -37
  33. data/vendor/brotli/enc/compress_fragment.cc +701 -0
  34. data/vendor/brotli/enc/compress_fragment.h +47 -0
  35. data/vendor/brotli/enc/compress_fragment_two_pass.cc +524 -0
  36. data/vendor/brotli/enc/compress_fragment_two_pass.h +40 -0
  37. data/vendor/brotli/enc/compressor.h +15 -0
  38. data/vendor/brotli/enc/context.h +1 -1
  39. data/vendor/brotli/enc/dictionary.h +2 -2
  40. data/vendor/brotli/enc/encode.cc +819 -286
  41. data/vendor/brotli/enc/encode.h +38 -15
  42. data/vendor/brotli/enc/encode_parallel.cc +40 -42
  43. data/vendor/brotli/enc/entropy_encode.cc +144 -147
  44. data/vendor/brotli/enc/entropy_encode.h +32 -8
  45. data/vendor/brotli/enc/entropy_encode_static.h +572 -0
  46. data/vendor/brotli/enc/fast_log.h +7 -40
  47. data/vendor/brotli/enc/find_match_length.h +9 -9
  48. data/vendor/brotli/enc/hash.h +462 -154
  49. data/vendor/brotli/enc/histogram.cc +6 -6
  50. data/vendor/brotli/enc/histogram.h +13 -13
  51. data/vendor/brotli/enc/literal_cost.cc +45 -45
  52. data/vendor/brotli/enc/metablock.cc +92 -89
  53. data/vendor/brotli/enc/metablock.h +12 -12
  54. data/vendor/brotli/enc/port.h +7 -16
  55. data/vendor/brotli/enc/prefix.h +23 -22
  56. data/vendor/brotli/enc/ringbuffer.h +75 -29
  57. data/vendor/brotli/enc/static_dict.cc +56 -48
  58. data/vendor/brotli/enc/static_dict.h +5 -5
  59. data/vendor/brotli/enc/streams.cc +1 -1
  60. data/vendor/brotli/enc/streams.h +5 -5
  61. data/vendor/brotli/enc/transform.h +40 -35
  62. data/vendor/brotli/enc/types.h +2 -0
  63. data/vendor/brotli/enc/utf8_util.cc +3 -2
  64. data/vendor/brotli/enc/write_bits.h +6 -6
  65. metadata +9 -5
  66. data/vendor/brotli/dec/streams.c +0 -102
  67. data/vendor/brotli/dec/streams.h +0 -95
@@ -9,6 +9,8 @@
9
9
  #include "./encode.h"
10
10
 
11
11
  #include <algorithm>
12
+ #include <cstdlib> /* free, malloc */
13
+ #include <cstring> /* memset */
12
14
  #include <limits>
13
15
 
14
16
  #include "./backward_references.h"
@@ -19,6 +21,8 @@
19
21
  #include "./context.h"
20
22
  #include "./metablock.h"
21
23
  #include "./transform.h"
24
+ #include "./compress_fragment.h"
25
+ #include "./compress_fragment_two_pass.h"
22
26
  #include "./entropy_encode.h"
23
27
  #include "./fast_log.h"
24
28
  #include "./hash.h"
@@ -32,20 +36,22 @@ namespace brotli {
32
36
  static const int kMinQualityForBlockSplit = 4;
33
37
  static const int kMinQualityForContextModeling = 5;
34
38
  static const int kMinQualityForOptimizeHistograms = 4;
35
- // For quality 1 there is no block splitting, so we buffer at most this much
39
+ // For quality 2 there is no block splitting, so we buffer at most this much
36
40
  // literals and commands.
37
- static const int kMaxNumDelayedSymbols = 0x2fff;
41
+ static const size_t kMaxNumDelayedSymbols = 0x2fff;
38
42
 
39
- void RecomputeDistancePrefixes(Command* cmds,
40
- size_t num_commands,
41
- int num_direct_distance_codes,
42
- int distance_postfix_bits) {
43
+ #define COPY_ARRAY(dst, src) memcpy(dst, src, sizeof(src));
44
+
45
+ static void RecomputeDistancePrefixes(Command* cmds,
46
+ size_t num_commands,
47
+ uint32_t num_direct_distance_codes,
48
+ uint32_t distance_postfix_bits) {
43
49
  if (num_direct_distance_codes == 0 && distance_postfix_bits == 0) {
44
50
  return;
45
51
  }
46
52
  for (size_t i = 0; i < num_commands; ++i) {
47
53
  Command* cmd = &cmds[i];
48
- if (cmd->copy_len_ > 0 && cmd->cmd_prefix_ >= 128) {
54
+ if (cmd->copy_len() && cmd->cmd_prefix_ >= 128) {
49
55
  PrefixEncodeCopyDistance(cmd->DistanceCode(),
50
56
  num_direct_distance_codes,
51
57
  distance_postfix_bits,
@@ -55,6 +61,16 @@ void RecomputeDistancePrefixes(Command* cmds,
55
61
  }
56
62
  }
57
63
 
64
+ /* Wraps 64-bit input position to 32-bit ringbuffer position preserving
65
+ "not-a-first-lap" feature. */
66
+ static uint32_t WrapPosition(uint64_t position) {
67
+ uint32_t result = static_cast<uint32_t>(position);
68
+ if (position > (1u << 30)) {
69
+ result = (result & ((1u << 30) - 1)) | (1u << 30);
70
+ }
71
+ return result;
72
+ }
73
+
58
74
  uint8_t* BrotliCompressor::GetBrotliStorage(size_t size) {
59
75
  if (storage_size_ < size) {
60
76
  delete[] storage_;
@@ -64,6 +80,352 @@ uint8_t* BrotliCompressor::GetBrotliStorage(size_t size) {
64
80
  return storage_;
65
81
  }
66
82
 
83
+ static size_t MaxHashTableSize(int quality) {
84
+ return quality == 0 ? 1 << 15 : 1 << 17;
85
+ }
86
+
87
+ static size_t HashTableSize(size_t max_table_size, size_t input_size) {
88
+ size_t htsize = 256;
89
+ while (htsize < max_table_size && htsize < input_size) {
90
+ htsize <<= 1;
91
+ }
92
+ return htsize;
93
+ }
94
+
95
+ int* BrotliCompressor::GetHashTable(int quality,
96
+ size_t input_size,
97
+ size_t* table_size) {
98
+ // Use smaller hash table when input.size() is smaller, since we
99
+ // fill the table, incurring O(hash table size) overhead for
100
+ // compression, and if the input is short, we won't need that
101
+ // many hash table entries anyway.
102
+ const size_t max_table_size = MaxHashTableSize(quality);
103
+ assert(max_table_size >= 256);
104
+ size_t htsize = HashTableSize(max_table_size, input_size);
105
+
106
+ int* table;
107
+ if (htsize <= sizeof(small_table_) / sizeof(small_table_[0])) {
108
+ table = small_table_;
109
+ } else {
110
+ if (large_table_ == NULL) {
111
+ large_table_ = new int[max_table_size];
112
+ }
113
+ table = large_table_;
114
+ }
115
+
116
+ *table_size = htsize;
117
+ memset(table, 0, htsize * sizeof(*table));
118
+ return table;
119
+ }
120
+
121
+ static void EncodeWindowBits(int lgwin, uint8_t* last_byte,
122
+ uint8_t* last_byte_bits) {
123
+ if (lgwin == 16) {
124
+ *last_byte = 0;
125
+ *last_byte_bits = 1;
126
+ } else if (lgwin == 17) {
127
+ *last_byte = 1;
128
+ *last_byte_bits = 7;
129
+ } else if (lgwin > 17) {
130
+ *last_byte = static_cast<uint8_t>(((lgwin - 17) << 1) | 1);
131
+ *last_byte_bits = 4;
132
+ } else {
133
+ *last_byte = static_cast<uint8_t>(((lgwin - 8) << 4) | 1);
134
+ *last_byte_bits = 7;
135
+ }
136
+ }
137
+
138
+ // Initializes the command and distance prefix codes for the first block.
139
+ static void InitCommandPrefixCodes(uint8_t cmd_depths[128],
140
+ uint16_t cmd_bits[128],
141
+ uint8_t cmd_code[512],
142
+ size_t* cmd_code_numbits) {
143
+ static const uint8_t kDefaultCommandDepths[128] = {
144
+ 0, 4, 4, 5, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8,
145
+ 0, 0, 0, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7,
146
+ 7, 7, 10, 10, 10, 10, 10, 10, 0, 4, 4, 5, 5, 5, 6, 6,
147
+ 7, 8, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
148
+ 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
149
+ 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4,
150
+ 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 7, 7, 7, 8, 10,
151
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
152
+ };
153
+ static const uint16_t kDefaultCommandBits[128] = {
154
+ 0, 0, 8, 9, 3, 35, 7, 71,
155
+ 39, 103, 23, 47, 175, 111, 239, 31,
156
+ 0, 0, 0, 4, 12, 2, 10, 6,
157
+ 13, 29, 11, 43, 27, 59, 87, 55,
158
+ 15, 79, 319, 831, 191, 703, 447, 959,
159
+ 0, 14, 1, 25, 5, 21, 19, 51,
160
+ 119, 159, 95, 223, 479, 991, 63, 575,
161
+ 127, 639, 383, 895, 255, 767, 511, 1023,
162
+ 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
163
+ 27, 59, 7, 39, 23, 55, 30, 1, 17, 9, 25, 5, 0, 8, 4, 12,
164
+ 2, 10, 6, 21, 13, 29, 3, 19, 11, 15, 47, 31, 95, 63, 127, 255,
165
+ 767, 2815, 1791, 3839, 511, 2559, 1535, 3583, 1023, 3071, 2047, 4095,
166
+ };
167
+ COPY_ARRAY(cmd_depths, kDefaultCommandDepths);
168
+ COPY_ARRAY(cmd_bits, kDefaultCommandBits);
169
+
170
+ // Initialize the pre-compressed form of the command and distance prefix
171
+ // codes.
172
+ static const uint8_t kDefaultCommandCode[] = {
173
+ 0xff, 0x77, 0xd5, 0xbf, 0xe7, 0xde, 0xea, 0x9e, 0x51, 0x5d, 0xde, 0xc6,
174
+ 0x70, 0x57, 0xbc, 0x58, 0x58, 0x58, 0xd8, 0xd8, 0x58, 0xd5, 0xcb, 0x8c,
175
+ 0xea, 0xe0, 0xc3, 0x87, 0x1f, 0x83, 0xc1, 0x60, 0x1c, 0x67, 0xb2, 0xaa,
176
+ 0x06, 0x83, 0xc1, 0x60, 0x30, 0x18, 0xcc, 0xa1, 0xce, 0x88, 0x54, 0x94,
177
+ 0x46, 0xe1, 0xb0, 0xd0, 0x4e, 0xb2, 0xf7, 0x04, 0x00,
178
+ };
179
+ static const int kDefaultCommandCodeNumBits = 448;
180
+ COPY_ARRAY(cmd_code, kDefaultCommandCode);
181
+ *cmd_code_numbits = kDefaultCommandCodeNumBits;
182
+ }
183
+
184
+ // Decide about the context map based on the ability of the prediction
185
+ // ability of the previous byte UTF8-prefix on the next byte. The
186
+ // prediction ability is calculated as shannon entropy. Here we need
187
+ // shannon entropy instead of 'BitsEntropy' since the prefix will be
188
+ // encoded with the remaining 6 bits of the following byte, and
189
+ // BitsEntropy will assume that symbol to be stored alone using Huffman
190
+ // coding.
191
+ static void ChooseContextMap(int quality,
192
+ uint32_t* bigram_histo,
193
+ size_t* num_literal_contexts,
194
+ const uint32_t** literal_context_map) {
195
+ uint32_t monogram_histo[3] = { 0 };
196
+ uint32_t two_prefix_histo[6] = { 0 };
197
+ size_t total = 0;
198
+ for (size_t i = 0; i < 9; ++i) {
199
+ total += bigram_histo[i];
200
+ monogram_histo[i % 3] += bigram_histo[i];
201
+ size_t j = i;
202
+ if (j >= 6) {
203
+ j -= 6;
204
+ }
205
+ two_prefix_histo[j] += bigram_histo[i];
206
+ }
207
+ size_t dummy;
208
+ double entropy1 = ShannonEntropy(monogram_histo, 3, &dummy);
209
+ double entropy2 = (ShannonEntropy(two_prefix_histo, 3, &dummy) +
210
+ ShannonEntropy(two_prefix_histo + 3, 3, &dummy));
211
+ double entropy3 = 0;
212
+ for (size_t k = 0; k < 3; ++k) {
213
+ entropy3 += ShannonEntropy(bigram_histo + 3 * k, 3, &dummy);
214
+ }
215
+
216
+ assert(total != 0);
217
+ double scale = 1.0 / static_cast<double>(total);
218
+ entropy1 *= scale;
219
+ entropy2 *= scale;
220
+ entropy3 *= scale;
221
+
222
+ static const uint32_t kStaticContextMapContinuation[64] = {
223
+ 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
224
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
225
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
226
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
227
+ };
228
+ static const uint32_t kStaticContextMapSimpleUTF8[64] = {
229
+ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
230
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
231
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
232
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
233
+ };
234
+ if (quality < 7) {
235
+ // 3 context models is a bit slower, don't use it at lower qualities.
236
+ entropy3 = entropy1 * 10;
237
+ }
238
+ // If expected savings by symbol are less than 0.2 bits, skip the
239
+ // context modeling -- in exchange for faster decoding speed.
240
+ if (entropy1 - entropy2 < 0.2 &&
241
+ entropy1 - entropy3 < 0.2) {
242
+ *num_literal_contexts = 1;
243
+ } else if (entropy2 - entropy3 < 0.02) {
244
+ *num_literal_contexts = 2;
245
+ *literal_context_map = kStaticContextMapSimpleUTF8;
246
+ } else {
247
+ *num_literal_contexts = 3;
248
+ *literal_context_map = kStaticContextMapContinuation;
249
+ }
250
+ }
251
+
252
+ static void DecideOverLiteralContextModeling(
253
+ const uint8_t* input,
254
+ size_t start_pos,
255
+ size_t length,
256
+ size_t mask,
257
+ int quality,
258
+ ContextType* literal_context_mode,
259
+ size_t* num_literal_contexts,
260
+ const uint32_t** literal_context_map) {
261
+ if (quality < kMinQualityForContextModeling || length < 64) {
262
+ return;
263
+ }
264
+ // Gather bigram data of the UTF8 byte prefixes. To make the analysis of
265
+ // UTF8 data faster we only examine 64 byte long strides at every 4kB
266
+ // intervals.
267
+ const size_t end_pos = start_pos + length;
268
+ uint32_t bigram_prefix_histo[9] = { 0 };
269
+ for (; start_pos + 64 <= end_pos; start_pos += 4096) {
270
+ static const int lut[4] = { 0, 0, 1, 2 };
271
+ const size_t stride_end_pos = start_pos + 64;
272
+ int prev = lut[input[start_pos & mask] >> 6] * 3;
273
+ for (size_t pos = start_pos + 1; pos < stride_end_pos; ++pos) {
274
+ const uint8_t literal = input[pos & mask];
275
+ ++bigram_prefix_histo[prev + lut[literal >> 6]];
276
+ prev = lut[literal >> 6] * 3;
277
+ }
278
+ }
279
+ *literal_context_mode = CONTEXT_UTF8;
280
+ ChooseContextMap(quality, &bigram_prefix_histo[0], num_literal_contexts,
281
+ literal_context_map);
282
+ }
283
+
284
+ static bool ShouldCompress(const uint8_t* data,
285
+ const size_t mask,
286
+ const uint64_t last_flush_pos,
287
+ const size_t bytes,
288
+ const size_t num_literals,
289
+ const size_t num_commands) {
290
+ if (num_commands < (bytes >> 8) + 2) {
291
+ if (num_literals > 0.99 * static_cast<double>(bytes)) {
292
+ uint32_t literal_histo[256] = { 0 };
293
+ static const uint32_t kSampleRate = 13;
294
+ static const double kMinEntropy = 7.92;
295
+ const double bit_cost_threshold =
296
+ static_cast<double>(bytes) * kMinEntropy / kSampleRate;
297
+ size_t t = (bytes + kSampleRate - 1) / kSampleRate;
298
+ uint32_t pos = static_cast<uint32_t>(last_flush_pos);
299
+ for (size_t i = 0; i < t; i++) {
300
+ ++literal_histo[data[pos & mask]];
301
+ pos += kSampleRate;
302
+ }
303
+ if (BitsEntropy(literal_histo, 256) > bit_cost_threshold) {
304
+ return false;
305
+ }
306
+ }
307
+ }
308
+ return true;
309
+ }
310
+
311
+ static void WriteMetaBlockInternal(const uint8_t* data,
312
+ const size_t mask,
313
+ const uint64_t last_flush_pos,
314
+ const size_t bytes,
315
+ const bool is_last,
316
+ const int quality,
317
+ const bool font_mode,
318
+ const uint8_t prev_byte,
319
+ const uint8_t prev_byte2,
320
+ const size_t num_literals,
321
+ const size_t num_commands,
322
+ Command* commands,
323
+ const int* saved_dist_cache,
324
+ int* dist_cache,
325
+ size_t* storage_ix,
326
+ uint8_t* storage) {
327
+ if (bytes == 0) {
328
+ // Write the ISLAST and ISEMPTY bits.
329
+ WriteBits(2, 3, storage_ix, storage);
330
+ *storage_ix = (*storage_ix + 7u) & ~7u;
331
+ return;
332
+ }
333
+
334
+ if (!ShouldCompress(data, mask, last_flush_pos, bytes,
335
+ num_literals, num_commands)) {
336
+ // Restore the distance cache, as its last update by
337
+ // CreateBackwardReferences is now unused.
338
+ memcpy(dist_cache, saved_dist_cache, 4 * sizeof(dist_cache[0]));
339
+ StoreUncompressedMetaBlock(is_last, data,
340
+ WrapPosition(last_flush_pos), mask, bytes,
341
+ storage_ix, storage);
342
+ return;
343
+ }
344
+
345
+ const uint8_t last_byte = storage[0];
346
+ const uint8_t last_byte_bits = static_cast<uint8_t>(*storage_ix & 0xff);
347
+ uint32_t num_direct_distance_codes = 0;
348
+ uint32_t distance_postfix_bits = 0;
349
+ if (quality > 9 && font_mode) {
350
+ num_direct_distance_codes = 12;
351
+ distance_postfix_bits = 1;
352
+ RecomputeDistancePrefixes(commands,
353
+ num_commands,
354
+ num_direct_distance_codes,
355
+ distance_postfix_bits);
356
+ }
357
+ if (quality == 2) {
358
+ StoreMetaBlockFast(data, WrapPosition(last_flush_pos),
359
+ bytes, mask, is_last,
360
+ commands, num_commands,
361
+ storage_ix, storage);
362
+ } else if (quality < kMinQualityForBlockSplit) {
363
+ StoreMetaBlockTrivial(data, WrapPosition(last_flush_pos),
364
+ bytes, mask, is_last,
365
+ commands, num_commands,
366
+ storage_ix, storage);
367
+ } else {
368
+ MetaBlockSplit mb;
369
+ ContextType literal_context_mode = CONTEXT_UTF8;
370
+ if (quality <= 9) {
371
+ size_t num_literal_contexts = 1;
372
+ const uint32_t* literal_context_map = NULL;
373
+ DecideOverLiteralContextModeling(data, WrapPosition(last_flush_pos),
374
+ bytes, mask,
375
+ quality,
376
+ &literal_context_mode,
377
+ &num_literal_contexts,
378
+ &literal_context_map);
379
+ if (literal_context_map == NULL) {
380
+ BuildMetaBlockGreedy(data, WrapPosition(last_flush_pos), mask,
381
+ commands, num_commands, &mb);
382
+ } else {
383
+ BuildMetaBlockGreedyWithContexts(data, WrapPosition(last_flush_pos),
384
+ mask,
385
+ prev_byte, prev_byte2,
386
+ literal_context_mode,
387
+ num_literal_contexts,
388
+ literal_context_map,
389
+ commands, num_commands,
390
+ &mb);
391
+ }
392
+ } else {
393
+ if (!IsMostlyUTF8(data, WrapPosition(last_flush_pos), mask, bytes,
394
+ kMinUTF8Ratio)) {
395
+ literal_context_mode = CONTEXT_SIGNED;
396
+ }
397
+ BuildMetaBlock(data, WrapPosition(last_flush_pos), mask,
398
+ prev_byte, prev_byte2,
399
+ commands, num_commands,
400
+ literal_context_mode,
401
+ &mb);
402
+ }
403
+ if (quality >= kMinQualityForOptimizeHistograms) {
404
+ OptimizeHistograms(num_direct_distance_codes,
405
+ distance_postfix_bits,
406
+ &mb);
407
+ }
408
+ StoreMetaBlock(data, WrapPosition(last_flush_pos), bytes, mask,
409
+ prev_byte, prev_byte2,
410
+ is_last,
411
+ num_direct_distance_codes,
412
+ distance_postfix_bits,
413
+ literal_context_mode,
414
+ commands, num_commands,
415
+ mb,
416
+ storage_ix, storage);
417
+ }
418
+ if (bytes + 4 < (*storage_ix >> 3)) {
419
+ // Restore the distance cache and last byte.
420
+ memcpy(dist_cache, saved_dist_cache, 4 * sizeof(dist_cache[0]));
421
+ storage[0] = last_byte;
422
+ *storage_ix = last_byte_bits;
423
+ StoreUncompressedMetaBlock(is_last, data,
424
+ WrapPosition(last_flush_pos), mask,
425
+ bytes, storage_ix, storage);
426
+ }
427
+ }
428
+
67
429
  BrotliCompressor::BrotliCompressor(BrotliParams params)
68
430
  : params_(params),
69
431
  hashers_(new Hashers()),
@@ -76,27 +438,33 @@ BrotliCompressor::BrotliCompressor(BrotliParams params)
76
438
  prev_byte_(0),
77
439
  prev_byte2_(0),
78
440
  storage_size_(0),
79
- storage_(0) {
441
+ storage_(0),
442
+ large_table_(NULL),
443
+ cmd_code_numbits_(0),
444
+ command_buf_(NULL),
445
+ literal_buf_(NULL),
446
+ is_last_block_emitted_(0) {
80
447
  // Sanitize params.
81
- params_.quality = std::max(1, params_.quality);
448
+ params_.quality = std::max(0, params_.quality);
82
449
  if (params_.lgwin < kMinWindowBits) {
83
450
  params_.lgwin = kMinWindowBits;
84
451
  } else if (params_.lgwin > kMaxWindowBits) {
85
452
  params_.lgwin = kMaxWindowBits;
86
453
  }
87
- if (params_.lgblock == 0) {
88
- params_.lgblock = params_.quality < kMinQualityForBlockSplit ? 14 : 16;
454
+ if (params_.quality <= 1) {
455
+ params_.lgblock = params_.lgwin;
456
+ } else if (params_.quality < kMinQualityForBlockSplit) {
457
+ params_.lgblock = 14;
458
+ } else if (params_.lgblock == 0) {
459
+ params_.lgblock = 16;
89
460
  if (params_.quality >= 9 && params_.lgwin > params_.lgblock) {
90
- params_.lgblock = std::min(21, params_.lgwin);
461
+ params_.lgblock = std::min(18, params_.lgwin);
91
462
  }
92
463
  } else {
93
464
  params_.lgblock = std::min(kMaxInputBlockBits,
94
465
  std::max(kMinInputBlockBits, params_.lgblock));
95
466
  }
96
467
 
97
- // Set maximum distance, see section 9.1. of the spec.
98
- max_backward_distance_ = (1 << params_.lgwin) - 16;
99
-
100
468
  // Initialize input and literal cost ring buffers.
101
469
  // We allocate at least lgwin + 1 bits for the ring buffer so that the newly
102
470
  // added block fits there completely and we still get lgwin bits and at least
@@ -109,19 +477,7 @@ BrotliCompressor::BrotliCompressor(BrotliParams params)
109
477
  cmd_alloc_size_ = 0;
110
478
 
111
479
  // Initialize last byte with stream header.
112
- if (params_.lgwin == 16) {
113
- last_byte_ = 0;
114
- last_byte_bits_ = 1;
115
- } else if (params_.lgwin == 17) {
116
- last_byte_ = 1;
117
- last_byte_bits_ = 7;
118
- } else if (params_.lgwin > 17) {
119
- last_byte_ = static_cast<uint8_t>(((params_.lgwin - 17) << 1) | 1);
120
- last_byte_bits_ = 4;
121
- } else {
122
- last_byte_ = static_cast<uint8_t>(((params_.lgwin - 8) << 4) | 1);
123
- last_byte_bits_ = 7;
124
- }
480
+ EncodeWindowBits(params_.lgwin, &last_byte_, &last_byte_bits_);
125
481
 
126
482
  // Initialize distance cache.
127
483
  dist_cache_[0] = 4;
@@ -132,16 +488,27 @@ BrotliCompressor::BrotliCompressor(BrotliParams params)
132
488
  // emitting an uncompressed block.
133
489
  memcpy(saved_dist_cache_, dist_cache_, sizeof(dist_cache_));
134
490
 
491
+ if (params_.quality == 0) {
492
+ InitCommandPrefixCodes(cmd_depths_, cmd_bits_,
493
+ cmd_code_, &cmd_code_numbits_);
494
+ } else if (params_.quality == 1) {
495
+ command_buf_ = new uint32_t[kCompressFragmentTwoPassBlockSize];
496
+ literal_buf_ = new uint8_t[kCompressFragmentTwoPassBlockSize];
497
+ }
498
+
135
499
  // Initialize hashers.
136
- hash_type_ = std::min(9, params_.quality);
500
+ hash_type_ = std::min(10, params_.quality);
137
501
  hashers_->Init(hash_type_);
138
502
  }
139
503
 
140
- BrotliCompressor::~BrotliCompressor() {
504
+ BrotliCompressor::~BrotliCompressor(void) {
141
505
  delete[] storage_;
142
506
  free(commands_);
143
507
  delete ringbuffer_;
144
508
  delete hashers_;
509
+ delete[] large_table_;
510
+ delete[] command_buf_;
511
+ delete[] literal_buf_;
145
512
  }
146
513
 
147
514
  void BrotliCompressor::CopyInputToRingBuffer(const size_t input_size,
@@ -206,35 +573,78 @@ void BrotliCompressor::BrotliSetCustomDictionary(
206
573
  if (size > 1) {
207
574
  prev_byte2_ = dict[size - 2];
208
575
  }
209
- hashers_->PrependCustomDictionary(hash_type_, size, dict);
576
+ hashers_->PrependCustomDictionary(hash_type_, params_.lgwin, size, dict);
210
577
  }
211
578
 
212
579
  bool BrotliCompressor::WriteBrotliData(const bool is_last,
213
580
  const bool force_flush,
214
581
  size_t* out_size,
215
582
  uint8_t** output) {
216
- const size_t bytes = input_pos_ - last_processed_pos_;
583
+ const uint64_t delta = input_pos_ - last_processed_pos_;
217
584
  const uint8_t* data = ringbuffer_->start();
218
- const size_t mask = ringbuffer_->mask();
585
+ const uint32_t mask = ringbuffer_->mask();
586
+
587
+ /* Adding more blocks after "last" block is forbidden. */
588
+ if (is_last_block_emitted_) return false;
589
+ if (is_last) is_last_block_emitted_ = 1;
219
590
 
220
- if (bytes > input_block_size()) {
591
+ if (delta > input_block_size()) {
221
592
  return false;
222
593
  }
594
+ const uint32_t bytes = static_cast<uint32_t>(delta);
595
+
596
+ if (params_.quality <= 1) {
597
+ if (delta == 0 && !is_last) {
598
+ // We have no new input data and we don't have to finish the stream, so
599
+ // nothing to do.
600
+ *out_size = 0;
601
+ return true;
602
+ }
603
+ const size_t max_out_size = 2 * bytes + 500;
604
+ uint8_t* storage = GetBrotliStorage(max_out_size);
605
+ storage[0] = last_byte_;
606
+ size_t storage_ix = last_byte_bits_;
607
+ size_t table_size;
608
+ int* table = GetHashTable(params_.quality, bytes, &table_size);
609
+ if (params_.quality == 0) {
610
+ BrotliCompressFragmentFast(
611
+ &data[WrapPosition(last_processed_pos_) & mask],
612
+ bytes, is_last,
613
+ table, table_size,
614
+ cmd_depths_, cmd_bits_,
615
+ &cmd_code_numbits_, cmd_code_,
616
+ &storage_ix, storage);
617
+ } else {
618
+ BrotliCompressFragmentTwoPass(
619
+ &data[WrapPosition(last_processed_pos_) & mask],
620
+ bytes, is_last,
621
+ command_buf_, literal_buf_,
622
+ table, table_size,
623
+ &storage_ix, storage);
624
+ }
625
+ last_byte_ = storage[storage_ix >> 3];
626
+ last_byte_bits_ = storage_ix & 7u;
627
+ last_processed_pos_ = input_pos_;
628
+ *output = &storage[0];
629
+ *out_size = storage_ix >> 3;
630
+ return true;
631
+ }
223
632
 
224
633
  // Theoretical max number of commands is 1 per 2 bytes.
225
634
  size_t newsize = num_commands_ + bytes / 2 + 1;
226
635
  if (newsize > cmd_alloc_size_) {
227
636
  // Reserve a bit more memory to allow merging with a next block
228
637
  // without realloc: that would impact speed.
229
- newsize += bytes / 4;
638
+ newsize += (bytes / 4) + 16;
230
639
  cmd_alloc_size_ = newsize;
231
640
  commands_ =
232
641
  static_cast<Command*>(realloc(commands_, sizeof(Command) * newsize));
233
642
  }
234
643
 
235
- CreateBackwardReferences(bytes, last_processed_pos_, data, mask,
236
- max_backward_distance_,
644
+ CreateBackwardReferences(bytes, WrapPosition(last_processed_pos_),
645
+ is_last, data, mask,
237
646
  params_.quality,
647
+ params_.lgwin,
238
648
  hashers_,
239
649
  hash_type_,
240
650
  dist_cache_,
@@ -244,9 +654,13 @@ bool BrotliCompressor::WriteBrotliData(const bool is_last,
244
654
  &num_literals_);
245
655
 
246
656
  size_t max_length = std::min<size_t>(mask + 1, 1u << kMaxInputBlockBits);
657
+ const size_t max_literals = max_length / 8;
658
+ const size_t max_commands = max_length / 8;
247
659
  if (!is_last && !force_flush &&
248
660
  (params_.quality >= kMinQualityForBlockSplit ||
249
661
  (num_literals_ + num_commands_ < kMaxNumDelayedSymbols)) &&
662
+ num_literals_ < max_literals &&
663
+ num_commands_ < max_commands &&
250
664
  input_pos_ + input_block_size() <= last_flush_pos_ + max_length) {
251
665
  // Merge with next input block. Everything will happen later.
252
666
  last_processed_pos_ = input_pos_;
@@ -262,237 +676,36 @@ bool BrotliCompressor::WriteBrotliData(const bool is_last,
262
676
  last_insert_len_ = 0;
263
677
  }
264
678
 
265
- return WriteMetaBlockInternal(is_last, out_size, output);
266
- }
267
-
268
- // Decide about the context map based on the ability of the prediction
269
- // ability of the previous byte UTF8-prefix on the next byte. The
270
- // prediction ability is calculated as shannon entropy. Here we need
271
- // shannon entropy instead of 'BitsEntropy' since the prefix will be
272
- // encoded with the remaining 6 bits of the following byte, and
273
- // BitsEntropy will assume that symbol to be stored alone using Huffman
274
- // coding.
275
- void ChooseContextMap(int quality,
276
- int* bigram_histo,
277
- int* num_literal_contexts,
278
- const int** literal_context_map) {
279
- int monogram_histo[3] = { 0 };
280
- int two_prefix_histo[6] = { 0 };
281
- int total = 0;
282
- for (int i = 0; i < 9; ++i) {
283
- total += bigram_histo[i];
284
- monogram_histo[i % 3] += bigram_histo[i];
285
- int j = i;
286
- if (j >= 6) {
287
- j -= 6;
288
- }
289
- two_prefix_histo[j] += bigram_histo[i];
290
- }
291
- int dummy;
292
- double entropy1 = ShannonEntropy(monogram_histo, 3, &dummy);
293
- double entropy2 = (ShannonEntropy(two_prefix_histo, 3, &dummy) +
294
- ShannonEntropy(two_prefix_histo + 3, 3, &dummy));
295
- double entropy3 = 0;
296
- for (int k = 0; k < 3; ++k) {
297
- entropy3 += ShannonEntropy(bigram_histo + 3 * k, 3, &dummy);
298
- }
299
-
300
- assert(total != 0);
301
- entropy1 *= (1.0 / total);
302
- entropy2 *= (1.0 / total);
303
- entropy3 *= (1.0 / total);
304
-
305
- static const int kStaticContextMapContinuation[64] = {
306
- 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
307
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
308
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
309
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
310
- };
311
- static const int kStaticContextMapSimpleUTF8[64] = {
312
- 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
313
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
314
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
315
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
316
- };
317
- if (quality < 7) {
318
- // 3 context models is a bit slower, don't use it at lower qualities.
319
- entropy3 = entropy1 * 10;
320
- }
321
- // If expected savings by symbol are less than 0.2 bits, skip the
322
- // context modeling -- in exchange for faster decoding speed.
323
- if (entropy1 - entropy2 < 0.2 &&
324
- entropy1 - entropy3 < 0.2) {
325
- *num_literal_contexts = 1;
326
- } else if (entropy2 - entropy3 < 0.02) {
327
- *num_literal_contexts = 2;
328
- *literal_context_map = kStaticContextMapSimpleUTF8;
329
- } else {
330
- *num_literal_contexts = 3;
331
- *literal_context_map = kStaticContextMapContinuation;
332
- }
333
- }
334
-
335
- void DecideOverLiteralContextModeling(const uint8_t* input,
336
- size_t start_pos,
337
- size_t length,
338
- size_t mask,
339
- int quality,
340
- int* literal_context_mode,
341
- int* num_literal_contexts,
342
- const int** literal_context_map) {
343
- if (quality < kMinQualityForContextModeling || length < 64) {
344
- return;
345
- }
346
- // Gather bigram data of the UTF8 byte prefixes. To make the analysis of
347
- // UTF8 data faster we only examine 64 byte long strides at every 4kB
348
- // intervals.
349
- const size_t end_pos = start_pos + length;
350
- int bigram_prefix_histo[9] = { 0 };
351
- for (; start_pos + 64 <= end_pos; start_pos += 4096) {
352
- static const int lut[4] = { 0, 0, 1, 2 };
353
- const size_t stride_end_pos = start_pos + 64;
354
- int prev = lut[input[start_pos & mask] >> 6] * 3;
355
- for (size_t pos = start_pos + 1; pos < stride_end_pos; ++pos) {
356
- const uint8_t literal = input[pos & mask];
357
- ++bigram_prefix_histo[prev + lut[literal >> 6]];
358
- prev = lut[literal >> 6] * 3;
359
- }
679
+ if (!is_last && input_pos_ == last_flush_pos_) {
680
+ // We have no new input data and we don't have to finish the stream, so
681
+ // nothing to do.
682
+ *out_size = 0;
683
+ return true;
360
684
  }
361
- *literal_context_mode = CONTEXT_UTF8;
362
- ChooseContextMap(quality, &bigram_prefix_histo[0], num_literal_contexts,
363
- literal_context_map);
364
- }
365
-
366
- bool BrotliCompressor::WriteMetaBlockInternal(const bool is_last,
367
- size_t* out_size,
368
- uint8_t** output) {
369
- const size_t bytes = input_pos_ - last_flush_pos_;
370
- const uint8_t* data = ringbuffer_->start();
371
- const size_t mask = ringbuffer_->mask();
372
- const size_t max_out_size = 2 * bytes + 500;
685
+ assert(input_pos_ >= last_flush_pos_);
686
+ assert(input_pos_ > last_flush_pos_ || is_last);
687
+ assert(input_pos_ - last_flush_pos_ <= 1u << 24);
688
+ const uint32_t metablock_size =
689
+ static_cast<uint32_t>(input_pos_ - last_flush_pos_);
690
+ const size_t max_out_size = 2 * metablock_size + 500;
373
691
  uint8_t* storage = GetBrotliStorage(max_out_size);
374
692
  storage[0] = last_byte_;
375
- int storage_ix = last_byte_bits_;
376
-
377
- bool uncompressed = false;
378
- if (num_commands_ < (bytes >> 8) + 2) {
379
- if (num_literals_ > 0.99 * static_cast<double>(bytes)) {
380
- int literal_histo[256] = { 0 };
381
- static const int kSampleRate = 13;
382
- static const double kMinEntropy = 7.92;
383
- const double bit_cost_threshold =
384
- static_cast<double>(bytes) * kMinEntropy / kSampleRate;
385
- for (size_t i = last_flush_pos_; i < input_pos_; i += kSampleRate) {
386
- ++literal_histo[data[i & mask]];
387
- }
388
- if (BitsEntropy(literal_histo, 256) > bit_cost_threshold) {
389
- uncompressed = true;
390
- }
391
- }
392
- }
393
-
394
- if (bytes == 0) {
395
- if (!StoreCompressedMetaBlockHeader(is_last, 0, &storage_ix, &storage[0])) {
396
- return false;
397
- }
398
- storage_ix = (storage_ix + 7) & ~7;
399
- } else if (uncompressed) {
400
- // Restore the distance cache, as its last update by
401
- // CreateBackwardReferences is now unused.
402
- memcpy(dist_cache_, saved_dist_cache_, sizeof(dist_cache_));
403
- if (!StoreUncompressedMetaBlock(is_last,
404
- data, last_flush_pos_, mask, bytes,
405
- &storage_ix,
406
- &storage[0])) {
407
- return false;
408
- }
409
- } else {
410
- int num_direct_distance_codes = 0;
411
- int distance_postfix_bits = 0;
412
- if (params_.quality > 9 && params_.mode == BrotliParams::MODE_FONT) {
413
- num_direct_distance_codes = 12;
414
- distance_postfix_bits = 1;
415
- RecomputeDistancePrefixes(commands_,
416
- num_commands_,
417
- num_direct_distance_codes,
418
- distance_postfix_bits);
419
- }
420
- if (params_.quality < kMinQualityForBlockSplit) {
421
- if (!StoreMetaBlockTrivial(data, last_flush_pos_, bytes, mask, is_last,
422
- commands_, num_commands_,
423
- &storage_ix,
424
- &storage[0])) {
425
- return false;
426
- }
427
- } else {
428
- MetaBlockSplit mb;
429
- int literal_context_mode = CONTEXT_UTF8;
430
- if (params_.quality <= 9) {
431
- int num_literal_contexts = 1;
432
- const int* literal_context_map = NULL;
433
- DecideOverLiteralContextModeling(data, last_flush_pos_, bytes, mask,
434
- params_.quality,
435
- &literal_context_mode,
436
- &num_literal_contexts,
437
- &literal_context_map);
438
- if (literal_context_map == NULL) {
439
- BuildMetaBlockGreedy(data, last_flush_pos_, mask,
440
- commands_, num_commands_,
441
- &mb);
442
- } else {
443
- BuildMetaBlockGreedyWithContexts(data, last_flush_pos_, mask,
444
- prev_byte_, prev_byte2_,
445
- literal_context_mode,
446
- num_literal_contexts,
447
- literal_context_map,
448
- commands_, num_commands_,
449
- &mb);
450
- }
451
- } else {
452
- if (!IsMostlyUTF8(data, last_flush_pos_, mask, bytes, kMinUTF8Ratio)) {
453
- literal_context_mode = CONTEXT_SIGNED;
454
- }
455
- BuildMetaBlock(data, last_flush_pos_, mask,
456
- prev_byte_, prev_byte2_,
457
- commands_, num_commands_,
458
- literal_context_mode,
459
- &mb);
460
- }
461
- if (params_.quality >= kMinQualityForOptimizeHistograms) {
462
- OptimizeHistograms(num_direct_distance_codes,
463
- distance_postfix_bits,
464
- &mb);
465
- }
466
- if (!StoreMetaBlock(data, last_flush_pos_, bytes, mask,
467
- prev_byte_, prev_byte2_,
468
- is_last,
469
- num_direct_distance_codes,
470
- distance_postfix_bits,
471
- literal_context_mode,
472
- commands_, num_commands_,
473
- mb,
474
- &storage_ix,
475
- &storage[0])) {
476
- return false;
477
- }
478
- }
479
- if (bytes + 4 < static_cast<size_t>(storage_ix >> 3)) {
480
- // Restore the distance cache and last byte.
481
- memcpy(dist_cache_, saved_dist_cache_, sizeof(dist_cache_));
482
- storage[0] = last_byte_;
483
- storage_ix = last_byte_bits_;
484
- if (!StoreUncompressedMetaBlock(is_last, data, last_flush_pos_, mask,
485
- bytes, &storage_ix, &storage[0])) {
486
- return false;
487
- }
488
- }
489
- }
693
+ size_t storage_ix = last_byte_bits_;
694
+ bool font_mode = params_.mode == BrotliParams::MODE_FONT;
695
+ WriteMetaBlockInternal(
696
+ data, mask, last_flush_pos_, metablock_size, is_last, params_.quality,
697
+ font_mode, prev_byte_, prev_byte2_, num_literals_, num_commands_,
698
+ commands_, saved_dist_cache_, dist_cache_, &storage_ix, storage);
490
699
  last_byte_ = storage[storage_ix >> 3];
491
- last_byte_bits_ = storage_ix & 7;
700
+ last_byte_bits_ = storage_ix & 7u;
492
701
  last_flush_pos_ = input_pos_;
493
702
  last_processed_pos_ = input_pos_;
494
- prev_byte_ = data[(last_flush_pos_ - 1) & mask];
495
- prev_byte2_ = data[(last_flush_pos_ - 2) & mask];
703
+ if (last_flush_pos_ > 0) {
704
+ prev_byte_ = data[(static_cast<uint32_t>(last_flush_pos_) - 1) & mask];
705
+ }
706
+ if (last_flush_pos_ > 1) {
707
+ prev_byte2_ = data[(static_cast<uint32_t>(last_flush_pos_) - 2) & mask];
708
+ }
496
709
  num_commands_ = 0;
497
710
  num_literals_ = 0;
498
711
  // Save the state of the distance cache in case we need to restore it for
@@ -532,21 +745,22 @@ bool BrotliCompressor::WriteMetadata(const size_t input_size,
532
745
  }
533
746
  uint64_t hdr_buffer_data[2];
534
747
  uint8_t* hdr_buffer = reinterpret_cast<uint8_t*>(&hdr_buffer_data[0]);
535
- int storage_ix = last_byte_bits_;
748
+ size_t storage_ix = last_byte_bits_;
536
749
  hdr_buffer[0] = last_byte_;
537
750
  WriteBits(1, 0, &storage_ix, hdr_buffer);
538
751
  WriteBits(2, 3, &storage_ix, hdr_buffer);
539
752
  WriteBits(1, 0, &storage_ix, hdr_buffer);
540
753
  if (input_size == 0) {
541
754
  WriteBits(2, 0, &storage_ix, hdr_buffer);
542
- *encoded_size = (storage_ix + 7) >> 3;
755
+ *encoded_size = (storage_ix + 7u) >> 3;
543
756
  memcpy(encoded_buffer, hdr_buffer, *encoded_size);
544
757
  } else {
545
- int nbits = Log2Floor(static_cast<uint32_t>(input_size) - 1) + 1;
546
- int nbytes = (nbits + 7) / 8;
758
+ uint32_t nbits = (input_size == 1) ? 0 : (Log2FloorNonZero(
759
+ static_cast<uint32_t>(input_size) - 1) + 1);
760
+ uint32_t nbytes = (nbits + 7) / 8;
547
761
  WriteBits(2, nbytes, &storage_ix, hdr_buffer);
548
762
  WriteBits(8 * nbytes, input_size - 1, &storage_ix, hdr_buffer);
549
- size_t hdr_size = (storage_ix + 7) >> 3;
763
+ size_t hdr_size = (storage_ix + 7u) >> 3;
550
764
  memcpy(encoded_buffer, hdr_buffer, hdr_size);
551
765
  memcpy(&encoded_buffer[hdr_size], input_buffer, input_size);
552
766
  *encoded_size = hdr_size + input_size;
@@ -564,6 +778,177 @@ bool BrotliCompressor::FinishStream(
564
778
  return WriteMetaBlock(0, NULL, true, encoded_size, encoded_buffer);
565
779
  }
566
780
 
781
+ static int BrotliCompressBufferQuality10(int lgwin,
782
+ size_t input_size,
783
+ const uint8_t* input_buffer,
784
+ size_t* encoded_size,
785
+ uint8_t* encoded_buffer) {
786
+ const size_t mask = std::numeric_limits<size_t>::max() >> 1;
787
+ assert(input_size <= mask + 1);
788
+ const size_t max_backward_limit = (1 << lgwin) - 16;
789
+ int dist_cache[4] = { 4, 11, 15, 16 };
790
+ int saved_dist_cache[4] = { 4, 11, 15, 16 };
791
+ int ok = 1;
792
+ const size_t max_out_size = *encoded_size;
793
+ size_t total_out_size = 0;
794
+ uint8_t last_byte;
795
+ uint8_t last_byte_bits;
796
+ EncodeWindowBits(lgwin, &last_byte, &last_byte_bits);
797
+
798
+ Hashers::H10* hasher = new Hashers::H10;
799
+ const size_t hasher_eff_size = std::min(input_size, max_backward_limit + 16);
800
+ hasher->Init(lgwin, 0, hasher_eff_size, true);
801
+
802
+ const int lgblock = std::min(18, lgwin);
803
+ const int lgmetablock = std::min(24, lgwin + 1);
804
+ const size_t max_block_size = static_cast<size_t>(1) << lgblock;
805
+ const size_t max_metablock_size = static_cast<size_t>(1) << lgmetablock;
806
+ const size_t max_literals_per_metablock = max_metablock_size / 8;
807
+ const size_t max_commands_per_metablock = max_metablock_size / 8;
808
+ size_t metablock_start = 0;
809
+ uint8_t prev_byte = 0;
810
+ uint8_t prev_byte2 = 0;
811
+ while (ok && metablock_start < input_size) {
812
+ const size_t metablock_end =
813
+ std::min(input_size, metablock_start + max_metablock_size);
814
+ const size_t expected_num_commands =
815
+ (metablock_end - metablock_start) / 12 + 16;
816
+ Command* commands = 0;
817
+ size_t num_commands = 0;
818
+ size_t last_insert_len = 0;
819
+ size_t num_literals = 0;
820
+ size_t metablock_size = 0;
821
+ size_t cmd_alloc_size = 0;
822
+
823
+ for (size_t block_start = metablock_start; block_start < metablock_end; ) {
824
+ size_t block_size = std::min(metablock_end - block_start, max_block_size);
825
+ ZopfliNode* nodes = new ZopfliNode[block_size + 1];
826
+ std::vector<uint32_t> path;
827
+ hasher->StitchToPreviousBlock(block_size, block_start,
828
+ input_buffer, mask);
829
+ ZopfliComputeShortestPath(block_size, block_start, input_buffer, mask,
830
+ max_backward_limit, dist_cache,
831
+ hasher, nodes, &path);
832
+ // We allocate a command buffer in the first iteration of this loop that
833
+ // will be likely big enough for the whole metablock, so that for most
834
+ // inputs we will not have to reallocate in later iterations. We do the
835
+ // allocation here and not before the loop, because if the input is small,
836
+ // this will be allocated after the zopfli cost model is freed, so this
837
+ // will not increase peak memory usage.
838
+ // TODO: If the first allocation is too small, increase command
839
+ // buffer size exponentially.
840
+ size_t new_cmd_alloc_size = std::max(expected_num_commands,
841
+ num_commands + path.size() + 1);
842
+ if (cmd_alloc_size != new_cmd_alloc_size) {
843
+ cmd_alloc_size = new_cmd_alloc_size;
844
+ commands = static_cast<Command*>(
845
+ realloc(commands, cmd_alloc_size * sizeof(Command)));
846
+ }
847
+ ZopfliCreateCommands(block_size, block_start, max_backward_limit, path,
848
+ &nodes[0], dist_cache, &last_insert_len,
849
+ &commands[num_commands], &num_literals);
850
+ num_commands += path.size();
851
+ block_start += block_size;
852
+ metablock_size += block_size;
853
+ delete[] nodes;
854
+ if (num_literals > max_literals_per_metablock ||
855
+ num_commands > max_commands_per_metablock) {
856
+ break;
857
+ }
858
+ }
859
+
860
+ if (last_insert_len > 0) {
861
+ Command cmd(last_insert_len);
862
+ commands[num_commands++] = cmd;
863
+ num_literals += last_insert_len;
864
+ }
865
+
866
+ const bool is_last = (metablock_start + metablock_size == input_size);
867
+ uint8_t* storage = NULL;
868
+ size_t storage_ix = last_byte_bits;
869
+
870
+ if (metablock_size == 0) {
871
+ // Write the ISLAST and ISEMPTY bits.
872
+ storage = new uint8_t[16];
873
+ storage[0] = last_byte;
874
+ WriteBits(2, 3, &storage_ix, storage);
875
+ storage_ix = (storage_ix + 7u) & ~7u;
876
+ } else if (!ShouldCompress(input_buffer, mask, metablock_start,
877
+ metablock_size, num_literals, num_commands)) {
878
+ // Restore the distance cache, as its last update by
879
+ // CreateBackwardReferences is now unused.
880
+ memcpy(dist_cache, saved_dist_cache, 4 * sizeof(dist_cache[0]));
881
+ storage = new uint8_t[metablock_size + 16];
882
+ storage[0] = last_byte;
883
+ StoreUncompressedMetaBlock(is_last, input_buffer,
884
+ metablock_start, mask, metablock_size,
885
+ &storage_ix, storage);
886
+ } else {
887
+ uint32_t num_direct_distance_codes = 0;
888
+ uint32_t distance_postfix_bits = 0;
889
+ MetaBlockSplit mb;
890
+ ContextType literal_context_mode = CONTEXT_UTF8;
891
+ if (!IsMostlyUTF8(
892
+ input_buffer, metablock_start, mask, metablock_size,
893
+ kMinUTF8Ratio)) {
894
+ literal_context_mode = CONTEXT_SIGNED;
895
+ }
896
+ BuildMetaBlock(input_buffer, metablock_start, mask,
897
+ prev_byte, prev_byte2,
898
+ commands, num_commands,
899
+ literal_context_mode,
900
+ &mb);
901
+ OptimizeHistograms(num_direct_distance_codes,
902
+ distance_postfix_bits,
903
+ &mb);
904
+ const size_t max_out_metablock_size = 2 * metablock_size + 500;
905
+ storage = new uint8_t[max_out_metablock_size];
906
+ storage[0] = last_byte;
907
+ StoreMetaBlock(input_buffer, metablock_start, metablock_size, mask,
908
+ prev_byte, prev_byte2,
909
+ is_last,
910
+ num_direct_distance_codes,
911
+ distance_postfix_bits,
912
+ literal_context_mode,
913
+ commands, num_commands,
914
+ mb,
915
+ &storage_ix, storage);
916
+ if (metablock_size + 4 < (storage_ix >> 3)) {
917
+ // Restore the distance cache and last byte.
918
+ memcpy(dist_cache, saved_dist_cache, 4 * sizeof(dist_cache[0]));
919
+ storage[0] = last_byte;
920
+ storage_ix = last_byte_bits;
921
+ StoreUncompressedMetaBlock(is_last, input_buffer,
922
+ metablock_start, mask,
923
+ metablock_size, &storage_ix, storage);
924
+ }
925
+ }
926
+ last_byte = storage[storage_ix >> 3];
927
+ last_byte_bits = storage_ix & 7u;
928
+ metablock_start += metablock_size;
929
+ prev_byte = input_buffer[metablock_start - 1];
930
+ prev_byte2 = input_buffer[metablock_start - 2];
931
+ // Save the state of the distance cache in case we need to restore it for
932
+ // emitting an uncompressed block.
933
+ memcpy(saved_dist_cache, dist_cache, 4 * sizeof(dist_cache[0]));
934
+
935
+ const size_t out_size = storage_ix >> 3;
936
+ total_out_size += out_size;
937
+ if (total_out_size <= max_out_size) {
938
+ memcpy(encoded_buffer, storage, out_size);
939
+ encoded_buffer += out_size;
940
+ } else {
941
+ ok = 0;
942
+ }
943
+ delete[] storage;
944
+ free(commands);
945
+ }
946
+
947
+ *encoded_size = total_out_size;
948
+ delete hasher;
949
+ return ok;
950
+ }
951
+
567
952
  int BrotliCompressBuffer(BrotliParams params,
568
953
  size_t input_size,
569
954
  const uint8_t* input_buffer,
@@ -573,6 +958,18 @@ int BrotliCompressBuffer(BrotliParams params,
573
958
  // Output buffer needs at least one byte.
574
959
  return 0;
575
960
  }
961
+ if (input_size == 0) {
962
+ // Handle the special case of empty input.
963
+ *encoded_size = 1;
964
+ *encoded_buffer = 6;
965
+ return 1;
966
+ }
967
+ if (params.quality == 10) {
968
+ // TODO: Implement this direct path for all quality levels.
969
+ const int lgwin = std::min(24, std::max(16, params.lgwin));
970
+ return BrotliCompressBufferQuality10(lgwin, input_size, input_buffer,
971
+ encoded_size, encoded_buffer);
972
+ }
576
973
  BrotliMemIn in(input_buffer, input_size);
577
974
  BrotliMemOut out(encoded_buffer, *encoded_size);
578
975
  if (!BrotliCompress(params, &in, &out)) {
@@ -582,54 +979,190 @@ int BrotliCompressBuffer(BrotliParams params,
582
979
  return 1;
583
980
  }
584
981
 
585
- size_t CopyOneBlockToRingBuffer(BrotliIn* r, BrotliCompressor* compressor) {
586
- const size_t block_size = compressor->input_block_size();
587
- size_t bytes_read = 0;
982
+ static bool BrotliInIsFinished(BrotliIn* r) {
983
+ size_t read_bytes;
984
+ return r->Read(0, &read_bytes) == NULL;
985
+ }
986
+
987
+ static const uint8_t* BrotliInReadAndCheckEnd(const size_t block_size,
988
+ BrotliIn* r,
989
+ size_t* bytes_read,
990
+ bool* is_last) {
991
+ *bytes_read = 0;
588
992
  const uint8_t* data = reinterpret_cast<const uint8_t*>(
589
- r->Read(block_size, &bytes_read));
993
+ r->Read(block_size, bytes_read));
994
+ assert((data == NULL) == (*bytes_read == 0));
995
+ *is_last = BrotliInIsFinished(r);
996
+ return data;
997
+ }
998
+
999
+ static bool CopyOneBlockToRingBuffer(BrotliIn* r,
1000
+ BrotliCompressor* compressor,
1001
+ size_t* bytes_read,
1002
+ bool* is_last) {
1003
+ const size_t block_size = compressor->input_block_size();
1004
+ const uint8_t* data = BrotliInReadAndCheckEnd(block_size, r,
1005
+ bytes_read, is_last);
590
1006
  if (data == NULL) {
591
- return 0;
1007
+ return *is_last;
592
1008
  }
593
- compressor->CopyInputToRingBuffer(bytes_read, data);
1009
+ compressor->CopyInputToRingBuffer(*bytes_read, data);
594
1010
 
595
1011
  // Read more bytes until block_size is filled or an EOF (data == NULL) is
596
1012
  // received. This is useful to get deterministic compressed output for the
597
1013
  // same input no matter how r->Read splits the input to chunks.
598
- for (size_t remaining = block_size - bytes_read; remaining > 0; ) {
1014
+ for (size_t remaining = block_size - *bytes_read; remaining > 0; ) {
599
1015
  size_t more_bytes_read = 0;
600
- data = reinterpret_cast<const uint8_t*>(
601
- r->Read(remaining, &more_bytes_read));
1016
+ data = BrotliInReadAndCheckEnd(remaining, r, &more_bytes_read, is_last);
602
1017
  if (data == NULL) {
603
- break;
1018
+ return *is_last;
604
1019
  }
605
1020
  compressor->CopyInputToRingBuffer(more_bytes_read, data);
606
- bytes_read += more_bytes_read;
1021
+ *bytes_read += more_bytes_read;
607
1022
  remaining -= more_bytes_read;
608
1023
  }
609
- return bytes_read;
1024
+ return true;
610
1025
  }
611
1026
 
612
- bool BrotliInIsFinished(BrotliIn* r) {
613
- size_t read_bytes;
614
- return r->Read(0, &read_bytes) == NULL;
615
- }
616
1027
 
617
1028
  int BrotliCompress(BrotliParams params, BrotliIn* in, BrotliOut* out) {
618
1029
  return BrotliCompressWithCustomDictionary(0, 0, params, in, out);
619
1030
  }
620
1031
 
1032
+ // Reads the provided input in 'block_size' blocks. Only the last read can be
1033
+ // smaller than 'block_size'.
1034
+ class BrotliBlockReader {
1035
+ public:
1036
+ explicit BrotliBlockReader(size_t block_size)
1037
+ : block_size_(block_size), buf_(NULL) {}
1038
+ ~BrotliBlockReader(void) { delete[] buf_; }
1039
+
1040
+ const uint8_t* Read(BrotliIn* in, size_t* bytes_read, bool* is_last) {
1041
+ *bytes_read = 0;
1042
+ const uint8_t* data = BrotliInReadAndCheckEnd(block_size_, in,
1043
+ bytes_read, is_last);
1044
+ if (data == NULL || *bytes_read == block_size_ || *is_last) {
1045
+ // If we could get the whole block in one read, or it is the last block,
1046
+ // we just return the pointer to the data without copying.
1047
+ return data;
1048
+ }
1049
+ // If the data comes in smaller chunks, we need to copy it into an internal
1050
+ // buffer until we get a whole block or reach the last chunk.
1051
+ if (buf_ == NULL) {
1052
+ buf_ = new uint8_t[block_size_];
1053
+ }
1054
+ memcpy(buf_, data, *bytes_read);
1055
+ do {
1056
+ size_t cur_bytes_read = 0;
1057
+ data = BrotliInReadAndCheckEnd(block_size_ - *bytes_read, in,
1058
+ &cur_bytes_read, is_last);
1059
+ if (data == NULL) {
1060
+ return *is_last ? buf_ : NULL;
1061
+ }
1062
+ memcpy(&buf_[*bytes_read], data, cur_bytes_read);
1063
+ *bytes_read += cur_bytes_read;
1064
+ } while (*bytes_read < block_size_ && !*is_last);
1065
+ return buf_;
1066
+ }
1067
+
1068
+ private:
1069
+ const size_t block_size_;
1070
+ uint8_t* buf_;
1071
+ };
1072
+
621
1073
  int BrotliCompressWithCustomDictionary(size_t dictsize, const uint8_t* dict,
622
1074
  BrotliParams params,
623
1075
  BrotliIn* in, BrotliOut* out) {
1076
+ if (params.quality <= 1) {
1077
+ const int quality = std::max(0, params.quality);
1078
+ const int lgwin = std::min(kMaxWindowBits,
1079
+ std::max(kMinWindowBits, params.lgwin));
1080
+ uint8_t* storage = NULL;
1081
+ int* table = NULL;
1082
+ uint32_t* command_buf = NULL;
1083
+ uint8_t* literal_buf = NULL;
1084
+ uint8_t cmd_depths[128];
1085
+ uint16_t cmd_bits[128];
1086
+ uint8_t cmd_code[512];
1087
+ size_t cmd_code_numbits;
1088
+ if (quality == 0) {
1089
+ InitCommandPrefixCodes(cmd_depths, cmd_bits, cmd_code, &cmd_code_numbits);
1090
+ }
1091
+ uint8_t last_byte;
1092
+ uint8_t last_byte_bits;
1093
+ EncodeWindowBits(lgwin, &last_byte, &last_byte_bits);
1094
+ BrotliBlockReader r(1u << lgwin);
1095
+ int ok = 1;
1096
+ bool is_last = false;
1097
+ while (ok && !is_last) {
1098
+ // Read next block of input.
1099
+ size_t bytes;
1100
+ const uint8_t* data = r.Read(in, &bytes, &is_last);
1101
+ if (data == NULL) {
1102
+ if (!is_last) {
1103
+ ok = 0;
1104
+ break;
1105
+ }
1106
+ assert(bytes == 0);
1107
+ }
1108
+ // Set up output storage.
1109
+ const size_t max_out_size = 2 * bytes + 500;
1110
+ if (storage == NULL) {
1111
+ storage = new uint8_t[max_out_size];
1112
+ }
1113
+ storage[0] = last_byte;
1114
+ size_t storage_ix = last_byte_bits;
1115
+ // Set up hash table.
1116
+ size_t htsize = HashTableSize(MaxHashTableSize(quality), bytes);
1117
+ if (table == NULL) {
1118
+ table = new int[htsize];
1119
+ }
1120
+ memset(table, 0, htsize * sizeof(table[0]));
1121
+ // Set up command and literal buffers for two pass mode.
1122
+ if (quality == 1 && command_buf == NULL) {
1123
+ size_t buf_size = std::min(bytes, kCompressFragmentTwoPassBlockSize);
1124
+ command_buf = new uint32_t[buf_size];
1125
+ literal_buf = new uint8_t[buf_size];
1126
+ }
1127
+ // Do the actual compression.
1128
+ if (quality == 0) {
1129
+ BrotliCompressFragmentFast(data, bytes, is_last, table, htsize,
1130
+ cmd_depths, cmd_bits,
1131
+ &cmd_code_numbits, cmd_code,
1132
+ &storage_ix, storage);
1133
+ } else {
1134
+ BrotliCompressFragmentTwoPass(data, bytes, is_last,
1135
+ command_buf, literal_buf,
1136
+ table, htsize,
1137
+ &storage_ix, storage);
1138
+ }
1139
+ // Save last bytes to stitch it together with the next output block.
1140
+ last_byte = storage[storage_ix >> 3];
1141
+ last_byte_bits = storage_ix & 7u;
1142
+ // Write output block.
1143
+ size_t out_bytes = storage_ix >> 3;
1144
+ if (out_bytes > 0 && !out->Write(storage, out_bytes)) {
1145
+ ok = 0;
1146
+ break;
1147
+ }
1148
+ }
1149
+ delete[] storage;
1150
+ delete[] table;
1151
+ delete[] command_buf;
1152
+ delete[] literal_buf;
1153
+ return ok;
1154
+ }
1155
+
624
1156
  size_t in_bytes = 0;
625
1157
  size_t out_bytes = 0;
626
- uint8_t* output;
1158
+ uint8_t* output = NULL;
627
1159
  bool final_block = false;
628
1160
  BrotliCompressor compressor(params);
629
1161
  if (dictsize != 0) compressor.BrotliSetCustomDictionary(dictsize, dict);
630
1162
  while (!final_block) {
631
- in_bytes = CopyOneBlockToRingBuffer(in, &compressor);
632
- final_block = in_bytes == 0 || BrotliInIsFinished(in);
1163
+ if (!CopyOneBlockToRingBuffer(in, &compressor, &in_bytes, &final_block)) {
1164
+ return false;
1165
+ }
633
1166
  out_bytes = 0;
634
1167
  if (!compressor.WriteBrotliData(final_block,
635
1168
  /* force_flush = */ false,