| /////////////////////////////////////////////////////////////////////////////// |
| // |
| /// \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; |
| } |