aboutsummaryrefslogblamecommitdiff
path: root/src/liblzma/common/block_header_encoder.c
blob: 3a16e6c374330d58f39581d18294fb460725312a (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_block *options)
{
	// Block Header Size + Block Flags + CRC32.
	size_t size = 1 + 1 + 4;

	// Compressed Size
	if (options->compressed_size != LZMA_VLI_VALUE_UNKNOWN) {
		if (options->compressed_size > LZMA_VLI_VALUE_MAX / 4 - 1
				|| options->compressed_size == 0
				|| (options->compressed_size & 3))
			return LZMA_PROG_ERROR;

		size += lzma_vli_size(options->compressed_size / 4 - 1);
	}

	// Uncompressed Size
	if (options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN) {
		const size_t add = lzma_vli_size(options->uncompressed_size);
		if (add == 0)
			return LZMA_PROG_ERROR;

		size += add;
	}

	// List of Filter Flags
	if (options->filters == NULL
			|| options->filters[0].id == LZMA_VLI_VALUE_UNKNOWN)
		return LZMA_PROG_ERROR;

	for (size_t i = 0; options->filters[i].id != LZMA_VLI_VALUE_UNKNOWN;
			++i) {
		// Don't allow too many filters.
		if (i == 4)
			return LZMA_PROG_ERROR;

		uint32_t add;
		return_if_error(lzma_filter_flags_size(&add,
				options->filters + i));

		size += add;
	}

	// Pad to a multiple of four bytes.
	options->header_size = (size + 3) & ~(size_t)(3);

	// NOTE: We don't verify that Total Size of the Block stays within
	// limits. This is because it is possible that we are called with
	// exaggerated values to reserve space for Block Header, and later
	// called again with lower, real values.

	return LZMA_OK;
}


extern LZMA_API lzma_ret
lzma_block_header_encode(const lzma_block *options, uint8_t *out)
{
	if ((options->header_size & 3)
			|| options->header_size < LZMA_BLOCK_HEADER_SIZE_MIN
			|| options->header_size > LZMA_BLOCK_HEADER_SIZE_MAX)
		return LZMA_PROG_ERROR;

	// Indicate the size of the buffer _excluding_ the CRC32 field.
	const size_t out_size = options->header_size - 4;

	// Store the Block Header Size.
	out[0] = out_size / 4;

	// We write Block Flags a little later.
	size_t out_pos = 2;

	// Compressed Size
	if (options->compressed_size != LZMA_VLI_VALUE_UNKNOWN) {
		// Compressed Size must be non-zero, fit into a 63-bit
		// integer and be a multiple of four. Also the Total Size
		// of the Block must fit into 63-bit integer.
		if (options->compressed_size == 0
				|| (options->compressed_size & 3)
				|| options->compressed_size
					> LZMA_VLI_VALUE_MAX
				|| lzma_block_total_size_get(options) == 0)
			return LZMA_PROG_ERROR;

		return_if_error(lzma_vli_encode(
				options->compressed_size / 4 - 1, NULL,
				out, &out_pos, out_size));
	}

	// Uncompressed Size
	if (options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN)
		return_if_error(lzma_vli_encode(
				options->uncompressed_size, NULL,
				out, &out_pos, out_size));

	// Filter Flags
	if (options->filters == NULL
			|| options->filters[0].id == LZMA_VLI_VALUE_UNKNOWN)
		return LZMA_PROG_ERROR;

	size_t filter_count = 0;
	do {
		// There can be at maximum of four filters.
		if (filter_count == 4)
			return LZMA_PROG_ERROR;

		return_if_error(lzma_filter_flags_encode(
				options->filters + filter_count,
				out, &out_pos, out_size));

	} while (options->filters[++filter_count].id
			!= LZMA_VLI_VALUE_UNKNOWN);

	// Block Flags
	out[1] = filter_count - 1;

	if (options->compressed_size != LZMA_VLI_VALUE_UNKNOWN)
		out[1] |= 0x40;

	if (options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN)
		out[1] |= 0x80;

	// Padding
	memzero(out + out_pos, out_size - out_pos);

	// CRC32
	integer_write_32(out + out_size, lzma_crc32(out, out_size, 0));

	return LZMA_OK;
}