mirror of https://git.tukaani.org/xz.git
212 lines
6.0 KiB
C
212 lines
6.0 KiB
C
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
/// \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;
|
||
|
}
|