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 |
#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 |
|
124 |
extern LZMA_API(lzma_ret) |
125 |
lzma_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 |
|
182 |
error: |
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 |
|
193 |
static lzma_ret |
194 |
validate_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 |
|
241 |
extern lzma_ret |
242 |
lzma_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 |
|
294 |
extern uint64_t |
295 |
lzma_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 |
} |