]> icculus.org git repositories - icculus/xz.git/blob - src/liblzma/common/filter_common.c
Beta was supposed to be API stable but I had forgot to rename
[icculus/xz.git] / 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 //  Copyright (C) 2008 Lasse Collin
7 //
8 //  This library is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU Lesser General Public
10 //  License as published by the Free Software Foundation; either
11 //  version 2.1 of the License, or (at your option) any later version.
12 //
13 //  This library is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 //  Lesser General Public License for more details.
17 //
18 ///////////////////////////////////////////////////////////////////////////////
19
20 #include "filter_common.h"
21
22
23 static const struct {
24         /// Filter ID
25         lzma_vli id;
26
27         /// True if it is OK to use this filter as non-last filter in
28         /// the chain.
29         bool non_last_ok;
30
31         /// True if it is OK to use this filter as the last filter in
32         /// the chain.
33         bool last_ok;
34
35         /// True if the filter may change the size of the data (that is, the
36         /// amount of encoded output can be different than the amount of
37         /// uncompressed input).
38         bool changes_size;
39
40 } features[] = {
41 #if defined (HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1)
42         {
43                 .id = LZMA_FILTER_LZMA1,
44                 .non_last_ok = false,
45                 .last_ok = true,
46                 .changes_size = true,
47         },
48 #endif
49 #ifdef HAVE_DECODER_LZMA2
50         {
51                 .id = LZMA_FILTER_LZMA2,
52                 .non_last_ok = false,
53                 .last_ok = true,
54                 .changes_size = true,
55         },
56 #endif
57 #if defined(HAVE_ENCODER_SUBBLOCK) || defined(HAVE_DECODER_SUBBLOCK)
58         {
59                 .id = LZMA_FILTER_SUBBLOCK,
60                 .non_last_ok = true,
61                 .last_ok = true,
62                 .changes_size = true,
63         },
64 #endif
65 #ifdef HAVE_DECODER_X86
66         {
67                 .id = LZMA_FILTER_X86,
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                 .non_last_ok = true,
77                 .last_ok = false,
78                 .changes_size = false,
79         },
80 #endif
81 #ifdef HAVE_DECODER_IA64
82         {
83                 .id = LZMA_FILTER_IA64,
84                 .non_last_ok = true,
85                 .last_ok = false,
86                 .changes_size = false,
87         },
88 #endif
89 #if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM)
90         {
91                 .id = LZMA_FILTER_ARM,
92                 .non_last_ok = true,
93                 .last_ok = false,
94                 .changes_size = false,
95         },
96 #endif
97 #if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB)
98         {
99                 .id = LZMA_FILTER_ARMTHUMB,
100                 .non_last_ok = true,
101                 .last_ok = false,
102                 .changes_size = false,
103         },
104 #endif
105 #if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC)
106         {
107                 .id = LZMA_FILTER_SPARC,
108                 .non_last_ok = true,
109                 .last_ok = false,
110                 .changes_size = false,
111         },
112 #endif
113 #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA)
114         {
115                 .id = LZMA_FILTER_DELTA,
116                 .non_last_ok = true,
117                 .last_ok = false,
118                 .changes_size = false,
119         },
120 #endif
121         {
122                 .id = LZMA_VLI_UNKNOWN
123         }
124 };
125
126
127 static lzma_ret
128 validate_chain(const lzma_filter *filters, size_t *count)
129 {
130         // There must be at least one filter.
131         if (filters == NULL || filters[0].id == LZMA_VLI_UNKNOWN)
132                 return LZMA_PROG_ERROR;
133
134         // Number of non-last filters that may change the size of the data
135         // significantly (that is, more than 1-2 % or so).
136         size_t changes_size_count = 0;
137
138         // True if it is OK to add a new filter after the current filter.
139         bool non_last_ok = true;
140
141         // True if the last filter in the given chain is actually usable as
142         // the last filter. Only filters that support embedding End of Payload
143         // Marker can be used as the last filter in the chain.
144         bool last_ok = false;
145
146         size_t i = 0;
147         do {
148                 size_t j;
149                 for (j = 0; filters[i].id != features[j].id; ++j)
150                         if (features[j].id == LZMA_VLI_UNKNOWN)
151                                 return LZMA_OPTIONS_ERROR;
152
153                 // If the previous filter in the chain cannot be a non-last
154                 // filter, the chain is invalid.
155                 if (!non_last_ok)
156                         return LZMA_OPTIONS_ERROR;
157
158                 non_last_ok = features[j].non_last_ok;
159                 last_ok = features[j].last_ok;
160                 changes_size_count += features[j].changes_size;
161
162         } while (filters[++i].id != LZMA_VLI_UNKNOWN);
163
164         // There must be 1-4 filters. The last filter must be usable as
165         // the last filter in the chain. At maximum of three filters are
166         // allowed to change the size of the data.
167         if (i > LZMA_FILTERS_MAX || !last_ok || changes_size_count > 3)
168                 return LZMA_OPTIONS_ERROR;
169
170         *count = i;
171         return LZMA_OK;
172 }
173
174
175 extern lzma_ret
176 lzma_raw_coder_init(lzma_next_coder *next, lzma_allocator *allocator,
177                 const lzma_filter *options,
178                 lzma_filter_find coder_find, bool is_encoder)
179 {
180         // Do some basic validation and get the number of filters.
181         size_t count;
182         return_if_error(validate_chain(options, &count));
183
184         // Set the filter functions and copy the options pointer.
185         lzma_filter_info filters[LZMA_FILTERS_MAX + 1];
186         if (is_encoder) {
187                 for (size_t i = 0; i < count; ++i) {
188                         // The order of the filters is reversed in the
189                         // encoder. It allows more efficient handling
190                         // of the uncompressed data.
191                         const size_t j = count - i - 1;
192
193                         const lzma_filter_coder *const fc
194                                         = coder_find(options[i].id);
195                         if (fc == NULL || fc->init == NULL)
196                                 return LZMA_OPTIONS_ERROR;
197
198                         filters[j].init = fc->init;
199                         filters[j].options = options[i].options;
200                 }
201         } else {
202                 for (size_t i = 0; i < count; ++i) {
203                         const lzma_filter_coder *const fc
204                                         = coder_find(options[i].id);
205                         if (fc == NULL || fc->init == NULL)
206                                 return LZMA_OPTIONS_ERROR;
207
208                         filters[i].init = fc->init;
209                         filters[i].options = options[i].options;
210                 }
211         }
212
213         // Terminate the array.
214         filters[count].init = NULL;
215
216         // Initialize the filters.
217         const lzma_ret ret = lzma_next_filter_init(next, allocator, filters);
218         if (ret != LZMA_OK)
219                 lzma_next_end(next, allocator);
220
221         return ret;
222 }
223
224
225 extern uint64_t
226 lzma_raw_coder_memusage(lzma_filter_find coder_find,
227                 const lzma_filter *filters)
228 {
229         // The chain has to have at least one filter.
230         {
231                 size_t tmp;
232                 if (validate_chain(filters, &tmp) != LZMA_OK)
233                         return UINT64_MAX;
234         }
235
236         uint64_t total = 0;
237         size_t i = 0;
238
239         do {
240                 const lzma_filter_coder *const fc
241                                  = coder_find(filters[i].id);
242                 if (fc == NULL)
243                         return UINT64_MAX; // Unsupported Filter ID
244
245                 if (fc->memusage == NULL) {
246                         // This filter doesn't have a function to calculate
247                         // the memory usage and validate the options. Such
248                         // filters need only little memory, so we use 1 KiB
249                         // as a good estimate. They also accept all possible
250                         // options, so there's no need to worry about lack
251                         // of validation.
252                         total += 1024;
253                 } else {
254                         // Call the filter-specific memory usage calculation
255                         // function.
256                         const uint64_t usage
257                                         = fc->memusage(filters[i].options);
258                         if (usage == UINT64_MAX)
259                                 return UINT64_MAX; // Invalid options
260
261                         total += usage;
262                 }
263         } while (filters[++i].id != LZMA_VLI_UNKNOWN);
264
265         // Add some fixed amount of extra. It's to compensate memory usage
266         // of Stream, Block etc. coders, malloc() overhead, stack etc.
267         return total + LZMA_MEMUSAGE_BASE;
268 }