diff options
Diffstat (limited to 'src/liblzma/common/block_header_encoder.c')
-rw-r--r-- | src/liblzma/common/block_header_encoder.c | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/src/liblzma/common/block_header_encoder.c b/src/liblzma/common/block_header_encoder.c new file mode 100644 index 00000000..594b4fc0 --- /dev/null +++ b/src/liblzma/common/block_header_encoder.c @@ -0,0 +1,211 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file block_header_encoder.c +/// \brief Encodes Block Header for .lzma files +// +// 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 "check.h" + + +extern LZMA_API lzma_ret +lzma_block_header_size(lzma_options_block *options) +{ + // Block Flags take two bytes. + size_t size = 2; + + // Compressed Size + if (!lzma_vli_is_valid(options->compressed_size)) { + return LZMA_PROG_ERROR; + + } else if (options->compressed_reserve != 0) { + // Make sure that the known Compressed Size fits into the + // reserved space. Note that lzma_vli_size() will return zero + // if options->compressed_size is LZMA_VLI_VALUE_UNKNOWN, so + // we don't need to handle that special case separately. + if (options->compressed_reserve > LZMA_VLI_BYTES_MAX + || lzma_vli_size(options->compressed_size) + > (size_t)(options->compressed_reserve)) + return LZMA_PROG_ERROR; + + size += options->compressed_reserve; + + } else if (options->compressed_size != LZMA_VLI_VALUE_UNKNOWN) { + // Compressed Size is known. We have already checked + // that is is a valid VLI, and since it isn't + // LZMA_VLI_VALUE_UNKNOWN, we can be sure that + // lzma_vli_size() will succeed. + size += lzma_vli_size(options->compressed_size); + } + + // Uncompressed Size + if (!lzma_vli_is_valid(options->uncompressed_size)) { + return LZMA_PROG_ERROR; + + } else if (options->uncompressed_reserve != 0) { + if (options->uncompressed_reserve > LZMA_VLI_BYTES_MAX + || lzma_vli_size(options->uncompressed_size) + > (size_t)(options->uncompressed_reserve)) + return LZMA_PROG_ERROR; + + size += options->uncompressed_reserve; + + } else if (options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN) { + size += lzma_vli_size(options->uncompressed_size); + } + + // List of Filter Flags + for (size_t i = 0; options->filters[i].id != LZMA_VLI_VALUE_UNKNOWN; + ++i) { + // Don't allow too many filters. + if (i == 7) + return LZMA_PROG_ERROR; + + uint32_t tmp; + const lzma_ret ret = lzma_filter_flags_size(&tmp, + options->filters + i); + if (ret != LZMA_OK) + return ret; + + size += tmp; + } + + // CRC32 + if (options->has_crc32) + size += 4; + + // Padding + int32_t padding; + if (options->padding == LZMA_BLOCK_HEADER_PADDING_AUTO) { + const uint32_t preferred = lzma_alignment_output( + options->filters, 1); + const uint32_t unaligned = size + options->alignment; + padding = (int32_t)(unaligned % preferred); + if (padding != 0) + padding = preferred - padding; + } else if (options->padding >= LZMA_BLOCK_HEADER_PADDING_MIN + && options->padding <= LZMA_BLOCK_HEADER_PADDING_MAX) { + padding = options->padding; + } else { + return LZMA_PROG_ERROR; + } + + // All success. Copy the calculated values to the options structure. + options->padding = padding; + options->header_size = size + (size_t)(padding); + + return LZMA_OK; +} + + +extern LZMA_API lzma_ret +lzma_block_header_encode(uint8_t *out, const lzma_options_block *options) +{ + // We write the Block Flags later. + if (options->header_size < 2) + return LZMA_PROG_ERROR; + + const size_t out_size = options->header_size; + size_t out_pos = 2; + + // Compressed Size + if (options->compressed_size != LZMA_VLI_VALUE_UNKNOWN + || options->compressed_reserve != 0) { + const lzma_vli size = options->compressed_size + != LZMA_VLI_VALUE_UNKNOWN + ? options->compressed_size : 0; + size_t vli_pos = 0; + if (lzma_vli_encode( + size, &vli_pos, options->compressed_reserve, + out, &out_pos, out_size) != LZMA_STREAM_END) + return LZMA_PROG_ERROR; + + } + + // Uncompressed Size + if (options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN + || options->uncompressed_reserve != 0) { + const lzma_vli size = options->uncompressed_size + != LZMA_VLI_VALUE_UNKNOWN + ? options->uncompressed_size : 0; + size_t vli_pos = 0; + if (lzma_vli_encode( + size, &vli_pos, options->uncompressed_reserve, + out, &out_pos, out_size) != LZMA_STREAM_END) + return LZMA_PROG_ERROR; + + } + + // Filter Flags + size_t filter_count; + for (filter_count = 0; options->filters[filter_count].id + != LZMA_VLI_VALUE_UNKNOWN; ++filter_count) { + // There can be at maximum of seven filters. + if (filter_count == 7) + return LZMA_PROG_ERROR; + + const lzma_ret ret = lzma_filter_flags_encode(out, &out_pos, + out_size, options->filters + filter_count); + // FIXME: Don't return LZMA_BUF_ERROR. + if (ret != LZMA_OK) + return ret; + } + + // Block Flags 1 + out[0] = filter_count; + + if (options->has_eopm) + out[0] |= 0x08; + else if (options->uncompressed_size == LZMA_VLI_VALUE_UNKNOWN) + return LZMA_PROG_ERROR; + + if (options->compressed_size != LZMA_VLI_VALUE_UNKNOWN + || options->compressed_reserve != 0) + out[0] |= 0x10; + + if (options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN + || options->uncompressed_reserve != 0) + out[0] |= 0x20; + + if (options->is_metadata) + out[0] |= 0x80; + + // Block Flags 2 + if (options->padding < LZMA_BLOCK_HEADER_PADDING_MIN + || options->padding > LZMA_BLOCK_HEADER_PADDING_MAX) + return LZMA_PROG_ERROR; + + out[1] = (uint8_t)(options->padding); + + // CRC32 + if (options->has_crc32) { + if (out_size - out_pos < 4) + return LZMA_PROG_ERROR; + + const uint32_t crc = lzma_crc32(out, out_pos, 0); + for (size_t i = 0; i < 4; ++i) + out[out_pos++] = crc >> (i * 8); + } + + // Padding - the amount of available space must now match with + // the size of the Padding field. + if (out_size - out_pos != (size_t)(options->padding)) + return LZMA_PROG_ERROR; + + memzero(out + out_pos, (size_t)(options->padding)); + + return LZMA_OK; +} |