/src/xz/src/liblzma/common/filter_common.c
Line | Count | Source (jump to first uncovered line) |
1 | | /////////////////////////////////////////////////////////////////////////////// |
2 | | // |
3 | | /// \file filter_common.c |
4 | | /// \brief Filter-specific stuff common for both encoder and decoder |
5 | | // |
6 | | // Author: Lasse Collin |
7 | | // |
8 | | // This file has been put into the public domain. |
9 | | // You can do whatever you want with this file. |
10 | | // |
11 | | /////////////////////////////////////////////////////////////////////////////// |
12 | | |
13 | | #include "filter_common.h" |
14 | | |
15 | | |
16 | | static const struct { |
17 | | /// Filter ID |
18 | | lzma_vli id; |
19 | | |
20 | | /// Size of the filter-specific options structure |
21 | | size_t options_size; |
22 | | |
23 | | /// True if it is OK to use this filter as non-last filter in |
24 | | /// the chain. |
25 | | bool non_last_ok; |
26 | | |
27 | | /// True if it is OK to use this filter as the last filter in |
28 | | /// the chain. |
29 | | bool last_ok; |
30 | | |
31 | | /// True if the filter may change the size of the data (that is, the |
32 | | /// amount of encoded output can be different than the amount of |
33 | | /// uncompressed input). |
34 | | bool changes_size; |
35 | | |
36 | | } features[] = { |
37 | | #if defined (HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1) |
38 | | { |
39 | | .id = LZMA_FILTER_LZMA1, |
40 | | .options_size = sizeof(lzma_options_lzma), |
41 | | .non_last_ok = false, |
42 | | .last_ok = true, |
43 | | .changes_size = true, |
44 | | }, |
45 | | { |
46 | | .id = LZMA_FILTER_LZMA1EXT, |
47 | | .options_size = sizeof(lzma_options_lzma), |
48 | | .non_last_ok = false, |
49 | | .last_ok = true, |
50 | | .changes_size = true, |
51 | | }, |
52 | | #endif |
53 | | #if defined(HAVE_ENCODER_LZMA2) || defined(HAVE_DECODER_LZMA2) |
54 | | { |
55 | | .id = LZMA_FILTER_LZMA2, |
56 | | .options_size = sizeof(lzma_options_lzma), |
57 | | .non_last_ok = false, |
58 | | .last_ok = true, |
59 | | .changes_size = true, |
60 | | }, |
61 | | #endif |
62 | | #if defined(HAVE_ENCODER_X86) || defined(HAVE_DECODER_X86) |
63 | | { |
64 | | .id = LZMA_FILTER_X86, |
65 | | .options_size = sizeof(lzma_options_bcj), |
66 | | .non_last_ok = true, |
67 | | .last_ok = false, |
68 | | .changes_size = false, |
69 | | }, |
70 | | #endif |
71 | | #if defined(HAVE_ENCODER_POWERPC) || defined(HAVE_DECODER_POWERPC) |
72 | | { |
73 | | .id = LZMA_FILTER_POWERPC, |
74 | | .options_size = sizeof(lzma_options_bcj), |
75 | | .non_last_ok = true, |
76 | | .last_ok = false, |
77 | | .changes_size = false, |
78 | | }, |
79 | | #endif |
80 | | #if defined(HAVE_ENCODER_IA64) || defined(HAVE_DECODER_IA64) |
81 | | { |
82 | | .id = LZMA_FILTER_IA64, |
83 | | .options_size = sizeof(lzma_options_bcj), |
84 | | .non_last_ok = true, |
85 | | .last_ok = false, |
86 | | .changes_size = false, |
87 | | }, |
88 | | #endif |
89 | | #if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM) |
90 | | { |
91 | | .id = LZMA_FILTER_ARM, |
92 | | .options_size = sizeof(lzma_options_bcj), |
93 | | .non_last_ok = true, |
94 | | .last_ok = false, |
95 | | .changes_size = false, |
96 | | }, |
97 | | #endif |
98 | | #if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB) |
99 | | { |
100 | | .id = LZMA_FILTER_ARMTHUMB, |
101 | | .options_size = sizeof(lzma_options_bcj), |
102 | | .non_last_ok = true, |
103 | | .last_ok = false, |
104 | | .changes_size = false, |
105 | | }, |
106 | | #endif |
107 | | #if defined(HAVE_ENCODER_ARM64) || defined(HAVE_DECODER_ARM64) |
108 | | { |
109 | | .id = LZMA_FILTER_ARM64, |
110 | | .options_size = sizeof(lzma_options_bcj), |
111 | | .non_last_ok = true, |
112 | | .last_ok = false, |
113 | | .changes_size = false, |
114 | | }, |
115 | | #endif |
116 | | #if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC) |
117 | | { |
118 | | .id = LZMA_FILTER_SPARC, |
119 | | .options_size = sizeof(lzma_options_bcj), |
120 | | .non_last_ok = true, |
121 | | .last_ok = false, |
122 | | .changes_size = false, |
123 | | }, |
124 | | #endif |
125 | | #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) |
126 | | { |
127 | | .id = LZMA_FILTER_DELTA, |
128 | | .options_size = sizeof(lzma_options_delta), |
129 | | .non_last_ok = true, |
130 | | .last_ok = false, |
131 | | .changes_size = false, |
132 | | }, |
133 | | #endif |
134 | | { |
135 | | .id = LZMA_VLI_UNKNOWN |
136 | | } |
137 | | }; |
138 | | |
139 | | |
140 | | extern LZMA_API(lzma_ret) |
141 | | lzma_filters_copy(const lzma_filter *src, lzma_filter *real_dest, |
142 | | const lzma_allocator *allocator) |
143 | 0 | { |
144 | 0 | if (src == NULL || real_dest == NULL) |
145 | 0 | return LZMA_PROG_ERROR; |
146 | | |
147 | | // Use a temporary destination so that the real destination |
148 | | // will never be modied if an error occurs. |
149 | 0 | lzma_filter dest[LZMA_FILTERS_MAX + 1]; |
150 | |
|
151 | 0 | lzma_ret ret; |
152 | 0 | size_t i; |
153 | 0 | for (i = 0; src[i].id != LZMA_VLI_UNKNOWN; ++i) { |
154 | | // There must be a maximum of four filters plus |
155 | | // the array terminator. |
156 | 0 | if (i == LZMA_FILTERS_MAX) { |
157 | 0 | ret = LZMA_OPTIONS_ERROR; |
158 | 0 | goto error; |
159 | 0 | } |
160 | | |
161 | 0 | dest[i].id = src[i].id; |
162 | |
|
163 | 0 | if (src[i].options == NULL) { |
164 | 0 | dest[i].options = NULL; |
165 | 0 | } else { |
166 | | // See if the filter is supported only when the |
167 | | // options is not NULL. This might be convenient |
168 | | // sometimes if the app is actually copying only |
169 | | // a partial filter chain with a place holder ID. |
170 | | // |
171 | | // When options is not NULL, the Filter ID must be |
172 | | // supported by us, because otherwise we don't know |
173 | | // how big the options are. |
174 | 0 | size_t j; |
175 | 0 | for (j = 0; src[i].id != features[j].id; ++j) { |
176 | 0 | if (features[j].id == LZMA_VLI_UNKNOWN) { |
177 | 0 | ret = LZMA_OPTIONS_ERROR; |
178 | 0 | goto error; |
179 | 0 | } |
180 | 0 | } |
181 | | |
182 | | // Allocate and copy the options. |
183 | 0 | dest[i].options = lzma_alloc(features[j].options_size, |
184 | 0 | allocator); |
185 | 0 | if (dest[i].options == NULL) { |
186 | 0 | ret = LZMA_MEM_ERROR; |
187 | 0 | goto error; |
188 | 0 | } |
189 | | |
190 | 0 | memcpy(dest[i].options, src[i].options, |
191 | 0 | features[j].options_size); |
192 | 0 | } |
193 | 0 | } |
194 | | |
195 | | // Terminate the filter array. |
196 | 0 | assert(i < LZMA_FILTERS_MAX + 1); |
197 | 0 | dest[i].id = LZMA_VLI_UNKNOWN; |
198 | 0 | dest[i].options = NULL; |
199 | | |
200 | | // Copy it to the caller-supplied array now that we know that |
201 | | // no errors occurred. |
202 | 0 | memcpy(real_dest, dest, (i + 1) * sizeof(lzma_filter)); |
203 | |
|
204 | 0 | return LZMA_OK; |
205 | | |
206 | 0 | error: |
207 | | // Free the options which we have already allocated. |
208 | 0 | while (i-- > 0) |
209 | 0 | lzma_free(dest[i].options, allocator); |
210 | |
|
211 | 0 | return ret; |
212 | 0 | } |
213 | | |
214 | | |
215 | | extern LZMA_API(void) |
216 | | lzma_filters_free(lzma_filter *filters, const lzma_allocator *allocator) |
217 | 281k | { |
218 | 281k | if (filters == NULL) |
219 | 0 | return; |
220 | | |
221 | 849k | for (size_t i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i) { |
222 | 567k | if (i == LZMA_FILTERS_MAX) { |
223 | | // The API says that LZMA_FILTERS_MAX + 1 is the |
224 | | // maximum allowed size including the terminating |
225 | | // element. Thus, we should never get here but in |
226 | | // case there is a bug and we do anyway, don't go |
227 | | // past the (probable) end of the array. |
228 | 0 | assert(0); |
229 | 0 | break; |
230 | 0 | } |
231 | | |
232 | 567k | lzma_free(filters[i].options, allocator); |
233 | 567k | filters[i].options = NULL; |
234 | 567k | filters[i].id = LZMA_VLI_UNKNOWN; |
235 | 567k | } |
236 | | |
237 | 281k | return; |
238 | 281k | } |
239 | | |
240 | | |
241 | | extern lzma_ret |
242 | | lzma_validate_chain(const lzma_filter *filters, size_t *count) |
243 | 561k | { |
244 | | // There must be at least one filter. |
245 | 561k | if (filters == NULL || filters[0].id == LZMA_VLI_UNKNOWN) |
246 | 0 | return LZMA_PROG_ERROR; |
247 | | |
248 | | // Number of non-last filters that may change the size of the data |
249 | | // significantly (that is, more than 1-2 % or so). |
250 | 561k | size_t changes_size_count = 0; |
251 | | |
252 | | // True if it is OK to add a new filter after the current filter. |
253 | 561k | bool non_last_ok = true; |
254 | | |
255 | | // True if the last filter in the given chain is actually usable as |
256 | | // the last filter. Only filters that support embedding End of Payload |
257 | | // Marker can be used as the last filter in the chain. |
258 | 561k | bool last_ok = false; |
259 | | |
260 | 561k | size_t i = 0; |
261 | 1.13M | do { |
262 | 1.13M | size_t j; |
263 | 5.19M | for (j = 0; filters[i].id != features[j].id; ++j) |
264 | 4.06M | if (features[j].id == LZMA_VLI_UNKNOWN) |
265 | 0 | return LZMA_OPTIONS_ERROR; |
266 | | |
267 | | // If the previous filter in the chain cannot be a non-last |
268 | | // filter, the chain is invalid. |
269 | 1.13M | if (!non_last_ok) |
270 | 5 | return LZMA_OPTIONS_ERROR; |
271 | | |
272 | 1.13M | non_last_ok = features[j].non_last_ok; |
273 | 1.13M | last_ok = features[j].last_ok; |
274 | 1.13M | changes_size_count += features[j].changes_size; |
275 | | |
276 | 1.13M | } while (filters[++i].id != LZMA_VLI_UNKNOWN); |
277 | | |
278 | | // There must be 1-4 filters. The last filter must be usable as |
279 | | // the last filter in the chain. A maximum of three filters are |
280 | | // allowed to change the size of the data. |
281 | 561k | if (i > LZMA_FILTERS_MAX || !last_ok || changes_size_count > 3) |
282 | 24 | return LZMA_OPTIONS_ERROR; |
283 | | |
284 | 561k | *count = i; |
285 | 561k | return LZMA_OK; |
286 | 561k | } |
287 | | |
288 | | |
289 | | extern lzma_ret |
290 | | lzma_raw_coder_init(lzma_next_coder *next, const lzma_allocator *allocator, |
291 | | const lzma_filter *options, |
292 | | lzma_filter_find coder_find, bool is_encoder) |
293 | 280k | { |
294 | | // Do some basic validation and get the number of filters. |
295 | 280k | size_t count; |
296 | 280k | return_if_error(lzma_validate_chain(options, &count)); |
297 | | |
298 | | // Set the filter functions and copy the options pointer. |
299 | 280k | lzma_filter_info filters[LZMA_FILTERS_MAX + 1]; |
300 | 280k | if (is_encoder) { |
301 | 0 | for (size_t i = 0; i < count; ++i) { |
302 | | // The order of the filters is reversed in the |
303 | | // encoder. It allows more efficient handling |
304 | | // of the uncompressed data. |
305 | 0 | const size_t j = count - i - 1; |
306 | |
|
307 | 0 | const lzma_filter_coder *const fc |
308 | 0 | = coder_find(options[i].id); |
309 | 0 | if (fc == NULL || fc->init == NULL) |
310 | 0 | return LZMA_OPTIONS_ERROR; |
311 | | |
312 | 0 | filters[j].id = options[i].id; |
313 | 0 | filters[j].init = fc->init; |
314 | 0 | filters[j].options = options[i].options; |
315 | 0 | } |
316 | 280k | } else { |
317 | 847k | for (size_t i = 0; i < count; ++i) { |
318 | 566k | const lzma_filter_coder *const fc |
319 | 566k | = coder_find(options[i].id); |
320 | 566k | if (fc == NULL || fc->init == NULL) |
321 | 0 | return LZMA_OPTIONS_ERROR; |
322 | | |
323 | 566k | filters[i].id = options[i].id; |
324 | 566k | filters[i].init = fc->init; |
325 | 566k | filters[i].options = options[i].options; |
326 | 566k | } |
327 | 280k | } |
328 | | |
329 | | // Terminate the array. |
330 | 280k | filters[count].id = LZMA_VLI_UNKNOWN; |
331 | 280k | filters[count].init = NULL; |
332 | | |
333 | | // Initialize the filters. |
334 | 280k | const lzma_ret ret = lzma_next_filter_init(next, allocator, filters); |
335 | 280k | if (ret != LZMA_OK) |
336 | 6 | lzma_next_end(next, allocator); |
337 | | |
338 | 280k | return ret; |
339 | 280k | } |
340 | | |
341 | | |
342 | | extern uint64_t |
343 | | lzma_raw_coder_memusage(lzma_filter_find coder_find, |
344 | | const lzma_filter *filters) |
345 | 280k | { |
346 | | // The chain has to have at least one filter. |
347 | 280k | { |
348 | 280k | size_t tmp; |
349 | 280k | if (lzma_validate_chain(filters, &tmp) != LZMA_OK) |
350 | 29 | return UINT64_MAX; |
351 | 280k | } |
352 | | |
353 | 280k | uint64_t total = 0; |
354 | 280k | size_t i = 0; |
355 | | |
356 | 566k | do { |
357 | 566k | const lzma_filter_coder *const fc |
358 | 566k | = coder_find(filters[i].id); |
359 | 566k | if (fc == NULL) |
360 | 0 | return UINT64_MAX; // Unsupported Filter ID |
361 | | |
362 | 566k | if (fc->memusage == NULL) { |
363 | | // This filter doesn't have a function to calculate |
364 | | // the memory usage and validate the options. Such |
365 | | // filters need only little memory, so we use 1 KiB |
366 | | // as a good estimate. They also accept all possible |
367 | | // options, so there's no need to worry about lack |
368 | | // of validation. |
369 | 284k | total += 1024; |
370 | 284k | } else { |
371 | | // Call the filter-specific memory usage calculation |
372 | | // function. |
373 | 282k | const uint64_t usage |
374 | 282k | = fc->memusage(filters[i].options); |
375 | 282k | if (usage == UINT64_MAX) |
376 | 0 | return UINT64_MAX; // Invalid options |
377 | | |
378 | 282k | total += usage; |
379 | 282k | } |
380 | 566k | } while (filters[++i].id != LZMA_VLI_UNKNOWN); |
381 | | |
382 | | // Add some fixed amount of extra. It's to compensate memory usage |
383 | | // of Stream, Block etc. coders, malloc() overhead, stack etc. |
384 | 280k | return total + LZMA_MEMUSAGE_BASE; |
385 | 280k | } |