12940b44dSPeter Avalos ///////////////////////////////////////////////////////////////////////////////
22940b44dSPeter Avalos //
32940b44dSPeter Avalos /// \file       filter_common.c
42940b44dSPeter Avalos /// \brief      Filter-specific stuff common for both encoder and decoder
52940b44dSPeter Avalos //
62940b44dSPeter Avalos //  Author:     Lasse Collin
72940b44dSPeter Avalos //
82940b44dSPeter Avalos //  This file has been put into the public domain.
92940b44dSPeter Avalos //  You can do whatever you want with this file.
102940b44dSPeter Avalos //
112940b44dSPeter Avalos ///////////////////////////////////////////////////////////////////////////////
122940b44dSPeter Avalos 
132940b44dSPeter Avalos #include "filter_common.h"
142940b44dSPeter Avalos 
152940b44dSPeter Avalos 
162940b44dSPeter Avalos static const struct {
172940b44dSPeter Avalos 	/// Filter ID
182940b44dSPeter Avalos 	lzma_vli id;
192940b44dSPeter Avalos 
202940b44dSPeter Avalos 	/// Size of the filter-specific options structure
212940b44dSPeter Avalos 	size_t options_size;
222940b44dSPeter Avalos 
232940b44dSPeter Avalos 	/// True if it is OK to use this filter as non-last filter in
242940b44dSPeter Avalos 	/// the chain.
252940b44dSPeter Avalos 	bool non_last_ok;
262940b44dSPeter Avalos 
272940b44dSPeter Avalos 	/// True if it is OK to use this filter as the last filter in
282940b44dSPeter Avalos 	/// the chain.
292940b44dSPeter Avalos 	bool last_ok;
302940b44dSPeter Avalos 
312940b44dSPeter Avalos 	/// True if the filter may change the size of the data (that is, the
322940b44dSPeter Avalos 	/// amount of encoded output can be different than the amount of
332940b44dSPeter Avalos 	/// uncompressed input).
342940b44dSPeter Avalos 	bool changes_size;
352940b44dSPeter Avalos 
362940b44dSPeter Avalos } features[] = {
372940b44dSPeter Avalos #if defined (HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1)
382940b44dSPeter Avalos 	{
392940b44dSPeter Avalos 		.id = LZMA_FILTER_LZMA1,
402940b44dSPeter Avalos 		.options_size = sizeof(lzma_options_lzma),
412940b44dSPeter Avalos 		.non_last_ok = false,
422940b44dSPeter Avalos 		.last_ok = true,
432940b44dSPeter Avalos 		.changes_size = true,
442940b44dSPeter Avalos 	},
452940b44dSPeter Avalos #endif
46114db65bSPeter Avalos #if defined(HAVE_ENCODER_LZMA2) || defined(HAVE_DECODER_LZMA2)
472940b44dSPeter Avalos 	{
482940b44dSPeter Avalos 		.id = LZMA_FILTER_LZMA2,
492940b44dSPeter Avalos 		.options_size = sizeof(lzma_options_lzma),
502940b44dSPeter Avalos 		.non_last_ok = false,
512940b44dSPeter Avalos 		.last_ok = true,
522940b44dSPeter Avalos 		.changes_size = true,
532940b44dSPeter Avalos 	},
542940b44dSPeter Avalos #endif
55114db65bSPeter Avalos #if defined(HAVE_ENCODER_X86) || defined(HAVE_DECODER_X86)
562940b44dSPeter Avalos 	{
572940b44dSPeter Avalos 		.id = LZMA_FILTER_X86,
582940b44dSPeter Avalos 		.options_size = sizeof(lzma_options_bcj),
592940b44dSPeter Avalos 		.non_last_ok = true,
602940b44dSPeter Avalos 		.last_ok = false,
612940b44dSPeter Avalos 		.changes_size = false,
622940b44dSPeter Avalos 	},
632940b44dSPeter Avalos #endif
642940b44dSPeter Avalos #if defined(HAVE_ENCODER_POWERPC) || defined(HAVE_DECODER_POWERPC)
652940b44dSPeter Avalos 	{
662940b44dSPeter Avalos 		.id = LZMA_FILTER_POWERPC,
672940b44dSPeter Avalos 		.options_size = sizeof(lzma_options_bcj),
682940b44dSPeter Avalos 		.non_last_ok = true,
692940b44dSPeter Avalos 		.last_ok = false,
702940b44dSPeter Avalos 		.changes_size = false,
712940b44dSPeter Avalos 	},
722940b44dSPeter Avalos #endif
73114db65bSPeter Avalos #if defined(HAVE_ENCODER_IA64) || defined(HAVE_DECODER_IA64)
742940b44dSPeter Avalos 	{
752940b44dSPeter Avalos 		.id = LZMA_FILTER_IA64,
762940b44dSPeter Avalos 		.options_size = sizeof(lzma_options_bcj),
772940b44dSPeter Avalos 		.non_last_ok = true,
782940b44dSPeter Avalos 		.last_ok = false,
792940b44dSPeter Avalos 		.changes_size = false,
802940b44dSPeter Avalos 	},
812940b44dSPeter Avalos #endif
822940b44dSPeter Avalos #if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM)
832940b44dSPeter Avalos 	{
842940b44dSPeter Avalos 		.id = LZMA_FILTER_ARM,
852940b44dSPeter Avalos 		.options_size = sizeof(lzma_options_bcj),
862940b44dSPeter Avalos 		.non_last_ok = true,
872940b44dSPeter Avalos 		.last_ok = false,
882940b44dSPeter Avalos 		.changes_size = false,
892940b44dSPeter Avalos 	},
902940b44dSPeter Avalos #endif
912940b44dSPeter Avalos #if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB)
922940b44dSPeter Avalos 	{
932940b44dSPeter Avalos 		.id = LZMA_FILTER_ARMTHUMB,
942940b44dSPeter Avalos 		.options_size = sizeof(lzma_options_bcj),
952940b44dSPeter Avalos 		.non_last_ok = true,
962940b44dSPeter Avalos 		.last_ok = false,
972940b44dSPeter Avalos 		.changes_size = false,
982940b44dSPeter Avalos 	},
992940b44dSPeter Avalos #endif
1002940b44dSPeter Avalos #if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC)
1012940b44dSPeter Avalos 	{
1022940b44dSPeter Avalos 		.id = LZMA_FILTER_SPARC,
1032940b44dSPeter Avalos 		.options_size = sizeof(lzma_options_bcj),
1042940b44dSPeter Avalos 		.non_last_ok = true,
1052940b44dSPeter Avalos 		.last_ok = false,
1062940b44dSPeter Avalos 		.changes_size = false,
1072940b44dSPeter Avalos 	},
1082940b44dSPeter Avalos #endif
1092940b44dSPeter Avalos #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA)
1102940b44dSPeter Avalos 	{
1112940b44dSPeter Avalos 		.id = LZMA_FILTER_DELTA,
1122940b44dSPeter Avalos 		.options_size = sizeof(lzma_options_delta),
1132940b44dSPeter Avalos 		.non_last_ok = true,
1142940b44dSPeter Avalos 		.last_ok = false,
1152940b44dSPeter Avalos 		.changes_size = false,
1162940b44dSPeter Avalos 	},
1172940b44dSPeter Avalos #endif
1182940b44dSPeter Avalos 	{
1192940b44dSPeter Avalos 		.id = LZMA_VLI_UNKNOWN
1202940b44dSPeter Avalos 	}
1212940b44dSPeter Avalos };
1222940b44dSPeter Avalos 
1232940b44dSPeter Avalos 
1242940b44dSPeter Avalos extern LZMA_API(lzma_ret)
lzma_filters_copy(const lzma_filter * src,lzma_filter * dest,const lzma_allocator * allocator)1252940b44dSPeter Avalos lzma_filters_copy(const lzma_filter *src, lzma_filter *dest,
126*15ab8c86SJohn Marino 		const lzma_allocator *allocator)
1272940b44dSPeter Avalos {
1282940b44dSPeter Avalos 	if (src == NULL || dest == NULL)
1292940b44dSPeter Avalos 		return LZMA_PROG_ERROR;
1302940b44dSPeter Avalos 
1312940b44dSPeter Avalos 	lzma_ret ret;
1322940b44dSPeter Avalos 	size_t i;
1332940b44dSPeter Avalos 	for (i = 0; src[i].id != LZMA_VLI_UNKNOWN; ++i) {
1342940b44dSPeter Avalos 		// There must be a maximum of four filters plus
1352940b44dSPeter Avalos 		// the array terminator.
1362940b44dSPeter Avalos 		if (i == LZMA_FILTERS_MAX) {
1372940b44dSPeter Avalos 			ret = LZMA_OPTIONS_ERROR;
1382940b44dSPeter Avalos 			goto error;
1392940b44dSPeter Avalos 		}
1402940b44dSPeter Avalos 
1412940b44dSPeter Avalos 		dest[i].id = src[i].id;
1422940b44dSPeter Avalos 
1432940b44dSPeter Avalos 		if (src[i].options == NULL) {
1442940b44dSPeter Avalos 			dest[i].options = NULL;
1452940b44dSPeter Avalos 		} else {
1462940b44dSPeter Avalos 			// See if the filter is supported only when the
1472940b44dSPeter Avalos 			// options is not NULL. This might be convenient
1482940b44dSPeter Avalos 			// sometimes if the app is actually copying only
1492940b44dSPeter Avalos 			// a partial filter chain with a place holder ID.
1502940b44dSPeter Avalos 			//
1512940b44dSPeter Avalos 			// When options is not NULL, the Filter ID must be
1522940b44dSPeter Avalos 			// supported by us, because otherwise we don't know
1532940b44dSPeter Avalos 			// how big the options are.
1542940b44dSPeter Avalos 			size_t j;
1552940b44dSPeter Avalos 			for (j = 0; src[i].id != features[j].id; ++j) {
1562940b44dSPeter Avalos 				if (features[j].id == LZMA_VLI_UNKNOWN) {
1572940b44dSPeter Avalos 					ret = LZMA_OPTIONS_ERROR;
1582940b44dSPeter Avalos 					goto error;
1592940b44dSPeter Avalos 				}
1602940b44dSPeter Avalos 			}
1612940b44dSPeter Avalos 
1622940b44dSPeter Avalos 			// Allocate and copy the options.
1632940b44dSPeter Avalos 			dest[i].options = lzma_alloc(features[j].options_size,
1642940b44dSPeter Avalos 					allocator);
1652940b44dSPeter Avalos 			if (dest[i].options == NULL) {
1662940b44dSPeter Avalos 				ret = LZMA_MEM_ERROR;
1672940b44dSPeter Avalos 				goto error;
1682940b44dSPeter Avalos 			}
1692940b44dSPeter Avalos 
1702940b44dSPeter Avalos 			memcpy(dest[i].options, src[i].options,
1712940b44dSPeter Avalos 					features[j].options_size);
1722940b44dSPeter Avalos 		}
1732940b44dSPeter Avalos 	}
1742940b44dSPeter Avalos 
1752940b44dSPeter Avalos 	// Terminate the filter array.
1762940b44dSPeter Avalos 	assert(i <= LZMA_FILTERS_MAX + 1);
1772940b44dSPeter Avalos 	dest[i].id = LZMA_VLI_UNKNOWN;
1782940b44dSPeter Avalos 	dest[i].options = NULL;
1792940b44dSPeter Avalos 
1802940b44dSPeter Avalos 	return LZMA_OK;
1812940b44dSPeter Avalos 
1822940b44dSPeter Avalos error:
1832940b44dSPeter Avalos 	// Free the options which we have already allocated.
1842940b44dSPeter Avalos 	while (i-- > 0) {
1852940b44dSPeter Avalos 		lzma_free(dest[i].options, allocator);
1862940b44dSPeter Avalos 		dest[i].options = NULL;
1872940b44dSPeter Avalos 	}
1882940b44dSPeter Avalos 
1892940b44dSPeter Avalos 	return ret;
1902940b44dSPeter Avalos }
1912940b44dSPeter Avalos 
1922940b44dSPeter Avalos 
1932940b44dSPeter Avalos static lzma_ret
validate_chain(const lzma_filter * filters,size_t * count)1942940b44dSPeter Avalos validate_chain(const lzma_filter *filters, size_t *count)
1952940b44dSPeter Avalos {
1962940b44dSPeter Avalos 	// There must be at least one filter.
1972940b44dSPeter Avalos 	if (filters == NULL || filters[0].id == LZMA_VLI_UNKNOWN)
1982940b44dSPeter Avalos 		return LZMA_PROG_ERROR;
1992940b44dSPeter Avalos 
2002940b44dSPeter Avalos 	// Number of non-last filters that may change the size of the data
2012940b44dSPeter Avalos 	// significantly (that is, more than 1-2 % or so).
2022940b44dSPeter Avalos 	size_t changes_size_count = 0;
2032940b44dSPeter Avalos 
2042940b44dSPeter Avalos 	// True if it is OK to add a new filter after the current filter.
2052940b44dSPeter Avalos 	bool non_last_ok = true;
2062940b44dSPeter Avalos 
2072940b44dSPeter Avalos 	// True if the last filter in the given chain is actually usable as
2082940b44dSPeter Avalos 	// the last filter. Only filters that support embedding End of Payload
2092940b44dSPeter Avalos 	// Marker can be used as the last filter in the chain.
2102940b44dSPeter Avalos 	bool last_ok = false;
2112940b44dSPeter Avalos 
2122940b44dSPeter Avalos 	size_t i = 0;
2132940b44dSPeter Avalos 	do {
2142940b44dSPeter Avalos 		size_t j;
2152940b44dSPeter Avalos 		for (j = 0; filters[i].id != features[j].id; ++j)
2162940b44dSPeter Avalos 			if (features[j].id == LZMA_VLI_UNKNOWN)
2172940b44dSPeter Avalos 				return LZMA_OPTIONS_ERROR;
2182940b44dSPeter Avalos 
2192940b44dSPeter Avalos 		// If the previous filter in the chain cannot be a non-last
2202940b44dSPeter Avalos 		// filter, the chain is invalid.
2212940b44dSPeter Avalos 		if (!non_last_ok)
2222940b44dSPeter Avalos 			return LZMA_OPTIONS_ERROR;
2232940b44dSPeter Avalos 
2242940b44dSPeter Avalos 		non_last_ok = features[j].non_last_ok;
2252940b44dSPeter Avalos 		last_ok = features[j].last_ok;
2262940b44dSPeter Avalos 		changes_size_count += features[j].changes_size;
2272940b44dSPeter Avalos 
2282940b44dSPeter Avalos 	} while (filters[++i].id != LZMA_VLI_UNKNOWN);
2292940b44dSPeter Avalos 
2302940b44dSPeter Avalos 	// There must be 1-4 filters. The last filter must be usable as
2312940b44dSPeter Avalos 	// the last filter in the chain. A maximum of three filters are
2322940b44dSPeter Avalos 	// allowed to change the size of the data.
2332940b44dSPeter Avalos 	if (i > LZMA_FILTERS_MAX || !last_ok || changes_size_count > 3)
2342940b44dSPeter Avalos 		return LZMA_OPTIONS_ERROR;
2352940b44dSPeter Avalos 
2362940b44dSPeter Avalos 	*count = i;
2372940b44dSPeter Avalos 	return LZMA_OK;
2382940b44dSPeter Avalos }
2392940b44dSPeter Avalos 
2402940b44dSPeter Avalos 
2412940b44dSPeter Avalos 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)242*15ab8c86SJohn Marino lzma_raw_coder_init(lzma_next_coder *next, const lzma_allocator *allocator,
2432940b44dSPeter Avalos 		const lzma_filter *options,
2442940b44dSPeter Avalos 		lzma_filter_find coder_find, bool is_encoder)
2452940b44dSPeter Avalos {
2462940b44dSPeter Avalos 	// Do some basic validation and get the number of filters.
2472940b44dSPeter Avalos 	size_t count;
2482940b44dSPeter Avalos 	return_if_error(validate_chain(options, &count));
2492940b44dSPeter Avalos 
2502940b44dSPeter Avalos 	// Set the filter functions and copy the options pointer.
2512940b44dSPeter Avalos 	lzma_filter_info filters[LZMA_FILTERS_MAX + 1];
2522940b44dSPeter Avalos 	if (is_encoder) {
2532940b44dSPeter Avalos 		for (size_t i = 0; i < count; ++i) {
2542940b44dSPeter Avalos 			// The order of the filters is reversed in the
2552940b44dSPeter Avalos 			// encoder. It allows more efficient handling
2562940b44dSPeter Avalos 			// of the uncompressed data.
2572940b44dSPeter Avalos 			const size_t j = count - i - 1;
2582940b44dSPeter Avalos 
2592940b44dSPeter Avalos 			const lzma_filter_coder *const fc
2602940b44dSPeter Avalos 					= coder_find(options[i].id);
2612940b44dSPeter Avalos 			if (fc == NULL || fc->init == NULL)
2622940b44dSPeter Avalos 				return LZMA_OPTIONS_ERROR;
2632940b44dSPeter Avalos 
2642940b44dSPeter Avalos 			filters[j].id = options[i].id;
2652940b44dSPeter Avalos 			filters[j].init = fc->init;
2662940b44dSPeter Avalos 			filters[j].options = options[i].options;
2672940b44dSPeter Avalos 		}
2682940b44dSPeter Avalos 	} else {
2692940b44dSPeter Avalos 		for (size_t i = 0; i < count; ++i) {
2702940b44dSPeter Avalos 			const lzma_filter_coder *const fc
2712940b44dSPeter Avalos 					= coder_find(options[i].id);
2722940b44dSPeter Avalos 			if (fc == NULL || fc->init == NULL)
2732940b44dSPeter Avalos 				return LZMA_OPTIONS_ERROR;
2742940b44dSPeter Avalos 
2752940b44dSPeter Avalos 			filters[i].id = options[i].id;
2762940b44dSPeter Avalos 			filters[i].init = fc->init;
2772940b44dSPeter Avalos 			filters[i].options = options[i].options;
2782940b44dSPeter Avalos 		}
2792940b44dSPeter Avalos 	}
2802940b44dSPeter Avalos 
2812940b44dSPeter Avalos 	// Terminate the array.
2822940b44dSPeter Avalos 	filters[count].id = LZMA_VLI_UNKNOWN;
2832940b44dSPeter Avalos 	filters[count].init = NULL;
2842940b44dSPeter Avalos 
2852940b44dSPeter Avalos 	// Initialize the filters.
2862940b44dSPeter Avalos 	const lzma_ret ret = lzma_next_filter_init(next, allocator, filters);
2872940b44dSPeter Avalos 	if (ret != LZMA_OK)
2882940b44dSPeter Avalos 		lzma_next_end(next, allocator);
2892940b44dSPeter Avalos 
2902940b44dSPeter Avalos 	return ret;
2912940b44dSPeter Avalos }
2922940b44dSPeter Avalos 
2932940b44dSPeter Avalos 
2942940b44dSPeter Avalos extern uint64_t
lzma_raw_coder_memusage(lzma_filter_find coder_find,const lzma_filter * filters)2952940b44dSPeter Avalos lzma_raw_coder_memusage(lzma_filter_find coder_find,
2962940b44dSPeter Avalos 		const lzma_filter *filters)
2972940b44dSPeter Avalos {
2982940b44dSPeter Avalos 	// The chain has to have at least one filter.
2992940b44dSPeter Avalos 	{
3002940b44dSPeter Avalos 		size_t tmp;
3012940b44dSPeter Avalos 		if (validate_chain(filters, &tmp) != LZMA_OK)
3022940b44dSPeter Avalos 			return UINT64_MAX;
3032940b44dSPeter Avalos 	}
3042940b44dSPeter Avalos 
3052940b44dSPeter Avalos 	uint64_t total = 0;
3062940b44dSPeter Avalos 	size_t i = 0;
3072940b44dSPeter Avalos 
3082940b44dSPeter Avalos 	do {
3092940b44dSPeter Avalos 		const lzma_filter_coder *const fc
3102940b44dSPeter Avalos 				 = coder_find(filters[i].id);
3112940b44dSPeter Avalos 		if (fc == NULL)
3122940b44dSPeter Avalos 			return UINT64_MAX; // Unsupported Filter ID
3132940b44dSPeter Avalos 
3142940b44dSPeter Avalos 		if (fc->memusage == NULL) {
3152940b44dSPeter Avalos 			// This filter doesn't have a function to calculate
3162940b44dSPeter Avalos 			// the memory usage and validate the options. Such
3172940b44dSPeter Avalos 			// filters need only little memory, so we use 1 KiB
3182940b44dSPeter Avalos 			// as a good estimate. They also accept all possible
3192940b44dSPeter Avalos 			// options, so there's no need to worry about lack
3202940b44dSPeter Avalos 			// of validation.
3212940b44dSPeter Avalos 			total += 1024;
3222940b44dSPeter Avalos 		} else {
3232940b44dSPeter Avalos 			// Call the filter-specific memory usage calculation
3242940b44dSPeter Avalos 			// function.
3252940b44dSPeter Avalos 			const uint64_t usage
3262940b44dSPeter Avalos 					= fc->memusage(filters[i].options);
3272940b44dSPeter Avalos 			if (usage == UINT64_MAX)
3282940b44dSPeter Avalos 				return UINT64_MAX; // Invalid options
3292940b44dSPeter Avalos 
3302940b44dSPeter Avalos 			total += usage;
3312940b44dSPeter Avalos 		}
3322940b44dSPeter Avalos 	} while (filters[++i].id != LZMA_VLI_UNKNOWN);
3332940b44dSPeter Avalos 
3342940b44dSPeter Avalos 	// Add some fixed amount of extra. It's to compensate memory usage
3352940b44dSPeter Avalos 	// of Stream, Block etc. coders, malloc() overhead, stack etc.
3362940b44dSPeter Avalos 	return total + LZMA_MEMUSAGE_BASE;
3372940b44dSPeter Avalos }
338