aboutsummaryrefslogtreecommitdiff
path: root/src/liblzma/common/raw_common.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/liblzma/common/raw_common.c178
1 files changed, 64 insertions, 114 deletions
diff --git a/src/liblzma/common/raw_common.c b/src/liblzma/common/raw_common.c
index d45bf4de..35252fc2 100644
--- a/src/liblzma/common/raw_common.c
+++ b/src/liblzma/common/raw_common.c
@@ -20,122 +20,81 @@
#include "raw_common.h"
-/// \brief Prepares the filter chain
-///
-/// Prepares the filter chain by setting uncompressed sizes for each filter,
-/// and adding implicit Subblock filter when needed.
-///
-/// \return true if error occurred, false on success.
-///
-static bool
-prepare(lzma_vli *id, lzma_vli *uncompressed_size, bool allow_implicit)
+static lzma_ret
+validate_options(const lzma_options_filter *options, size_t *count)
{
- bool needs_end_of_input = false;
-
- switch (id[0]) {
- case LZMA_FILTER_COPY:
- case LZMA_FILTER_X86:
- case LZMA_FILTER_POWERPC:
- case LZMA_FILTER_IA64:
- case LZMA_FILTER_ARM:
- case LZMA_FILTER_ARMTHUMB:
- case LZMA_FILTER_SPARC:
- case LZMA_FILTER_DELTA:
- uncompressed_size[1] = uncompressed_size[0];
- needs_end_of_input = true;
- break;
-
- case LZMA_FILTER_SUBBLOCK:
- case LZMA_FILTER_LZMA:
- // These change the size of the data unpredictably.
- uncompressed_size[1] = LZMA_VLI_VALUE_UNKNOWN;
- break;
-
- case LZMA_FILTER_SUBBLOCK_HELPER:
- uncompressed_size[1] = uncompressed_size[0];
- break;
-
- default:
- // Unknown filter.
- return true;
- }
+ if (options == NULL)
+ return LZMA_PROG_ERROR;
- // Is this the last filter in the chain?
- if (id[1] == LZMA_VLI_VALUE_UNKNOWN) {
- if (needs_end_of_input && allow_implicit
- && uncompressed_size[0]
- == LZMA_VLI_VALUE_UNKNOWN) {
- // Add implicit Subblock filter.
- id[1] = LZMA_FILTER_SUBBLOCK;
- uncompressed_size[1] = LZMA_VLI_VALUE_UNKNOWN;
- id[2] = LZMA_VLI_VALUE_UNKNOWN;
+ // Number of non-last filters that may change the size of the data
+ // significantly (that is, more than 1-2 % or so).
+ size_t change = 0;
+
+ // 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;
+ for (i = 0; options[i].id != LZMA_VLI_VALUE_UNKNOWN; ++i) {
+ switch (options[i].id) {
+ // Not #ifdeffing these for simplicity.
+ case LZMA_FILTER_X86:
+ case LZMA_FILTER_POWERPC:
+ case LZMA_FILTER_IA64:
+ case LZMA_FILTER_ARM:
+ case LZMA_FILTER_ARMTHUMB:
+ case LZMA_FILTER_SPARC:
+ case LZMA_FILTER_DELTA:
+ // These don't change the size of the data and cannot
+ // be used as the last filter in the chain.
+ last_ok = false;
+ break;
+
+#ifdef HAVE_FILTER_SUBBLOCK
+ case LZMA_FILTER_SUBBLOCK:
+ last_ok = true;
+ ++change;
+ break;
+#endif
+
+#ifdef HAVE_FILTER_LZMA
+ case LZMA_FILTER_LZMA:
+ last_ok = true;
+ break;
+#endif
+
+ default:
+ return LZMA_HEADER_ERROR;
}
-
- return false;
}
- return prepare(id + 1, uncompressed_size + 1, allow_implicit);
+ // There must be 1-4 filters and the last filter must be usable as
+ // the last filter in the chain.
+ if (i == 0 || i > 4 || !last_ok)
+ return LZMA_HEADER_ERROR;
+
+ // At maximum of two non-last filters are allowed to change the
+ // size of the data.
+ if (change > 2)
+ return LZMA_HEADER_ERROR;
+
+ *count = i;
+ return LZMA_OK;
}
extern lzma_ret
lzma_raw_coder_init(lzma_next_coder *next, lzma_allocator *allocator,
- const lzma_options_filter *options, lzma_vli uncompressed_size,
+ const lzma_options_filter *options,
lzma_init_function (*get_function)(lzma_vli id),
- bool allow_implicit, bool is_encoder)
+ bool is_encoder)
{
- if (options == NULL || !lzma_vli_is_valid(uncompressed_size))
- return LZMA_PROG_ERROR;
-
- // Count the number of filters in the chain.
- size_t count = 0;
- while (options[count].id != LZMA_VLI_VALUE_UNKNOWN)
- ++count;
-
- // Allocate enough space from the stack for IDs and uncompressed
- // sizes. We need two extra: possible implicit Subblock and end
- // of array indicator.
- lzma_vli ids[count + 2];
- lzma_vli uncompressed_sizes[count + 2];
- bool using_implicit = false;
-
- uncompressed_sizes[0] = uncompressed_size;
-
- if (count == 0) {
- if (!allow_implicit)
- return LZMA_PROG_ERROR;
-
- count = 1;
- using_implicit = true;
-
- // Special case: no filters were specified, so an implicit
- // Copy or Subblock filter is used.
- if (uncompressed_size == LZMA_VLI_VALUE_UNKNOWN)
- ids[0] = LZMA_FILTER_SUBBLOCK;
- else
- ids[0] = LZMA_FILTER_COPY;
+ // Do some basic validation and get the number of filters.
+ size_t count;
+ return_if_error(validate_options(options, &count));
- ids[1] = LZMA_VLI_VALUE_UNKNOWN;
-
- } else {
- // Prepare the ids[] and uncompressed_sizes[].
- for (size_t i = 0; i < count; ++i)
- ids[i] = options[i].id;
-
- ids[count] = LZMA_VLI_VALUE_UNKNOWN;
-
- if (prepare(ids, uncompressed_sizes, allow_implicit))
- return LZMA_HEADER_ERROR;
-
- // Check if implicit Subblock filter was added.
- if (ids[count] != LZMA_VLI_VALUE_UNKNOWN) {
- assert(ids[count] == LZMA_FILTER_SUBBLOCK);
- ++count;
- using_implicit = true;
- }
- }
-
- // Set the filter functions, and copy uncompressed sizes and options.
+ // Set the filter functions and copy the options pointer.
lzma_filter_info filters[count + 1];
if (is_encoder) {
for (size_t i = 0; i < count; ++i) {
@@ -144,29 +103,20 @@ lzma_raw_coder_init(lzma_next_coder *next, lzma_allocator *allocator,
// of the uncompressed data.
const size_t j = count - i - 1;
- filters[j].init = get_function(ids[i]);
+ filters[j].init = get_function(options[i].id);
if (filters[j].init == NULL)
return LZMA_HEADER_ERROR;
filters[j].options = options[i].options;
- filters[j].uncompressed_size = uncompressed_sizes[i];
}
-
- if (using_implicit)
- filters[0].options = NULL;
-
} else {
for (size_t i = 0; i < count; ++i) {
- filters[i].init = get_function(ids[i]);
+ filters[i].init = get_function(options[i].id);
if (filters[i].init == NULL)
return LZMA_HEADER_ERROR;
filters[i].options = options[i].options;
- filters[i].uncompressed_size = uncompressed_sizes[i];
}
-
- if (using_implicit)
- filters[count - 1].options = NULL;
}
// Terminate the array.