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