1 ///////////////////////////////////////////////////////////////////////////////
3 /// \file filter_common.c
4 /// \brief Filter-specific stuff common for both encoder and decoder
6 // Author: Lasse Collin
8 // This file has been put into the public domain.
9 // You can do whatever you want with this file.
11 ///////////////////////////////////////////////////////////////////////////////
13 #include "filter_common.h"
20 /// True if it is OK to use this filter as non-last filter in
24 /// True if it is OK to use this filter as the last filter in
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).
34 #if defined (HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1)
36 .id = LZMA_FILTER_LZMA1,
42 #ifdef HAVE_DECODER_LZMA2
44 .id = LZMA_FILTER_LZMA2,
50 #if defined(HAVE_ENCODER_SUBBLOCK) || defined(HAVE_DECODER_SUBBLOCK)
52 .id = LZMA_FILTER_SUBBLOCK,
58 #ifdef HAVE_DECODER_X86
60 .id = LZMA_FILTER_X86,
63 .changes_size = false,
66 #if defined(HAVE_ENCODER_POWERPC) || defined(HAVE_DECODER_POWERPC)
68 .id = LZMA_FILTER_POWERPC,
71 .changes_size = false,
74 #ifdef HAVE_DECODER_IA64
76 .id = LZMA_FILTER_IA64,
79 .changes_size = false,
82 #if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM)
84 .id = LZMA_FILTER_ARM,
87 .changes_size = false,
90 #if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB)
92 .id = LZMA_FILTER_ARMTHUMB,
95 .changes_size = false,
98 #if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC)
100 .id = LZMA_FILTER_SPARC,
103 .changes_size = false,
106 #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA)
108 .id = LZMA_FILTER_DELTA,
111 .changes_size = false,
115 .id = LZMA_VLI_UNKNOWN
121 validate_chain(const lzma_filter *filters, size_t *count)
123 // There must be at least one filter.
124 if (filters == NULL || filters[0].id == LZMA_VLI_UNKNOWN)
125 return LZMA_PROG_ERROR;
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;
131 // True if it is OK to add a new filter after the current filter.
132 bool non_last_ok = true;
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;
142 for (j = 0; filters[i].id != features[j].id; ++j)
143 if (features[j].id == LZMA_VLI_UNKNOWN)
144 return LZMA_OPTIONS_ERROR;
146 // If the previous filter in the chain cannot be a non-last
147 // filter, the chain is invalid.
149 return LZMA_OPTIONS_ERROR;
151 non_last_ok = features[j].non_last_ok;
152 last_ok = features[j].last_ok;
153 changes_size_count += features[j].changes_size;
155 } while (filters[++i].id != LZMA_VLI_UNKNOWN);
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;
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)
173 // Do some basic validation and get the number of filters.
175 return_if_error(validate_chain(options, &count));
177 // Set the filter functions and copy the options pointer.
178 lzma_filter_info filters[LZMA_FILTERS_MAX + 1];
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;
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;
191 filters[j].init = fc->init;
192 filters[j].options = options[i].options;
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;
201 filters[i].init = fc->init;
202 filters[i].options = options[i].options;
206 // Terminate the array.
207 filters[count].init = NULL;
209 // Initialize the filters.
210 const lzma_ret ret = lzma_next_filter_init(next, allocator, filters);
212 lzma_next_end(next, allocator);
219 lzma_raw_coder_memusage(lzma_filter_find coder_find,
220 const lzma_filter *filters)
222 // The chain has to have at least one filter.
225 if (validate_chain(filters, &tmp) != LZMA_OK)
233 const lzma_filter_coder *const fc
234 = coder_find(filters[i].id);
236 return UINT64_MAX; // Unsupported Filter ID
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
247 // Call the filter-specific memory usage calculation
250 = fc->memusage(filters[i].options);
251 if (usage == UINT64_MAX)
252 return UINT64_MAX; // Invalid options
256 } while (filters[++i].id != LZMA_VLI_UNKNOWN);
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;