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