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 #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 
133 extern LZMA_API(lzma_ret)
134 lzma_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 
191 error:
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 
202 static lzma_ret
203 validate_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 
250 extern lzma_ret
251 lzma_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 
303 extern uint64_t
304 lzma_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