Index: xz/src/liblzma/common/filter_common.c |
=================================================================== |
--- xz/src/liblzma/common/filter_common.c (revision 0) |
+++ xz/src/liblzma/common/filter_common.c (revision 0) |
@@ -0,0 +1,337 @@ |
+/////////////////////////////////////////////////////////////////////////////// |
+// |
+/// \file filter_common.c |
+/// \brief Filter-specific stuff common for both encoder and decoder |
+// |
+// Author: Lasse Collin |
+// |
+// This file has been put into the public domain. |
+// You can do whatever you want with this file. |
+// |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+#include "filter_common.h" |
+ |
+ |
+static const struct { |
+ /// Filter ID |
+ lzma_vli id; |
+ |
+ /// Size of the filter-specific options structure |
+ size_t options_size; |
+ |
+ /// True if it is OK to use this filter as non-last filter in |
+ /// the chain. |
+ bool non_last_ok; |
+ |
+ /// True if it is OK to use this filter as the last filter in |
+ /// the chain. |
+ bool last_ok; |
+ |
+ /// True if the filter may change the size of the data (that is, the |
+ /// amount of encoded output can be different than the amount of |
+ /// uncompressed input). |
+ bool changes_size; |
+ |
+} features[] = { |
+#if defined (HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1) |
+ { |
+ .id = LZMA_FILTER_LZMA1, |
+ .options_size = sizeof(lzma_options_lzma), |
+ .non_last_ok = false, |
+ .last_ok = true, |
+ .changes_size = true, |
+ }, |
+#endif |
+#ifdef HAVE_DECODER_LZMA2 |
+ { |
+ .id = LZMA_FILTER_LZMA2, |
+ .options_size = sizeof(lzma_options_lzma), |
+ .non_last_ok = false, |
+ .last_ok = true, |
+ .changes_size = true, |
+ }, |
+#endif |
+#ifdef HAVE_DECODER_X86 |
+ { |
+ .id = LZMA_FILTER_X86, |
+ .options_size = sizeof(lzma_options_bcj), |
+ .non_last_ok = true, |
+ .last_ok = false, |
+ .changes_size = false, |
+ }, |
+#endif |
+#if defined(HAVE_ENCODER_POWERPC) || defined(HAVE_DECODER_POWERPC) |
+ { |
+ .id = LZMA_FILTER_POWERPC, |
+ .options_size = sizeof(lzma_options_bcj), |
+ .non_last_ok = true, |
+ .last_ok = false, |
+ .changes_size = false, |
+ }, |
+#endif |
+#ifdef HAVE_DECODER_IA64 |
+ { |
+ .id = LZMA_FILTER_IA64, |
+ .options_size = sizeof(lzma_options_bcj), |
+ .non_last_ok = true, |
+ .last_ok = false, |
+ .changes_size = false, |
+ }, |
+#endif |
+#if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM) |
+ { |
+ .id = LZMA_FILTER_ARM, |
+ .options_size = sizeof(lzma_options_bcj), |
+ .non_last_ok = true, |
+ .last_ok = false, |
+ .changes_size = false, |
+ }, |
+#endif |
+#if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB) |
+ { |
+ .id = LZMA_FILTER_ARMTHUMB, |
+ .options_size = sizeof(lzma_options_bcj), |
+ .non_last_ok = true, |
+ .last_ok = false, |
+ .changes_size = false, |
+ }, |
+#endif |
+#if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC) |
+ { |
+ .id = LZMA_FILTER_SPARC, |
+ .options_size = sizeof(lzma_options_bcj), |
+ .non_last_ok = true, |
+ .last_ok = false, |
+ .changes_size = false, |
+ }, |
+#endif |
+#if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) |
+ { |
+ .id = LZMA_FILTER_DELTA, |
+ .options_size = sizeof(lzma_options_delta), |
+ .non_last_ok = true, |
+ .last_ok = false, |
+ .changes_size = false, |
+ }, |
+#endif |
+ { |
+ .id = LZMA_VLI_UNKNOWN |
+ } |
+}; |
+ |
+ |
+extern LZMA_API(lzma_ret) |
+lzma_filters_copy(const lzma_filter *src, lzma_filter *dest, |
+ lzma_allocator *allocator) |
+{ |
+ if (src == NULL || dest == NULL) |
+ return LZMA_PROG_ERROR; |
+ |
+ lzma_ret ret; |
+ size_t i; |
+ for (i = 0; src[i].id != LZMA_VLI_UNKNOWN; ++i) { |
+ // There must be a maximum of four filters plus |
+ // the array terminator. |
+ if (i == LZMA_FILTERS_MAX) { |
+ ret = LZMA_OPTIONS_ERROR; |
+ goto error; |
+ } |
+ |
+ dest[i].id = src[i].id; |
+ |
+ if (src[i].options == NULL) { |
+ dest[i].options = NULL; |
+ } else { |
+ // See if the filter is supported only when the |
+ // options is not NULL. This might be convenient |
+ // sometimes if the app is actually copying only |
+ // a partial filter chain with a place holder ID. |
+ // |
+ // When options is not NULL, the Filter ID must be |
+ // supported by us, because otherwise we don't know |
+ // how big the options are. |
+ size_t j; |
+ for (j = 0; src[i].id != features[j].id; ++j) { |
+ if (features[j].id == LZMA_VLI_UNKNOWN) { |
+ ret = LZMA_OPTIONS_ERROR; |
+ goto error; |
+ } |
+ } |
+ |
+ // Allocate and copy the options. |
+ dest[i].options = lzma_alloc(features[j].options_size, |
+ allocator); |
+ if (dest[i].options == NULL) { |
+ ret = LZMA_MEM_ERROR; |
+ goto error; |
+ } |
+ |
+ memcpy(dest[i].options, src[i].options, |
+ features[j].options_size); |
+ } |
+ } |
+ |
+ // Terminate the filter array. |
+ assert(i <= LZMA_FILTERS_MAX + 1); |
+ dest[i].id = LZMA_VLI_UNKNOWN; |
+ dest[i].options = NULL; |
+ |
+ return LZMA_OK; |
+ |
+error: |
+ // Free the options which we have already allocated. |
+ while (i-- > 0) { |
+ lzma_free(dest[i].options, allocator); |
+ dest[i].options = NULL; |
+ } |
+ |
+ return ret; |
+} |
+ |
+ |
+static lzma_ret |
+validate_chain(const lzma_filter *filters, size_t *count) |
+{ |
+ // There must be at least one filter. |
+ if (filters == NULL || filters[0].id == LZMA_VLI_UNKNOWN) |
+ return LZMA_PROG_ERROR; |
+ |
+ // Number of non-last filters that may change the size of the data |
+ // significantly (that is, more than 1-2 % or so). |
+ size_t changes_size_count = 0; |
+ |
+ // True if it is OK to add a new filter after the current filter. |
+ bool non_last_ok = true; |
+ |
+ // True if the last filter in the given chain is actually usable as |
+ // the last filter. Only filters that support embedding End of Payload |
+ // Marker can be used as the last filter in the chain. |
+ bool last_ok = false; |
+ |
+ size_t i = 0; |
+ do { |
+ size_t j; |
+ for (j = 0; filters[i].id != features[j].id; ++j) |
+ if (features[j].id == LZMA_VLI_UNKNOWN) |
+ return LZMA_OPTIONS_ERROR; |
+ |
+ // If the previous filter in the chain cannot be a non-last |
+ // filter, the chain is invalid. |
+ if (!non_last_ok) |
+ return LZMA_OPTIONS_ERROR; |
+ |
+ non_last_ok = features[j].non_last_ok; |
+ last_ok = features[j].last_ok; |
+ changes_size_count += features[j].changes_size; |
+ |
+ } while (filters[++i].id != LZMA_VLI_UNKNOWN); |
+ |
+ // There must be 1-4 filters. The last filter must be usable as |
+ // the last filter in the chain. A maximum of three filters are |
+ // allowed to change the size of the data. |
+ if (i > LZMA_FILTERS_MAX || !last_ok || changes_size_count > 3) |
+ return LZMA_OPTIONS_ERROR; |
+ |
+ *count = i; |
+ return LZMA_OK; |
+} |
+ |
+ |
+extern lzma_ret |
+lzma_raw_coder_init(lzma_next_coder *next, lzma_allocator *allocator, |
+ const lzma_filter *options, |
+ lzma_filter_find coder_find, bool is_encoder) |
+{ |
+ // Do some basic validation and get the number of filters. |
+ size_t count; |
+ return_if_error(validate_chain(options, &count)); |
+ |
+ // Set the filter functions and copy the options pointer. |
+ lzma_filter_info filters[LZMA_FILTERS_MAX + 1]; |
+ if (is_encoder) { |
+ for (size_t i = 0; i < count; ++i) { |
+ // The order of the filters is reversed in the |
+ // encoder. It allows more efficient handling |
+ // of the uncompressed data. |
+ const size_t j = count - i - 1; |
+ |
+ const lzma_filter_coder *const fc |
+ = coder_find(options[i].id); |
+ if (fc == NULL || fc->init == NULL) |
+ return LZMA_OPTIONS_ERROR; |
+ |
+ filters[j].id = options[i].id; |
+ filters[j].init = fc->init; |
+ filters[j].options = options[i].options; |
+ } |
+ } else { |
+ for (size_t i = 0; i < count; ++i) { |
+ const lzma_filter_coder *const fc |
+ = coder_find(options[i].id); |
+ if (fc == NULL || fc->init == NULL) |
+ return LZMA_OPTIONS_ERROR; |
+ |
+ filters[i].id = options[i].id; |
+ filters[i].init = fc->init; |
+ filters[i].options = options[i].options; |
+ } |
+ } |
+ |
+ // Terminate the array. |
+ filters[count].id = LZMA_VLI_UNKNOWN; |
+ filters[count].init = NULL; |
+ |
+ // Initialize the filters. |
+ const lzma_ret ret = lzma_next_filter_init(next, allocator, filters); |
+ if (ret != LZMA_OK) |
+ lzma_next_end(next, allocator); |
+ |
+ return ret; |
+} |
+ |
+ |
+extern uint64_t |
+lzma_raw_coder_memusage(lzma_filter_find coder_find, |
+ const lzma_filter *filters) |
+{ |
+ // The chain has to have at least one filter. |
+ { |
+ size_t tmp; |
+ if (validate_chain(filters, &tmp) != LZMA_OK) |
+ return UINT64_MAX; |
+ } |
+ |
+ uint64_t total = 0; |
+ size_t i = 0; |
+ |
+ do { |
+ const lzma_filter_coder *const fc |
+ = coder_find(filters[i].id); |
+ if (fc == NULL) |
+ return UINT64_MAX; // Unsupported Filter ID |
+ |
+ if (fc->memusage == NULL) { |
+ // This filter doesn't have a function to calculate |
+ // the memory usage and validate the options. Such |
+ // filters need only little memory, so we use 1 KiB |
+ // as a good estimate. They also accept all possible |
+ // options, so there's no need to worry about lack |
+ // of validation. |
+ total += 1024; |
+ } else { |
+ // Call the filter-specific memory usage calculation |
+ // function. |
+ const uint64_t usage |
+ = fc->memusage(filters[i].options); |
+ if (usage == UINT64_MAX) |
+ return UINT64_MAX; // Invalid options |
+ |
+ total += usage; |
+ } |
+ } while (filters[++i].id != LZMA_VLI_UNKNOWN); |
+ |
+ // Add some fixed amount of extra. It's to compensate memory usage |
+ // of Stream, Block etc. coders, malloc() overhead, stack etc. |
+ return total + LZMA_MEMUSAGE_BASE; |
+} |
Property changes on: xz/src/liblzma/common/filter_common.c |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |