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