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