]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/xz/src/liblzma/common/filter_common.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / xz / src / liblzma / common / filter_common.c
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                 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, 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 }