filter_common.c revision 207753
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
16static 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#endif
46#ifdef HAVE_DECODER_LZMA2
47	{
48		.id = LZMA_FILTER_LZMA2,
49		.options_size = sizeof(lzma_options_lzma),
50		.non_last_ok = false,
51		.last_ok = true,
52		.changes_size = true,
53	},
54#endif
55#if defined(HAVE_ENCODER_SUBBLOCK) || defined(HAVE_DECODER_SUBBLOCK)
56	{
57		.id = LZMA_FILTER_SUBBLOCK,
58		.options_size = sizeof(lzma_options_subblock),
59		.non_last_ok = true,
60		.last_ok = true,
61		.changes_size = true,
62	},
63#endif
64#ifdef HAVE_DECODER_X86
65	{
66		.id = LZMA_FILTER_X86,
67		.options_size = sizeof(lzma_options_bcj),
68		.non_last_ok = true,
69		.last_ok = false,
70		.changes_size = false,
71	},
72#endif
73#if defined(HAVE_ENCODER_POWERPC) || defined(HAVE_DECODER_POWERPC)
74	{
75		.id = LZMA_FILTER_POWERPC,
76		.options_size = sizeof(lzma_options_bcj),
77		.non_last_ok = true,
78		.last_ok = false,
79		.changes_size = false,
80	},
81#endif
82#ifdef HAVE_DECODER_IA64
83	{
84		.id = LZMA_FILTER_IA64,
85		.options_size = sizeof(lzma_options_bcj),
86		.non_last_ok = true,
87		.last_ok = false,
88		.changes_size = false,
89	},
90#endif
91#if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM)
92	{
93		.id = LZMA_FILTER_ARM,
94		.options_size = sizeof(lzma_options_bcj),
95		.non_last_ok = true,
96		.last_ok = false,
97		.changes_size = false,
98	},
99#endif
100#if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB)
101	{
102		.id = LZMA_FILTER_ARMTHUMB,
103		.options_size = sizeof(lzma_options_bcj),
104		.non_last_ok = true,
105		.last_ok = false,
106		.changes_size = false,
107	},
108#endif
109#if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC)
110	{
111		.id = LZMA_FILTER_SPARC,
112		.options_size = sizeof(lzma_options_bcj),
113		.non_last_ok = true,
114		.last_ok = false,
115		.changes_size = false,
116	},
117#endif
118#if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA)
119	{
120		.id = LZMA_FILTER_DELTA,
121		.options_size = sizeof(lzma_options_delta),
122		.non_last_ok = true,
123		.last_ok = false,
124		.changes_size = false,
125	},
126#endif
127	{
128		.id = LZMA_VLI_UNKNOWN
129	}
130};
131
132
133extern LZMA_API(lzma_ret)
134lzma_filters_copy(const lzma_filter *src, lzma_filter *dest,
135		lzma_allocator *allocator)
136{
137	if (src == NULL || dest == NULL)
138		return LZMA_PROG_ERROR;
139
140	lzma_ret ret;
141	size_t i;
142	for (i = 0; src[i].id != LZMA_VLI_UNKNOWN; ++i) {
143		// There must be a maximum of four filters plus
144		// the array terminator.
145		if (i == LZMA_FILTERS_MAX) {
146			ret = LZMA_OPTIONS_ERROR;
147			goto error;
148		}
149
150		dest[i].id = src[i].id;
151
152		if (src[i].options == NULL) {
153			dest[i].options = NULL;
154		} else {
155			// See if the filter is supported only when the
156			// options is not NULL. This might be convenient
157			// sometimes if the app is actually copying only
158			// a partial filter chain with a place holder ID.
159			//
160			// When options is not NULL, the Filter ID must be
161			// supported by us, because otherwise we don't know
162			// how big the options are.
163			size_t j;
164			for (j = 0; src[i].id != features[j].id; ++j) {
165				if (features[j].id == LZMA_VLI_UNKNOWN) {
166					ret = LZMA_OPTIONS_ERROR;
167					goto error;
168				}
169			}
170
171			// Allocate and copy the options.
172			dest[i].options = lzma_alloc(features[j].options_size,
173					allocator);
174			if (dest[i].options == NULL) {
175				ret = LZMA_MEM_ERROR;
176				goto error;
177			}
178
179			memcpy(dest[i].options, src[i].options,
180					features[j].options_size);
181		}
182	}
183
184	// Terminate the filter array.
185	assert(i <= LZMA_FILTERS_MAX + 1);
186	dest[i].id = LZMA_VLI_UNKNOWN;
187	dest[i].options = NULL;
188
189	return LZMA_OK;
190
191error:
192	// Free the options which we have already allocated.
193	while (i-- > 0) {
194		lzma_free(dest[i].options, allocator);
195		dest[i].options = NULL;
196	}
197
198	return ret;
199}
200
201
202static lzma_ret
203validate_chain(const lzma_filter *filters, size_t *count)
204{
205	// There must be at least one filter.
206	if (filters == NULL || filters[0].id == LZMA_VLI_UNKNOWN)
207		return LZMA_PROG_ERROR;
208
209	// Number of non-last filters that may change the size of the data
210	// significantly (that is, more than 1-2 % or so).
211	size_t changes_size_count = 0;
212
213	// True if it is OK to add a new filter after the current filter.
214	bool non_last_ok = true;
215
216	// True if the last filter in the given chain is actually usable as
217	// the last filter. Only filters that support embedding End of Payload
218	// Marker can be used as the last filter in the chain.
219	bool last_ok = false;
220
221	size_t i = 0;
222	do {
223		size_t j;
224		for (j = 0; filters[i].id != features[j].id; ++j)
225			if (features[j].id == LZMA_VLI_UNKNOWN)
226				return LZMA_OPTIONS_ERROR;
227
228		// If the previous filter in the chain cannot be a non-last
229		// filter, the chain is invalid.
230		if (!non_last_ok)
231			return LZMA_OPTIONS_ERROR;
232
233		non_last_ok = features[j].non_last_ok;
234		last_ok = features[j].last_ok;
235		changes_size_count += features[j].changes_size;
236
237	} while (filters[++i].id != LZMA_VLI_UNKNOWN);
238
239	// There must be 1-4 filters. The last filter must be usable as
240	// the last filter in the chain. A maximum of three filters are
241	// allowed to change the size of the data.
242	if (i > LZMA_FILTERS_MAX || !last_ok || changes_size_count > 3)
243		return LZMA_OPTIONS_ERROR;
244
245	*count = i;
246	return LZMA_OK;
247}
248
249
250extern lzma_ret
251lzma_raw_coder_init(lzma_next_coder *next, lzma_allocator *allocator,
252		const lzma_filter *options,
253		lzma_filter_find coder_find, bool is_encoder)
254{
255	// Do some basic validation and get the number of filters.
256	size_t count;
257	return_if_error(validate_chain(options, &count));
258
259	// Set the filter functions and copy the options pointer.
260	lzma_filter_info filters[LZMA_FILTERS_MAX + 1];
261	if (is_encoder) {
262		for (size_t i = 0; i < count; ++i) {
263			// The order of the filters is reversed in the
264			// encoder. It allows more efficient handling
265			// of the uncompressed data.
266			const size_t j = count - i - 1;
267
268			const lzma_filter_coder *const fc
269					= coder_find(options[i].id);
270			if (fc == NULL || fc->init == NULL)
271				return LZMA_OPTIONS_ERROR;
272
273			filters[j].id = options[i].id;
274			filters[j].init = fc->init;
275			filters[j].options = options[i].options;
276		}
277	} else {
278		for (size_t i = 0; i < count; ++i) {
279			const lzma_filter_coder *const fc
280					= coder_find(options[i].id);
281			if (fc == NULL || fc->init == NULL)
282				return LZMA_OPTIONS_ERROR;
283
284			filters[i].id = options[i].id;
285			filters[i].init = fc->init;
286			filters[i].options = options[i].options;
287		}
288	}
289
290	// Terminate the array.
291	filters[count].id = LZMA_VLI_UNKNOWN;
292	filters[count].init = NULL;
293
294	// Initialize the filters.
295	const lzma_ret ret = lzma_next_filter_init(next, allocator, filters);
296	if (ret != LZMA_OK)
297		lzma_next_end(next, allocator);
298
299	return ret;
300}
301
302
303extern uint64_t
304lzma_raw_coder_memusage(lzma_filter_find coder_find,
305		const lzma_filter *filters)
306{
307	// The chain has to have at least one filter.
308	{
309		size_t tmp;
310		if (validate_chain(filters, &tmp) != LZMA_OK)
311			return UINT64_MAX;
312	}
313
314	uint64_t total = 0;
315	size_t i = 0;
316
317	do {
318		const lzma_filter_coder *const fc
319				 = coder_find(filters[i].id);
320		if (fc == NULL)
321			return UINT64_MAX; // Unsupported Filter ID
322
323		if (fc->memusage == NULL) {
324			// This filter doesn't have a function to calculate
325			// the memory usage and validate the options. Such
326			// filters need only little memory, so we use 1 KiB
327			// as a good estimate. They also accept all possible
328			// options, so there's no need to worry about lack
329			// of validation.
330			total += 1024;
331		} else {
332			// Call the filter-specific memory usage calculation
333			// function.
334			const uint64_t usage
335					= fc->memusage(filters[i].options);
336			if (usage == UINT64_MAX)
337				return UINT64_MAX; // Invalid options
338
339			total += usage;
340		}
341	} while (filters[++i].id != LZMA_VLI_UNKNOWN);
342
343	// Add some fixed amount of extra. It's to compensate memory usage
344	// of Stream, Block etc. coders, malloc() overhead, stack etc.
345	return total + LZMA_MEMUSAGE_BASE;
346}
347