liblzma: Add RISC-V BCJ filter.
[xz.git] / src / liblzma / common / filter_common.c
blobbff68b613cdd1227ea2ed986a9addd77ae92b77e
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.
11 ///////////////////////////////////////////////////////////////////////////////
13 #include "filter_common.h"
16 static const struct {
17 /// Filter ID
18 lzma_vli id;
20 /// Size of the filter-specific options structure
21 size_t options_size;
23 /// True if it is OK to use this filter as non-last filter in
24 /// the chain.
25 bool non_last_ok;
27 /// True if it is OK to use this filter as the last filter in
28 /// the chain.
29 bool last_ok;
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;
36 } features[] = {
37 #if defined (HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1)
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,
46 .id = LZMA_FILTER_LZMA1EXT,
47 .options_size = sizeof(lzma_options_lzma),
48 .non_last_ok = false,
49 .last_ok = true,
50 .changes_size = true,
52 #endif
53 #if defined(HAVE_ENCODER_LZMA2) || defined(HAVE_DECODER_LZMA2)
55 .id = LZMA_FILTER_LZMA2,
56 .options_size = sizeof(lzma_options_lzma),
57 .non_last_ok = false,
58 .last_ok = true,
59 .changes_size = true,
61 #endif
62 #if defined(HAVE_ENCODER_X86) || defined(HAVE_DECODER_X86)
64 .id = LZMA_FILTER_X86,
65 .options_size = sizeof(lzma_options_bcj),
66 .non_last_ok = true,
67 .last_ok = false,
68 .changes_size = false,
70 #endif
71 #if defined(HAVE_ENCODER_POWERPC) || defined(HAVE_DECODER_POWERPC)
73 .id = LZMA_FILTER_POWERPC,
74 .options_size = sizeof(lzma_options_bcj),
75 .non_last_ok = true,
76 .last_ok = false,
77 .changes_size = false,
79 #endif
80 #if defined(HAVE_ENCODER_IA64) || defined(HAVE_DECODER_IA64)
82 .id = LZMA_FILTER_IA64,
83 .options_size = sizeof(lzma_options_bcj),
84 .non_last_ok = true,
85 .last_ok = false,
86 .changes_size = false,
88 #endif
89 #if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM)
91 .id = LZMA_FILTER_ARM,
92 .options_size = sizeof(lzma_options_bcj),
93 .non_last_ok = true,
94 .last_ok = false,
95 .changes_size = false,
97 #endif
98 #if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB)
100 .id = LZMA_FILTER_ARMTHUMB,
101 .options_size = sizeof(lzma_options_bcj),
102 .non_last_ok = true,
103 .last_ok = false,
104 .changes_size = false,
106 #endif
107 #if defined(HAVE_ENCODER_ARM64) || defined(HAVE_DECODER_ARM64)
109 .id = LZMA_FILTER_ARM64,
110 .options_size = sizeof(lzma_options_bcj),
111 .non_last_ok = true,
112 .last_ok = false,
113 .changes_size = false,
115 #endif
116 #if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC)
118 .id = LZMA_FILTER_SPARC,
119 .options_size = sizeof(lzma_options_bcj),
120 .non_last_ok = true,
121 .last_ok = false,
122 .changes_size = false,
124 #endif
125 #if defined(HAVE_ENCODER_RISCV) || defined(HAVE_DECODER_RISCV)
127 .id = LZMA_FILTER_RISCV,
128 .options_size = sizeof(lzma_options_bcj),
129 .non_last_ok = true,
130 .last_ok = false,
131 .changes_size = false,
133 #endif
134 #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA)
136 .id = LZMA_FILTER_DELTA,
137 .options_size = sizeof(lzma_options_delta),
138 .non_last_ok = true,
139 .last_ok = false,
140 .changes_size = false,
142 #endif
144 .id = LZMA_VLI_UNKNOWN
149 extern LZMA_API(lzma_ret)
150 lzma_filters_copy(const lzma_filter *src, lzma_filter *real_dest,
151 const lzma_allocator *allocator)
153 if (src == NULL || real_dest == NULL)
154 return LZMA_PROG_ERROR;
156 // Use a temporary destination so that the real destination
157 // will never be modied if an error occurs.
158 lzma_filter dest[LZMA_FILTERS_MAX + 1];
160 lzma_ret ret;
161 size_t i;
162 for (i = 0; src[i].id != LZMA_VLI_UNKNOWN; ++i) {
163 // There must be a maximum of four filters plus
164 // the array terminator.
165 if (i == LZMA_FILTERS_MAX) {
166 ret = LZMA_OPTIONS_ERROR;
167 goto error;
170 dest[i].id = src[i].id;
172 if (src[i].options == NULL) {
173 dest[i].options = NULL;
174 } else {
175 // See if the filter is supported only when the
176 // options is not NULL. This might be convenient
177 // sometimes if the app is actually copying only
178 // a partial filter chain with a place holder ID.
180 // When options is not NULL, the Filter ID must be
181 // supported by us, because otherwise we don't know
182 // how big the options are.
183 size_t j;
184 for (j = 0; src[i].id != features[j].id; ++j) {
185 if (features[j].id == LZMA_VLI_UNKNOWN) {
186 ret = LZMA_OPTIONS_ERROR;
187 goto error;
191 // Allocate and copy the options.
192 dest[i].options = lzma_alloc(features[j].options_size,
193 allocator);
194 if (dest[i].options == NULL) {
195 ret = LZMA_MEM_ERROR;
196 goto error;
199 memcpy(dest[i].options, src[i].options,
200 features[j].options_size);
204 // Terminate the filter array.
205 assert(i < LZMA_FILTERS_MAX + 1);
206 dest[i].id = LZMA_VLI_UNKNOWN;
207 dest[i].options = NULL;
209 // Copy it to the caller-supplied array now that we know that
210 // no errors occurred.
211 memcpy(real_dest, dest, (i + 1) * sizeof(lzma_filter));
213 return LZMA_OK;
215 error:
216 // Free the options which we have already allocated.
217 while (i-- > 0)
218 lzma_free(dest[i].options, allocator);
220 return ret;
224 extern LZMA_API(void)
225 lzma_filters_free(lzma_filter *filters, const lzma_allocator *allocator)
227 if (filters == NULL)
228 return;
230 for (size_t i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i) {
231 if (i == LZMA_FILTERS_MAX) {
232 // The API says that LZMA_FILTERS_MAX + 1 is the
233 // maximum allowed size including the terminating
234 // element. Thus, we should never get here but in
235 // case there is a bug and we do anyway, don't go
236 // past the (probable) end of the array.
237 assert(0);
238 break;
241 lzma_free(filters[i].options, allocator);
242 filters[i].options = NULL;
243 filters[i].id = LZMA_VLI_UNKNOWN;
246 return;
250 extern lzma_ret
251 lzma_validate_chain(const lzma_filter *filters, size_t *count)
253 // There must be at least one filter.
254 if (filters == NULL || filters[0].id == LZMA_VLI_UNKNOWN)
255 return LZMA_PROG_ERROR;
257 // Number of non-last filters that may change the size of the data
258 // significantly (that is, more than 1-2 % or so).
259 size_t changes_size_count = 0;
261 // True if it is OK to add a new filter after the current filter.
262 bool non_last_ok = true;
264 // True if the last filter in the given chain is actually usable as
265 // the last filter. Only filters that support embedding End of Payload
266 // Marker can be used as the last filter in the chain.
267 bool last_ok = false;
269 size_t i = 0;
270 do {
271 size_t j;
272 for (j = 0; filters[i].id != features[j].id; ++j)
273 if (features[j].id == LZMA_VLI_UNKNOWN)
274 return LZMA_OPTIONS_ERROR;
276 // If the previous filter in the chain cannot be a non-last
277 // filter, the chain is invalid.
278 if (!non_last_ok)
279 return LZMA_OPTIONS_ERROR;
281 non_last_ok = features[j].non_last_ok;
282 last_ok = features[j].last_ok;
283 changes_size_count += features[j].changes_size;
285 } while (filters[++i].id != LZMA_VLI_UNKNOWN);
287 // There must be 1-4 filters. The last filter must be usable as
288 // the last filter in the chain. A maximum of three filters are
289 // allowed to change the size of the data.
290 if (i > LZMA_FILTERS_MAX || !last_ok || changes_size_count > 3)
291 return LZMA_OPTIONS_ERROR;
293 *count = i;
294 return LZMA_OK;
298 extern lzma_ret
299 lzma_raw_coder_init(lzma_next_coder *next, const lzma_allocator *allocator,
300 const lzma_filter *options,
301 lzma_filter_find coder_find, bool is_encoder)
303 // Do some basic validation and get the number of filters.
304 size_t count;
305 return_if_error(lzma_validate_chain(options, &count));
307 // Set the filter functions and copy the options pointer.
308 lzma_filter_info filters[LZMA_FILTERS_MAX + 1];
309 if (is_encoder) {
310 for (size_t i = 0; i < count; ++i) {
311 // The order of the filters is reversed in the
312 // encoder. It allows more efficient handling
313 // of the uncompressed data.
314 const size_t j = count - i - 1;
316 const lzma_filter_coder *const fc
317 = coder_find(options[i].id);
318 if (fc == NULL || fc->init == NULL)
319 return LZMA_OPTIONS_ERROR;
321 filters[j].id = options[i].id;
322 filters[j].init = fc->init;
323 filters[j].options = options[i].options;
325 } else {
326 for (size_t i = 0; i < count; ++i) {
327 const lzma_filter_coder *const fc
328 = coder_find(options[i].id);
329 if (fc == NULL || fc->init == NULL)
330 return LZMA_OPTIONS_ERROR;
332 filters[i].id = options[i].id;
333 filters[i].init = fc->init;
334 filters[i].options = options[i].options;
338 // Terminate the array.
339 filters[count].id = LZMA_VLI_UNKNOWN;
340 filters[count].init = NULL;
342 // Initialize the filters.
343 const lzma_ret ret = lzma_next_filter_init(next, allocator, filters);
344 if (ret != LZMA_OK)
345 lzma_next_end(next, allocator);
347 return ret;
351 extern uint64_t
352 lzma_raw_coder_memusage(lzma_filter_find coder_find,
353 const lzma_filter *filters)
355 // The chain has to have at least one filter.
357 size_t tmp;
358 if (lzma_validate_chain(filters, &tmp) != LZMA_OK)
359 return UINT64_MAX;
362 uint64_t total = 0;
363 size_t i = 0;
365 do {
366 const lzma_filter_coder *const fc
367 = coder_find(filters[i].id);
368 if (fc == NULL)
369 return UINT64_MAX; // Unsupported Filter ID
371 if (fc->memusage == NULL) {
372 // This filter doesn't have a function to calculate
373 // the memory usage and validate the options. Such
374 // filters need only little memory, so we use 1 KiB
375 // as a good estimate. They also accept all possible
376 // options, so there's no need to worry about lack
377 // of validation.
378 total += 1024;
379 } else {
380 // Call the filter-specific memory usage calculation
381 // function.
382 const uint64_t usage
383 = fc->memusage(filters[i].options);
384 if (usage == UINT64_MAX)
385 return UINT64_MAX; // Invalid options
387 total += usage;
389 } while (filters[++i].id != LZMA_VLI_UNKNOWN);
391 // Add some fixed amount of extra. It's to compensate memory usage
392 // of Stream, Block etc. coders, malloc() overhead, stack etc.
393 return total + LZMA_MEMUSAGE_BASE;