encode_parallel.cc 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /* Copyright 2013 Google Inc. All Rights Reserved.
  2. Distributed under MIT license.
  3. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
  4. */
  5. /* Implementation of parallel Brotli compressor. */
  6. #include "./encode_parallel.h"
  7. #include <vector>
  8. #include "./backward_references.h"
  9. #include "./brotli_bit_stream.h"
  10. #include "./context.h"
  11. #include "./entropy_encode.h"
  12. #include "./fast_log.h"
  13. #include "./hash.h"
  14. #include "./metablock.h"
  15. #include "./port.h"
  16. #include "./prefix.h"
  17. #include "./utf8_util.h"
  18. namespace brotli {
  19. namespace {
  20. static void RecomputeDistancePrefixes(Command* cmds, size_t num_commands,
  21. uint32_t num_direct_distance_codes,
  22. uint32_t distance_postfix_bits) {
  23. if (num_direct_distance_codes == 0 &&
  24. distance_postfix_bits == 0) {
  25. return;
  26. }
  27. for (size_t i = 0; i < num_commands; ++i) {
  28. Command* cmd = &cmds[i];
  29. if (CommandCopyLen(cmd) && cmd->cmd_prefix_ >= 128) {
  30. PrefixEncodeCopyDistance(CommandDistanceCode(cmd),
  31. num_direct_distance_codes,
  32. distance_postfix_bits,
  33. &cmd->dist_prefix_,
  34. &cmd->dist_extra_);
  35. }
  36. }
  37. }
  38. /* Returns 1 on success, otherwise 0. */
  39. int WriteMetaBlockParallel(const BrotliParams& params,
  40. const uint32_t input_size,
  41. const uint8_t* input_buffer,
  42. const uint32_t prefix_size,
  43. const uint8_t* prefix_buffer,
  44. const int is_first,
  45. const int is_last,
  46. size_t* encoded_size,
  47. uint8_t* encoded_buffer) {
  48. if (input_size == 0) {
  49. return 0;
  50. }
  51. MemoryManager memory_manager;
  52. MemoryManager* m = &memory_manager;
  53. BrotliInitMemoryManager(m, 0, 0, 0);
  54. uint8_t* storage;
  55. size_t storage_ix;
  56. uint8_t first_byte;
  57. size_t first_byte_bits;
  58. size_t output_size;
  59. uint32_t num_direct_distance_codes;
  60. uint32_t distance_postfix_bits;
  61. ContextType literal_context_mode;
  62. size_t last_insert_len = 0;
  63. size_t num_commands = 0;
  64. size_t num_literals = 0;
  65. int dist_cache[4] = { -4, -4, -4, -4 };
  66. Command* commands;
  67. int hash_type = BROTLI_MIN(int, 10, params.quality);
  68. Hashers* hashers;
  69. int use_utf8_mode;
  70. uint8_t prev_byte;
  71. uint8_t prev_byte2;
  72. const uint32_t mask = BROTLI_UINT32_MAX >> 1;
  73. /* Copy prefix + next input block into a continuous area. */
  74. uint32_t input_pos = prefix_size;
  75. /* CreateBackwardReferences reads up to 3 bytes past the end of input if the
  76. mask points past the end of input.
  77. FindMatchLengthWithLimit could do another 8 bytes look-forward. */
  78. uint8_t* input = BROTLI_ALLOC(m, uint8_t, prefix_size + input_size + 4 + 8);
  79. if (BROTLI_IS_OOM(m)) goto oom;
  80. memcpy(input, prefix_buffer, prefix_size);
  81. memcpy(input + input_pos, input_buffer, input_size);
  82. /* Since we don't have a ringbuffer, masking is a no-op.
  83. We use one less bit than the full range because some of the code uses
  84. mask + 1 as the size of the ringbuffer. */
  85. prev_byte = input_pos > 0 ? input[(input_pos - 1) & mask] : 0;
  86. prev_byte2 = input_pos > 1 ? input[(input_pos - 2) & mask] : 0;
  87. /* Decide about UTF8 mode. */
  88. static const double kMinUTF8Ratio = 0.75;
  89. use_utf8_mode = BrotliIsMostlyUTF8(
  90. input, input_pos, mask, input_size, kMinUTF8Ratio);
  91. /* Initialize hashers. */
  92. hashers = BROTLI_ALLOC(m, Hashers, 1);
  93. if (BROTLI_IS_OOM(m)) goto oom;
  94. InitHashers(hashers);
  95. HashersSetup(m, hashers, hash_type);
  96. if (BROTLI_IS_OOM(m)) goto oom;
  97. /* Compute backward references. */
  98. commands = BROTLI_ALLOC(m, Command, ((input_size + 1) >> 1));
  99. if (BROTLI_IS_OOM(m)) goto oom;
  100. BrotliCreateBackwardReferences(m, input_size, input_pos, is_last, input,
  101. mask, params.quality, params.lgwin, hashers, hash_type, dist_cache,
  102. &last_insert_len, commands, &num_commands, &num_literals);
  103. if (BROTLI_IS_OOM(m)) goto oom;
  104. DestroyHashers(m, hashers);
  105. BROTLI_FREE(m, hashers);
  106. if (last_insert_len > 0) {
  107. InitInsertCommand(&commands[num_commands++], last_insert_len);
  108. num_literals += last_insert_len;
  109. }
  110. assert(num_commands != 0);
  111. /* Build the meta-block. */
  112. MetaBlockSplit mb;
  113. InitMetaBlockSplit(&mb);
  114. num_direct_distance_codes = params.mode == BrotliParams::MODE_FONT ? 12 : 0;
  115. distance_postfix_bits = params.mode == BrotliParams::MODE_FONT ? 1 : 0;
  116. literal_context_mode = use_utf8_mode ? CONTEXT_UTF8 : CONTEXT_SIGNED;
  117. RecomputeDistancePrefixes(commands, num_commands,
  118. num_direct_distance_codes,
  119. distance_postfix_bits);
  120. if (params.quality <= 9) {
  121. BrotliBuildMetaBlockGreedy(m, input, input_pos, mask,
  122. commands, num_commands,
  123. &mb);
  124. if (BROTLI_IS_OOM(m)) goto oom;
  125. } else {
  126. BrotliBuildMetaBlock(m, input, input_pos, mask, params.quality,
  127. prev_byte, prev_byte2,
  128. commands, num_commands,
  129. literal_context_mode,
  130. &mb);
  131. if (BROTLI_IS_OOM(m)) goto oom;
  132. }
  133. /* Set up the temporary output storage. */
  134. storage = BROTLI_ALLOC(m, uint8_t, 2 * input_size + 500);
  135. if (BROTLI_IS_OOM(m)) goto oom;
  136. first_byte = 0;
  137. first_byte_bits = 0;
  138. if (is_first) {
  139. if (params.lgwin == 16) {
  140. first_byte = 0;
  141. first_byte_bits = 1;
  142. } else if (params.lgwin == 17) {
  143. first_byte = 1;
  144. first_byte_bits = 7;
  145. } else {
  146. first_byte = static_cast<uint8_t>(((params.lgwin - 17) << 1) | 1);
  147. first_byte_bits = 4;
  148. }
  149. }
  150. storage[0] = static_cast<uint8_t>(first_byte);
  151. storage_ix = first_byte_bits;
  152. /* Store the meta-block to the temporary output. */
  153. BrotliStoreMetaBlock(m, input, input_pos, input_size, mask,
  154. prev_byte, prev_byte2,
  155. is_last,
  156. num_direct_distance_codes,
  157. distance_postfix_bits,
  158. literal_context_mode,
  159. commands, num_commands,
  160. &mb,
  161. &storage_ix, storage);
  162. if (BROTLI_IS_OOM(m)) goto oom;
  163. DestroyMetaBlockSplit(m, &mb);
  164. BROTLI_FREE(m, commands);
  165. /* If this is not the last meta-block, store an empty metadata
  166. meta-block so that the meta-block will end at a byte boundary. */
  167. if (!is_last) {
  168. BrotliStoreSyncMetaBlock(&storage_ix, storage);
  169. }
  170. /* If the compressed data is too large, fall back to an uncompressed
  171. meta-block. */
  172. output_size = storage_ix >> 3;
  173. if (input_size + 4 < output_size) {
  174. storage[0] = static_cast<uint8_t>(first_byte);
  175. storage_ix = first_byte_bits;
  176. BrotliStoreUncompressedMetaBlock(is_last, input, input_pos, mask,
  177. input_size,
  178. &storage_ix, storage);
  179. output_size = storage_ix >> 3;
  180. }
  181. /* Copy the temporary output with size-check to the output. */
  182. if (output_size > *encoded_size) {
  183. BROTLI_FREE(m, storage);
  184. BROTLI_FREE(m, input);
  185. return 0;
  186. }
  187. memcpy(encoded_buffer, storage, output_size);
  188. *encoded_size = output_size;
  189. BROTLI_FREE(m, storage);
  190. BROTLI_FREE(m, input);
  191. return 1;
  192. oom:
  193. BrotliWipeOutMemoryManager(m);
  194. return 0;
  195. }
  196. } /* namespace */
  197. int BrotliCompressBufferParallel(BrotliParams params,
  198. size_t input_size,
  199. const uint8_t* input_buffer,
  200. size_t* encoded_size,
  201. uint8_t* encoded_buffer) {
  202. if (*encoded_size == 0) {
  203. /* Output buffer needs at least one byte. */
  204. return 0;
  205. } else if (input_size == 0) {
  206. encoded_buffer[0] = 6;
  207. *encoded_size = 1;
  208. return 1;
  209. }
  210. /* Sanitize params. */
  211. if (params.lgwin < kBrotliMinWindowBits) {
  212. params.lgwin = kBrotliMinWindowBits;
  213. } else if (params.lgwin > kBrotliMaxWindowBits) {
  214. params.lgwin = kBrotliMaxWindowBits;
  215. }
  216. if (params.lgblock == 0) {
  217. params.lgblock = 16;
  218. if (params.quality >= 9 && params.lgwin > params.lgblock) {
  219. params.lgblock = BROTLI_MIN(int, 21, params.lgwin);
  220. }
  221. } else if (params.lgblock < kBrotliMinInputBlockBits) {
  222. params.lgblock = kBrotliMinInputBlockBits;
  223. } else if (params.lgblock > kBrotliMaxInputBlockBits) {
  224. params.lgblock = kBrotliMaxInputBlockBits;
  225. }
  226. size_t max_input_block_size = 1 << params.lgblock;
  227. size_t max_prefix_size = 1u << params.lgwin;
  228. std::vector<std::vector<uint8_t> > compressed_pieces;
  229. /* Compress block-by-block independently. */
  230. for (size_t pos = 0; pos < input_size; ) {
  231. uint32_t input_block_size = static_cast<uint32_t>(
  232. BROTLI_MIN(size_t, max_input_block_size, input_size - pos));
  233. uint32_t prefix_size =
  234. static_cast<uint32_t>(BROTLI_MIN(size_t, max_prefix_size, pos));
  235. size_t out_size = input_block_size + (input_block_size >> 3) + 1024;
  236. std::vector<uint8_t> out(out_size);
  237. if (!WriteMetaBlockParallel(params,
  238. input_block_size,
  239. &input_buffer[pos],
  240. prefix_size,
  241. &input_buffer[pos - prefix_size],
  242. (pos == 0) ? 1 : 0,
  243. (pos + input_block_size == input_size) ? 1 : 0,
  244. &out_size,
  245. &out[0])) {
  246. return 0;
  247. }
  248. out.resize(out_size);
  249. compressed_pieces.push_back(out);
  250. pos += input_block_size;
  251. }
  252. /* Piece together the output. */
  253. size_t out_pos = 0;
  254. for (size_t i = 0; i < compressed_pieces.size(); ++i) {
  255. const std::vector<uint8_t>& out = compressed_pieces[i];
  256. if (out_pos + out.size() > *encoded_size) {
  257. return 0;
  258. }
  259. memcpy(&encoded_buffer[out_pos], &out[0], out.size());
  260. out_pos += out.size();
  261. }
  262. *encoded_size = out_pos;
  263. return 1;
  264. }
  265. } /* namespace brotli */