1207753Smm///////////////////////////////////////////////////////////////////////////////
2207753Smm//
3207753Smm/// \file       filter_common.c
4207753Smm/// \brief      Filter-specific stuff common for both encoder and decoder
5207753Smm//
6207753Smm//  Author:     Lasse Collin
7207753Smm//
8207753Smm//  This file has been put into the public domain.
9207753Smm//  You can do whatever you want with this file.
10207753Smm//
11207753Smm///////////////////////////////////////////////////////////////////////////////
12207753Smm
13207753Smm#include "filter_common.h"
14207753Smm
15207753Smm
16207753Smmstatic const struct {
17207753Smm	/// Filter ID
18207753Smm	lzma_vli id;
19207753Smm
20207753Smm	/// Size of the filter-specific options structure
21207753Smm	size_t options_size;
22207753Smm
23207753Smm	/// True if it is OK to use this filter as non-last filter in
24207753Smm	/// the chain.
25207753Smm	bool non_last_ok;
26207753Smm
27207753Smm	/// True if it is OK to use this filter as the last filter in
28207753Smm	/// the chain.
29207753Smm	bool last_ok;
30207753Smm
31207753Smm	/// True if the filter may change the size of the data (that is, the
32207753Smm	/// amount of encoded output can be different than the amount of
33207753Smm	/// uncompressed input).
34207753Smm	bool changes_size;
35207753Smm
36207753Smm} features[] = {
37207753Smm#if defined (HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1)
38207753Smm	{
39207753Smm		.id = LZMA_FILTER_LZMA1,
40207753Smm		.options_size = sizeof(lzma_options_lzma),
41207753Smm		.non_last_ok = false,
42207753Smm		.last_ok = true,
43207753Smm		.changes_size = true,
44207753Smm	},
45207753Smm#endif
46223935Smm#if defined(HAVE_ENCODER_LZMA2) || defined(HAVE_DECODER_LZMA2)
47207753Smm	{
48207753Smm		.id = LZMA_FILTER_LZMA2,
49207753Smm		.options_size = sizeof(lzma_options_lzma),
50207753Smm		.non_last_ok = false,
51207753Smm		.last_ok = true,
52207753Smm		.changes_size = true,
53207753Smm	},
54207753Smm#endif
55223935Smm#if defined(HAVE_ENCODER_X86) || defined(HAVE_DECODER_X86)
56207753Smm	{
57207753Smm		.id = LZMA_FILTER_X86,
58207753Smm		.options_size = sizeof(lzma_options_bcj),
59207753Smm		.non_last_ok = true,
60207753Smm		.last_ok = false,
61207753Smm		.changes_size = false,
62207753Smm	},
63207753Smm#endif
64207753Smm#if defined(HAVE_ENCODER_POWERPC) || defined(HAVE_DECODER_POWERPC)
65207753Smm	{
66207753Smm		.id = LZMA_FILTER_POWERPC,
67207753Smm		.options_size = sizeof(lzma_options_bcj),
68207753Smm		.non_last_ok = true,
69207753Smm		.last_ok = false,
70207753Smm		.changes_size = false,
71207753Smm	},
72207753Smm#endif
73223935Smm#if defined(HAVE_ENCODER_IA64) || defined(HAVE_DECODER_IA64)
74207753Smm	{
75207753Smm		.id = LZMA_FILTER_IA64,
76207753Smm		.options_size = sizeof(lzma_options_bcj),
77207753Smm		.non_last_ok = true,
78207753Smm		.last_ok = false,
79207753Smm		.changes_size = false,
80207753Smm	},
81207753Smm#endif
82207753Smm#if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM)
83207753Smm	{
84207753Smm		.id = LZMA_FILTER_ARM,
85207753Smm		.options_size = sizeof(lzma_options_bcj),
86207753Smm		.non_last_ok = true,
87207753Smm		.last_ok = false,
88207753Smm		.changes_size = false,
89207753Smm	},
90207753Smm#endif
91207753Smm#if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB)
92207753Smm	{
93207753Smm		.id = LZMA_FILTER_ARMTHUMB,
94207753Smm		.options_size = sizeof(lzma_options_bcj),
95207753Smm		.non_last_ok = true,
96207753Smm		.last_ok = false,
97207753Smm		.changes_size = false,
98207753Smm	},
99207753Smm#endif
100207753Smm#if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC)
101207753Smm	{
102207753Smm		.id = LZMA_FILTER_SPARC,
103207753Smm		.options_size = sizeof(lzma_options_bcj),
104207753Smm		.non_last_ok = true,
105207753Smm		.last_ok = false,
106207753Smm		.changes_size = false,
107207753Smm	},
108207753Smm#endif
109207753Smm#if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA)
110207753Smm	{
111207753Smm		.id = LZMA_FILTER_DELTA,
112207753Smm		.options_size = sizeof(lzma_options_delta),
113207753Smm		.non_last_ok = true,
114207753Smm		.last_ok = false,
115207753Smm		.changes_size = false,
116207753Smm	},
117207753Smm#endif
118207753Smm	{
119207753Smm		.id = LZMA_VLI_UNKNOWN
120207753Smm	}
121207753Smm};
122207753Smm
123207753Smm
124207753Smmextern LZMA_API(lzma_ret)
125207753Smmlzma_filters_copy(const lzma_filter *src, lzma_filter *dest,
126278433Srpaulo		const lzma_allocator *allocator)
127207753Smm{
128207753Smm	if (src == NULL || dest == NULL)
129207753Smm		return LZMA_PROG_ERROR;
130207753Smm
131207753Smm	lzma_ret ret;
132207753Smm	size_t i;
133207753Smm	for (i = 0; src[i].id != LZMA_VLI_UNKNOWN; ++i) {
134207753Smm		// There must be a maximum of four filters plus
135207753Smm		// the array terminator.
136207753Smm		if (i == LZMA_FILTERS_MAX) {
137207753Smm			ret = LZMA_OPTIONS_ERROR;
138207753Smm			goto error;
139207753Smm		}
140207753Smm
141207753Smm		dest[i].id = src[i].id;
142207753Smm
143207753Smm		if (src[i].options == NULL) {
144207753Smm			dest[i].options = NULL;
145207753Smm		} else {
146207753Smm			// See if the filter is supported only when the
147207753Smm			// options is not NULL. This might be convenient
148207753Smm			// sometimes if the app is actually copying only
149207753Smm			// a partial filter chain with a place holder ID.
150207753Smm			//
151207753Smm			// When options is not NULL, the Filter ID must be
152207753Smm			// supported by us, because otherwise we don't know
153207753Smm			// how big the options are.
154207753Smm			size_t j;
155207753Smm			for (j = 0; src[i].id != features[j].id; ++j) {
156207753Smm				if (features[j].id == LZMA_VLI_UNKNOWN) {
157207753Smm					ret = LZMA_OPTIONS_ERROR;
158207753Smm					goto error;
159207753Smm				}
160207753Smm			}
161207753Smm
162207753Smm			// Allocate and copy the options.
163207753Smm			dest[i].options = lzma_alloc(features[j].options_size,
164207753Smm					allocator);
165207753Smm			if (dest[i].options == NULL) {
166207753Smm				ret = LZMA_MEM_ERROR;
167207753Smm				goto error;
168207753Smm			}
169207753Smm
170207753Smm			memcpy(dest[i].options, src[i].options,
171207753Smm					features[j].options_size);
172207753Smm		}
173207753Smm	}
174207753Smm
175207753Smm	// Terminate the filter array.
176207753Smm	assert(i <= LZMA_FILTERS_MAX + 1);
177207753Smm	dest[i].id = LZMA_VLI_UNKNOWN;
178207753Smm	dest[i].options = NULL;
179207753Smm
180207753Smm	return LZMA_OK;
181207753Smm
182207753Smmerror:
183207753Smm	// Free the options which we have already allocated.
184207753Smm	while (i-- > 0) {
185207753Smm		lzma_free(dest[i].options, allocator);
186207753Smm		dest[i].options = NULL;
187207753Smm	}
188207753Smm
189207753Smm	return ret;
190207753Smm}
191207753Smm
192207753Smm
193207753Smmstatic lzma_ret
194207753Smmvalidate_chain(const lzma_filter *filters, size_t *count)
195207753Smm{
196207753Smm	// There must be at least one filter.
197207753Smm	if (filters == NULL || filters[0].id == LZMA_VLI_UNKNOWN)
198207753Smm		return LZMA_PROG_ERROR;
199207753Smm
200207753Smm	// Number of non-last filters that may change the size of the data
201207753Smm	// significantly (that is, more than 1-2 % or so).
202207753Smm	size_t changes_size_count = 0;
203207753Smm
204207753Smm	// True if it is OK to add a new filter after the current filter.
205207753Smm	bool non_last_ok = true;
206207753Smm
207207753Smm	// True if the last filter in the given chain is actually usable as
208207753Smm	// the last filter. Only filters that support embedding End of Payload
209207753Smm	// Marker can be used as the last filter in the chain.
210207753Smm	bool last_ok = false;
211207753Smm
212207753Smm	size_t i = 0;
213207753Smm	do {
214207753Smm		size_t j;
215207753Smm		for (j = 0; filters[i].id != features[j].id; ++j)
216207753Smm			if (features[j].id == LZMA_VLI_UNKNOWN)
217207753Smm				return LZMA_OPTIONS_ERROR;
218207753Smm
219207753Smm		// If the previous filter in the chain cannot be a non-last
220207753Smm		// filter, the chain is invalid.
221207753Smm		if (!non_last_ok)
222207753Smm			return LZMA_OPTIONS_ERROR;
223207753Smm
224207753Smm		non_last_ok = features[j].non_last_ok;
225207753Smm		last_ok = features[j].last_ok;
226207753Smm		changes_size_count += features[j].changes_size;
227207753Smm
228207753Smm	} while (filters[++i].id != LZMA_VLI_UNKNOWN);
229207753Smm
230207753Smm	// There must be 1-4 filters. The last filter must be usable as
231207753Smm	// the last filter in the chain. A maximum of three filters are
232207753Smm	// allowed to change the size of the data.
233207753Smm	if (i > LZMA_FILTERS_MAX || !last_ok || changes_size_count > 3)
234207753Smm		return LZMA_OPTIONS_ERROR;
235207753Smm
236207753Smm	*count = i;
237207753Smm	return LZMA_OK;
238207753Smm}
239207753Smm
240207753Smm
241207753Smmextern lzma_ret
242278433Srpaulolzma_raw_coder_init(lzma_next_coder *next, const lzma_allocator *allocator,
243207753Smm		const lzma_filter *options,
244207753Smm		lzma_filter_find coder_find, bool is_encoder)
245207753Smm{
246207753Smm	// Do some basic validation and get the number of filters.
247207753Smm	size_t count;
248207753Smm	return_if_error(validate_chain(options, &count));
249207753Smm
250207753Smm	// Set the filter functions and copy the options pointer.
251207753Smm	lzma_filter_info filters[LZMA_FILTERS_MAX + 1];
252207753Smm	if (is_encoder) {
253207753Smm		for (size_t i = 0; i < count; ++i) {
254207753Smm			// The order of the filters is reversed in the
255207753Smm			// encoder. It allows more efficient handling
256207753Smm			// of the uncompressed data.
257207753Smm			const size_t j = count - i - 1;
258207753Smm
259207753Smm			const lzma_filter_coder *const fc
260207753Smm					= coder_find(options[i].id);
261207753Smm			if (fc == NULL || fc->init == NULL)
262207753Smm				return LZMA_OPTIONS_ERROR;
263207753Smm
264207753Smm			filters[j].id = options[i].id;
265207753Smm			filters[j].init = fc->init;
266207753Smm			filters[j].options = options[i].options;
267207753Smm		}
268207753Smm	} else {
269207753Smm		for (size_t i = 0; i < count; ++i) {
270207753Smm			const lzma_filter_coder *const fc
271207753Smm					= coder_find(options[i].id);
272207753Smm			if (fc == NULL || fc->init == NULL)
273207753Smm				return LZMA_OPTIONS_ERROR;
274207753Smm
275207753Smm			filters[i].id = options[i].id;
276207753Smm			filters[i].init = fc->init;
277207753Smm			filters[i].options = options[i].options;
278207753Smm		}
279207753Smm	}
280207753Smm
281207753Smm	// Terminate the array.
282207753Smm	filters[count].id = LZMA_VLI_UNKNOWN;
283207753Smm	filters[count].init = NULL;
284207753Smm
285207753Smm	// Initialize the filters.
286207753Smm	const lzma_ret ret = lzma_next_filter_init(next, allocator, filters);
287207753Smm	if (ret != LZMA_OK)
288207753Smm		lzma_next_end(next, allocator);
289207753Smm
290207753Smm	return ret;
291207753Smm}
292207753Smm
293207753Smm
294207753Smmextern uint64_t
295207753Smmlzma_raw_coder_memusage(lzma_filter_find coder_find,
296207753Smm		const lzma_filter *filters)
297207753Smm{
298207753Smm	// The chain has to have at least one filter.
299207753Smm	{
300207753Smm		size_t tmp;
301207753Smm		if (validate_chain(filters, &tmp) != LZMA_OK)
302207753Smm			return UINT64_MAX;
303207753Smm	}
304207753Smm
305207753Smm	uint64_t total = 0;
306207753Smm	size_t i = 0;
307207753Smm
308207753Smm	do {
309207753Smm		const lzma_filter_coder *const fc
310207753Smm				 = coder_find(filters[i].id);
311207753Smm		if (fc == NULL)
312207753Smm			return UINT64_MAX; // Unsupported Filter ID
313207753Smm
314207753Smm		if (fc->memusage == NULL) {
315207753Smm			// This filter doesn't have a function to calculate
316207753Smm			// the memory usage and validate the options. Such
317207753Smm			// filters need only little memory, so we use 1 KiB
318207753Smm			// as a good estimate. They also accept all possible
319207753Smm			// options, so there's no need to worry about lack
320207753Smm			// of validation.
321207753Smm			total += 1024;
322207753Smm		} else {
323207753Smm			// Call the filter-specific memory usage calculation
324207753Smm			// function.
325207753Smm			const uint64_t usage
326207753Smm					= fc->memusage(filters[i].options);
327207753Smm			if (usage == UINT64_MAX)
328207753Smm				return UINT64_MAX; // Invalid options
329207753Smm
330207753Smm			total += usage;
331207753Smm		}
332207753Smm	} while (filters[++i].id != LZMA_VLI_UNKNOWN);
333207753Smm
334207753Smm	// Add some fixed amount of extra. It's to compensate memory usage
335207753Smm	// of Stream, Block etc. coders, malloc() overhead, stack etc.
336207753Smm	return total + LZMA_MEMUSAGE_BASE;
337207753Smm}
338