/////////////////////////////////////////////////////////////////////////////// // /// \file raw_common.c /// \brief Stuff shared between raw encoder and raw decoder // // Copyright (C) 2007 Lasse Collin // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // /////////////////////////////////////////////////////////////////////////////// #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 implicit) { 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; } // Is this the last filter in the chain? if (id[1] == LZMA_VLI_VALUE_UNKNOWN) { if (!needs_end_of_input || !implicit || uncompressed_size[0] != LZMA_VLI_VALUE_UNKNOWN) return false; // Add implicit Subblock filter. id[1] = LZMA_FILTER_SUBBLOCK; uncompressed_size[1] = LZMA_VLI_VALUE_UNKNOWN; id[2] = LZMA_VLI_VALUE_UNKNOWN; } return prepare(id + 1, uncompressed_size + 1, implicit); } extern lzma_ret lzma_raw_coder_init(lzma_next_coder *next, lzma_allocator *allocator, const lzma_options_filter *options, lzma_vli uncompressed_size, lzma_init_function (*get_function)(lzma_vli id), bool allow_implicit, 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; 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. lzma_filter_info filters[count + 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; filters[j].init = get_function(ids[i]); 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]); 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. filters[count].init = NULL; // Initialize the filters. return lzma_next_filter_init(next, allocator, filters); }