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)
lzma_filters_copy(const lzma_filter * src,lzma_filter * dest,const lzma_allocator * allocator)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
validate_chain(const lzma_filter * filters,size_t * count)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
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 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
lzma_raw_coder_memusage(lzma_filter_find coder_find,const lzma_filter * filters)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 }
338