aboutsummaryrefslogtreecommitdiff
path: root/src/liblzma/common/block_header_encoder.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/liblzma/common/block_header_encoder.c')
-rw-r--r--src/liblzma/common/block_header_encoder.c211
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;
+}