aboutsummaryrefslogblamecommitdiff
path: root/src/liblzma/common/block_header_encoder.c
blob: 594b4fc041f3fbf4b50c9cdfe728ec537c50f1d4 (plain) (tree)


















































































































































































































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