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