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 {
144 if (src == NULL || real_dest == NULL)
145 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 lzma_filter dest[LZMA_FILTERS_MAX + 1];
150
151 lzma_ret ret;
152 size_t i;
153 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 if (i == LZMA_FILTERS_MAX) {
157 ret = LZMA_OPTIONS_ERROR;
158 goto error;
159 }
160
161 dest[i].id = src[i].id;
162
163 if (src[i].options == NULL) {
164 dest[i].options = NULL;
165 } 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 size_t j;
175 for (j = 0; src[i].id != features[j].id; ++j) {
176 if (features[j].id == LZMA_VLI_UNKNOWN) {
177 ret = LZMA_OPTIONS_ERROR;
178 goto error;
179 }
180 }
181
182 // Allocate and copy the options.
183 dest[i].options = lzma_alloc(features[j].options_size,
184 allocator);
185 if (dest[i].options == NULL) {
186 ret = LZMA_MEM_ERROR;
187 goto error;
188 }
189
190 memcpy(dest[i].options, src[i].options,
191 features[j].options_size);
192 }
193 }
194
195 // Terminate the filter array.
196 assert(i < LZMA_FILTERS_MAX + 1);
197 dest[i].id = LZMA_VLI_UNKNOWN;
198 dest[i].options = NULL;
199
200 // Copy it to the caller-supplied array now that we know that
201 // no errors occurred.
202 memcpy(real_dest, dest, (i + 1) * sizeof(lzma_filter));
203
204 return LZMA_OK;
205
206 error:
207 // Free the options which we have already allocated.
208 while (i-- > 0)
209 lzma_free(dest[i].options, allocator);
210
211 return ret;
212 }
213
214
215 extern LZMA_API(void)
216 lzma_filters_free(lzma_filter *filters, const lzma_allocator *allocator)
217 {
218 if (filters == NULL)
219 return;
220
221 for (size_t i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i) {
222 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 assert(0);
229 break;
230 }
231
232 lzma_free(filters[i].options, allocator);
233 filters[i].options = NULL;
234 filters[i].id = LZMA_VLI_UNKNOWN;
235 }
236
237 return;
238 }
239
240
241 extern lzma_ret
242 lzma_validate_chain(const lzma_filter *filters, size_t *count)
243 {
244 // There must be at least one filter.
245 if (filters == NULL || filters[0].id == LZMA_VLI_UNKNOWN)
246 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 size_t changes_size_count = 0;
251
252 // True if it is OK to add a new filter after the current filter.
253 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 bool last_ok = false;
259
260 size_t i = 0;
261 do {
262 size_t j;
263 for (j = 0; filters[i].id != features[j].id; ++j)
264 if (features[j].id == LZMA_VLI_UNKNOWN)
265 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 if (!non_last_ok)
270 return LZMA_OPTIONS_ERROR;
271
272 non_last_ok = features[j].non_last_ok;
273 last_ok = features[j].last_ok;
274 changes_size_count += features[j].changes_size;
275
276 } 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 if (i > LZMA_FILTERS_MAX || !last_ok || changes_size_count > 3)
282 return LZMA_OPTIONS_ERROR;
283
284 *count = i;
285 return LZMA_OK;
286 }
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 {
294 // Do some basic validation and get the number of filters.
295 size_t count;
296 return_if_error(lzma_validate_chain(options, &count));
297
298 // Set the filter functions and copy the options pointer.
299 lzma_filter_info filters[LZMA_FILTERS_MAX + 1];
300 if (is_encoder) {
301 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 const size_t j = count - i - 1;
306
307 const lzma_filter_coder *const fc
308 = coder_find(options[i].id);
309 if (fc == NULL || fc->init == NULL)
310 return LZMA_OPTIONS_ERROR;
311
312 filters[j].id = options[i].id;
313 filters[j].init = fc->init;
314 filters[j].options = options[i].options;
315 }
316 } else {
317 for (size_t i = 0; i < count; ++i) {
318 const lzma_filter_coder *const fc
319 = coder_find(options[i].id);
320 if (fc == NULL || fc->init == NULL)
321 return LZMA_OPTIONS_ERROR;
322
323 filters[i].id = options[i].id;
324 filters[i].init = fc->init;
325 filters[i].options = options[i].options;
326 }
327 }
328
329 // Terminate the array.
330 filters[count].id = LZMA_VLI_UNKNOWN;
331 filters[count].init = NULL;
332
333 // Initialize the filters.
334 const lzma_ret ret = lzma_next_filter_init(next, allocator, filters);
335 if (ret != LZMA_OK)
336 lzma_next_end(next, allocator);
337
338 return ret;
339 }
340
341
342 extern uint64_t
343 lzma_raw_coder_memusage(lzma_filter_find coder_find,
344 const lzma_filter *filters)
345 {
346 // The chain has to have at least one filter.
347 {
348 size_t tmp;
349 if (lzma_validate_chain(filters, &tmp) != LZMA_OK)
350 return UINT64_MAX;
351 }
352
353 uint64_t total = 0;
354 size_t i = 0;
355
356 do {
357 const lzma_filter_coder *const fc
358 = coder_find(filters[i].id);
359 if (fc == NULL)
360 return UINT64_MAX; // Unsupported Filter ID
361
362 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 total += 1024;
370 } else {
371 // Call the filter-specific memory usage calculation
372 // function.
373 const uint64_t usage
374 = fc->memusage(filters[i].options);
375 if (usage == UINT64_MAX)
376 return UINT64_MAX; // Invalid options
377
378 total += usage;
379 }
380 } 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 return total + LZMA_MEMUSAGE_BASE;
385 }