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 }
386