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