/////////////////////////////////////////////////////////////////////////////// // /// \file filter_flags_encoder.c /// \brief Decodes a Filter Flags field // // 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 "common.h" #include "lzma_encoder.h" /// \brief Calculates the size of the Filter Properties field /// /// This currently can return only LZMA_OK or LZMA_HEADER_ERROR, but /// with some new filters it may return also LZMA_PROG_ERROR. static lzma_ret get_properties_size(uint32_t *size, const lzma_options_filter *options) { lzma_ret ret = LZMA_OK; switch (options->id) { #ifdef HAVE_FILTER_COPY case LZMA_FILTER_COPY: *size = 0; break; #endif #ifdef HAVE_FILTER_SUBBLOCK case LZMA_FILTER_SUBBLOCK: *size = 0; break; #endif #ifdef HAVE_FILTER_SIMPLE # ifdef HAVE_FILTER_X86 case LZMA_FILTER_X86: # endif # ifdef HAVE_FILTER_POWERPC case LZMA_FILTER_POWERPC: # endif # ifdef HAVE_FILTER_IA64 case LZMA_FILTER_IA64: # endif # ifdef HAVE_FILTER_ARM case LZMA_FILTER_ARM: # endif # ifdef HAVE_FILTER_ARMTHUMB case LZMA_FILTER_ARMTHUMB: # endif # ifdef HAVE_FILTER_SPARC case LZMA_FILTER_SPARC: # endif if (options->options == NULL || ((const lzma_options_simple *)( options->options))->start_offset == 0) *size = 0; else *size = 4; break; #endif #ifdef HAVE_FILTER_DELTA case LZMA_FILTER_DELTA: *size = 1; break; #endif #ifdef HAVE_FILTER_LZMA case LZMA_FILTER_LZMA: *size = 2; break; #endif default: // Unknown filter - if the Filter ID is a proper VLI, // return LZMA_HEADER_ERROR instead of LZMA_PROG_ERROR, // because it's possible that we just don't have support // compiled in for the requested filter. ret = options->id <= LZMA_VLI_VALUE_MAX ? LZMA_HEADER_ERROR : LZMA_PROG_ERROR; break; } return ret; } extern LZMA_API lzma_ret lzma_filter_flags_size(uint32_t *size, const lzma_options_filter *options) { // Get size of Filter Properties. uint32_t prop_size; const lzma_ret ret = get_properties_size(&prop_size, options); if (ret != LZMA_OK) return ret; // Size of Filter ID field if it exists. size_t id_size; size_t prop_size_size; if (options->id < 0xE0 && (lzma_vli)(prop_size) == options->id / 0x20) { // ID and Size of Filter Properties fit into Misc. id_size = 0; prop_size_size = 0; } else { // At least Filter ID is stored using the External ID field. id_size = lzma_vli_size(options->id); if (id_size == 0) return LZMA_PROG_ERROR; if (prop_size <= 30) { // Size of Filter Properties fits into Misc still. prop_size_size = 0; } else { // The Size of Filter Properties field is used too. prop_size_size = lzma_vli_size(prop_size); if (prop_size_size == 0) return LZMA_PROG_ERROR; } } // 1 is for the Misc field. *size = 1 + id_size + prop_size_size + prop_size; return LZMA_OK; } #ifdef HAVE_FILTER_SIMPLE /// Encodes Filter Properties of the so called simple filters static lzma_ret properties_simple(uint8_t *out, size_t *out_pos, size_t out_size, const lzma_options_simple *options) { if (options == NULL || options->start_offset == 0) return LZMA_OK; if (out_size - *out_pos < 4) return LZMA_BUF_ERROR; for (size_t i = 0; i < 4; ++i) out[(*out_pos)++] = options->start_offset >> (i * 8); return LZMA_OK; } #endif #ifdef HAVE_FILTER_DELTA /// Encodes Filter Properties of the Delta filter static lzma_ret properties_delta(uint8_t *out, size_t *out_pos, size_t out_size, const lzma_options_delta *options) { if (options == NULL) return LZMA_PROG_ERROR; // It's possible that newer liblzma versions will support larger // distance values. if (options->distance < LZMA_DELTA_DISTANCE_MIN || options->distance > LZMA_DELTA_DISTANCE_MAX) return LZMA_HEADER_ERROR; if (out_size - *out_pos < 1) return LZMA_BUF_ERROR; out[*out_pos] = options->distance - LZMA_DELTA_DISTANCE_MIN; ++*out_pos; return LZMA_OK; } #endif #ifdef HAVE_FILTER_LZMA /// Encodes LZMA Properties and Dictionary Flags (two bytes) static lzma_ret properties_lzma(uint8_t *out, size_t *out_pos, size_t out_size, const lzma_options_lzma *options) { if (options == NULL) return LZMA_PROG_ERROR; if (out_size - *out_pos < 2) return LZMA_BUF_ERROR; // LZMA Properties if (lzma_lzma_encode_properties(options, out + *out_pos)) return LZMA_HEADER_ERROR; ++*out_pos; // Dictionary flags // // Dictionary size is encoded using six bits of // which one is mantissa and five are exponent. // // There are some limits that must hold to keep // this coding working. # if LZMA_DICTIONARY_SIZE_MAX > UINT32_MAX / 2 # error LZMA_DICTIONARY_SIZE_MAX is too big. # endif # if LZMA_DICTIONARY_SIZE_MIN < 1 # error LZMA_DICTIONARY_SIZE_MIN cannot be zero. # endif // Validate it: if (options->dictionary_size < LZMA_DICTIONARY_SIZE_MIN || options->dictionary_size > LZMA_DICTIONARY_SIZE_MAX) return LZMA_HEADER_ERROR; if (options->dictionary_size == 1) { // Special case out[*out_pos] = 0x00; } else { // TODO This could be more elegant. uint32_t i = 1; while (((2 | ((i + 1) & 1)) << ((i - 1) / 2)) < options->dictionary_size) ++i; out[*out_pos] = i; } ++*out_pos; return LZMA_OK; } #endif extern LZMA_API lzma_ret lzma_filter_flags_encode(uint8_t *out, size_t *out_pos, size_t out_size, const lzma_options_filter *options) { // Minimum output is one byte (everything fits into Misc). // The caller should have checked that there is enough output space, // so we return LZMA_PROG_ERROR instead of LZMA_BUF_ERROR. if (*out_pos >= out_size) return LZMA_PROG_ERROR; // Get size of Filter Properties. uint32_t prop_size; lzma_ret ret = get_properties_size(&prop_size, options); if (ret != LZMA_OK) return ret; // Misc, External ID, and Size of Properties if (options->id < 0xE0 && (lzma_vli)(prop_size) == options->id / 0x20) { // ID and Size of Filter Properties fit into Misc. out[*out_pos] = options->id; ++*out_pos; } else if (prop_size <= 30) { // Size of Filter Properties fits into Misc. out[*out_pos] = prop_size + 0xE0; ++*out_pos; // External ID is used to encode the Filter ID. If encoding // the VLI fails, it's because the caller has given as too // little output space, which it should have checked already. // So return LZMA_PROG_ERROR, not LZMA_BUF_ERROR. size_t dummy = 0; if (lzma_vli_encode(options->id, &dummy, 1, out, out_pos, out_size) != LZMA_STREAM_END) return LZMA_PROG_ERROR; } else { // Nothing fits into Misc. out[*out_pos] = 0xFF; ++*out_pos; // External ID is used to encode the Filter ID. size_t dummy = 0; if (lzma_vli_encode(options->id, &dummy, 1, out, out_pos, out_size) != LZMA_STREAM_END) return LZMA_PROG_ERROR; // External Size of Filter Properties dummy = 0; if (lzma_vli_encode(prop_size, &dummy, 1, out, out_pos, out_size) != LZMA_STREAM_END) return LZMA_PROG_ERROR; } // Filter Properties switch (options->id) { #ifdef HAVE_FILTER_COPY case LZMA_FILTER_COPY: assert(prop_size == 0); ret = options->options == NULL ? LZMA_OK : LZMA_HEADER_ERROR; break; #endif #ifdef HAVE_FILTER_SUBBLOCK case LZMA_FILTER_SUBBLOCK: assert(prop_size == 0); ret = LZMA_OK; break; #endif #ifdef HAVE_FILTER_SIMPLE # ifdef HAVE_FILTER_X86 case LZMA_FILTER_X86: # endif # ifdef HAVE_FILTER_POWERPC case LZMA_FILTER_POWERPC: # endif # ifdef HAVE_FILTER_IA64 case LZMA_FILTER_IA64: # endif # ifdef HAVE_FILTER_ARM case LZMA_FILTER_ARM: # endif # ifdef HAVE_FILTER_ARMTHUMB case LZMA_FILTER_ARMTHUMB: # endif # ifdef HAVE_FILTER_SPARC case LZMA_FILTER_SPARC: # endif ret = properties_simple(out, out_pos, out_size, options->options); break; #endif #ifdef HAVE_FILTER_DELTA case LZMA_FILTER_DELTA: ret = properties_delta(out, out_pos, out_size, options->options); break; #endif #ifdef HAVE_FILTER_LZMA case LZMA_FILTER_LZMA: ret = properties_lzma(out, out_pos, out_size, options->options); break; #endif default: assert(0); ret = LZMA_PROG_ERROR; break; } return ret; }