aboutsummaryrefslogblamecommitdiff
path: root/src/liblzma/common/stream_encoder_multi.c
blob: 33b4efd97716f4317dbe7ec370016f08a92087ef (plain) (tree)



















                                                                               
                                 




























































                                                                         
                                                                        



























                                                                            
                                                                       





                                                                  

                                                               









                                                                   

                                                                         




                                                                    

                                                                








                                                                  
                                                                      
 

                                                             






































































                                                                              




                                                                               

                  

                                                                       












                                                                           
                                                                               

                  

                                                                       







                                                           
                                                                        




                                                                  
                                                               


                                                                           
                                                                  




















                                                                           
                                                                     












                                                       
                                                                        




                                                                         
                                                                
                                                                
                                                                         

















                                                                             

                                                        






















































































                                                                              





                                                                              














                                                                      
///////////////////////////////////////////////////////////////////////////////
//
/// \file       stream_encoder_multi.c
/// \brief      Encodes Multi-Block .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 "stream_common.h"
#include "stream_encoder_multi.h"
#include "block_encoder.h"
#include "metadata_encoder.h"


struct lzma_coder_s {
	enum {
		SEQ_STREAM_HEADER_COPY,
		SEQ_HEADER_METADATA_INIT,
		SEQ_HEADER_METADATA_COPY,
		SEQ_HEADER_METADATA_CODE,
		SEQ_DATA_INIT,
		SEQ_DATA_COPY,
		SEQ_DATA_CODE,
		SEQ_FOOTER_METADATA_INIT,
		SEQ_FOOTER_METADATA_COPY,
		SEQ_FOOTER_METADATA_CODE,
		SEQ_STREAM_FOOTER_INIT,
		SEQ_STREAM_FOOTER_COPY,
	} sequence;

	/// Block or Metadata encoder
	lzma_next_coder next;

	/// Options for the Block encoder
	lzma_options_block block_options;

	/// Information about the Stream
	lzma_info *info;

	/// Information about the current Data Block
	lzma_info_iter iter;

	/// Pointer to user-supplied options structure. We don't write to
	/// it, only read instructions from the application, thus this is
	/// const even though the user-supplied pointer from
	/// lzma_options_filter structure isn't.
	const lzma_options_stream *stream_options;

	/// Stream Header or Stream Footer in encoded form
	uint8_t *header;
	size_t header_pos;
	size_t header_size;
};


typedef enum {
	BLOCK_HEADER_METADATA,
	BLOCK_DATA,
	BLOCK_FOOTER_METADATA,
} block_type;


static lzma_ret
block_header_encode(lzma_coder *coder, lzma_allocator *allocator,
		lzma_vli uncompressed_size, block_type type)
{
	assert(coder->header == NULL);

	coder->block_options = (lzma_options_block){
		.check = coder->stream_options->check,
		.has_crc32 = coder->stream_options->has_crc32,
		.has_eopm = uncompressed_size == LZMA_VLI_VALUE_UNKNOWN,
		.is_metadata = type != BLOCK_DATA,
		.has_uncompressed_size_in_footer = false,
		.has_backward_size = type == BLOCK_FOOTER_METADATA,
		.handle_padding = false,
		.total_size = LZMA_VLI_VALUE_UNKNOWN,
		.compressed_size = LZMA_VLI_VALUE_UNKNOWN,
		.uncompressed_size = uncompressed_size,
		.compressed_reserve = 0,
		.uncompressed_reserve = 0,
		.total_limit = LZMA_VLI_VALUE_UNKNOWN,
		.uncompressed_limit = LZMA_VLI_VALUE_UNKNOWN,
		.padding = LZMA_BLOCK_HEADER_PADDING_AUTO,
	};

	if (type == BLOCK_DATA) {
		memcpy(coder->block_options.filters,
				coder->stream_options->filters,
				sizeof(coder->stream_options->filters));
		coder->block_options.alignment = coder->iter.stream_offset;
	} else {
		memcpy(coder->block_options.filters,
				coder->stream_options->metadata_filters,
				sizeof(coder->stream_options->filters));
		coder->block_options.alignment
				= lzma_info_metadata_alignment_get(
				coder->info, type == BLOCK_HEADER_METADATA);
	}

	return_if_error(lzma_block_header_size(&coder->block_options));

	coder->header_size = coder->block_options.header_size;
	coder->header = lzma_alloc(coder->header_size, allocator);
	if (coder->header == NULL)
		return LZMA_MEM_ERROR;

	return_if_error(lzma_block_header_encode(
			coder->header, &coder->block_options));

	coder->header_pos = 0;
	return LZMA_OK;
}


static lzma_ret
metadata_encoder_init(lzma_coder *coder, lzma_allocator *allocator,
		lzma_metadata *metadata, block_type type)
{
	return_if_error(lzma_info_metadata_set(coder->info, allocator,
			metadata, type == BLOCK_HEADER_METADATA, false));

	const lzma_vli metadata_size = lzma_metadata_size(metadata);
	if (metadata_size == 0)
		return LZMA_PROG_ERROR;

	return_if_error(block_header_encode(
			coder, allocator, metadata_size, type));

	return lzma_metadata_encoder_init(&coder->next, allocator,
			&coder->block_options, metadata);
}


static lzma_ret
data_encoder_init(lzma_coder *coder, lzma_allocator *allocator)
{
	return_if_error(lzma_info_iter_next(&coder->iter, allocator));

	return_if_error(block_header_encode(coder, allocator,
			LZMA_VLI_VALUE_UNKNOWN, BLOCK_DATA));

	return lzma_block_encoder_init(&coder->next, allocator,
			&coder->block_options);
}


static lzma_ret
stream_encode(lzma_coder *coder, lzma_allocator *allocator,
		const uint8_t *restrict in, size_t *restrict in_pos,
		size_t in_size, uint8_t *restrict out,
		size_t *restrict out_pos, size_t out_size, lzma_action action)
{
	// Main loop
	while (*out_pos < out_size)
	switch (coder->sequence) {
	case SEQ_STREAM_HEADER_COPY:
	case SEQ_HEADER_METADATA_COPY:
	case SEQ_DATA_COPY:
	case SEQ_FOOTER_METADATA_COPY:
	case SEQ_STREAM_FOOTER_COPY:
		bufcpy(coder->header, &coder->header_pos, coder->header_size,
				out, out_pos, out_size);
		if (coder->header_pos < coder->header_size)
			return LZMA_OK;

		lzma_free(coder->header, allocator);
		coder->header = NULL;

		switch (coder->sequence) {
		case SEQ_STREAM_HEADER_COPY:
			// Write Header Metadata Block if we have Extra for it
			// or known Uncompressed Size.
			if (coder->stream_options->header != NULL
					|| coder->stream_options
						->uncompressed_size
						!= LZMA_VLI_VALUE_UNKNOWN) {
				coder->sequence = SEQ_HEADER_METADATA_INIT;
			} else {
				// Mark that Header Metadata Block doesn't
				// exist.
				if (lzma_info_size_set(coder->info,
						LZMA_INFO_HEADER_METADATA, 0)
						!= LZMA_OK)
					return LZMA_PROG_ERROR;

				coder->sequence = SEQ_DATA_INIT;
			}
			break;

		case SEQ_HEADER_METADATA_COPY:
		case SEQ_DATA_COPY:
		case SEQ_FOOTER_METADATA_COPY:
			++coder->sequence;
			break;

		case SEQ_STREAM_FOOTER_COPY:
			return LZMA_STREAM_END;

		default:
			assert(0);
		}

		break;

	case SEQ_HEADER_METADATA_INIT: {
		lzma_metadata metadata = {
			.header_metadata_size = LZMA_VLI_VALUE_UNKNOWN,
			.total_size = LZMA_VLI_VALUE_UNKNOWN,
			.uncompressed_size = coder->stream_options
					->uncompressed_size,
			.index = NULL,
			// Metadata encoder doesn't modify this, but since
			// the lzma_extra structure is used also when decoding
			// Metadata, the pointer is not const, and we need
			// to cast the constness away in the encoder.
			.extra = (lzma_extra *)(coder->stream_options->header),
		};

		return_if_error(metadata_encoder_init(coder, allocator,
				&metadata, BLOCK_HEADER_METADATA));

		coder->sequence = SEQ_HEADER_METADATA_COPY;
		break;
	}

	case SEQ_FOOTER_METADATA_INIT: {
		lzma_metadata metadata = {
			.header_metadata_size
					= lzma_info_size_get(coder->info,
						LZMA_INFO_HEADER_METADATA),
			.total_size = LZMA_VLI_VALUE_UNKNOWN,
			.uncompressed_size = LZMA_VLI_VALUE_UNKNOWN,
			.index = lzma_info_index_get(coder->info, false),
			.extra = (lzma_extra *)(coder->stream_options->footer),
		};

		return_if_error(metadata_encoder_init(coder, allocator,
				&metadata, BLOCK_FOOTER_METADATA));

		coder->sequence = SEQ_FOOTER_METADATA_COPY;
		break;
	}

	case SEQ_HEADER_METADATA_CODE:
	case SEQ_FOOTER_METADATA_CODE: {
		size_t dummy = 0;
		const lzma_ret ret = coder->next.code(coder->next.coder,
				allocator, NULL, &dummy, 0,
				out, out_pos, out_size, LZMA_RUN);
		if (ret != LZMA_STREAM_END)
			return ret;

		return_if_error(lzma_info_size_set(coder->info,
				coder->sequence == SEQ_HEADER_METADATA_CODE
					? LZMA_INFO_HEADER_METADATA
					: LZMA_INFO_FOOTER_METADATA,
				coder->block_options.total_size));

		++coder->sequence;
		break;
	}

	case SEQ_DATA_INIT: {
		// Don't create an empty Block unless it would be
		// the only Data Block.
		if (*in_pos == in_size) {
			if (action != LZMA_FINISH)
				return LZMA_OK;

			if (lzma_info_index_count_get(coder->info) != 0) {
				if (lzma_info_index_finish(coder->info))
					return LZMA_DATA_ERROR;

				coder->sequence = SEQ_FOOTER_METADATA_INIT;
				break;
			}
		}

		return_if_error(data_encoder_init(coder, allocator));

		coder->sequence = SEQ_DATA_COPY;
		break;
	}

	case SEQ_DATA_CODE: {
		static const lzma_action convert[4] = {
			LZMA_RUN,
			LZMA_SYNC_FLUSH,
			LZMA_FINISH,
			LZMA_FINISH,
		};

		const lzma_ret ret = coder->next.code(coder->next.coder,
				allocator, in, in_pos, in_size,
				out, out_pos, out_size, convert[action]);
		if (ret != LZMA_STREAM_END || action == LZMA_SYNC_FLUSH)
			return ret;

		return_if_error(lzma_info_iter_set(&coder->iter,
				coder->block_options.total_size,
				coder->block_options.uncompressed_size));

		coder->sequence = SEQ_DATA_INIT;
		break;
	}

	case SEQ_STREAM_FOOTER_INIT: {
		assert(coder->header == NULL);

		lzma_stream_flags flags = {
			.check = coder->stream_options->check,
			.has_crc32 = coder->stream_options->has_crc32,
			.is_multi = true,
		};

		coder->header = lzma_alloc(LZMA_STREAM_TAIL_SIZE, allocator);
		if (coder->header == NULL)
			return LZMA_MEM_ERROR;

		return_if_error(lzma_stream_tail_encode(
				coder->header, &flags));

		coder->header_size = LZMA_STREAM_TAIL_SIZE;
		coder->header_pos = 0;

		coder->sequence = SEQ_STREAM_FOOTER_COPY;
		break;
	}

	default:
		return LZMA_PROG_ERROR;
	}

	return LZMA_OK;
}


static void
stream_encoder_end(lzma_coder *coder, lzma_allocator *allocator)
{
	lzma_next_coder_end(&coder->next, allocator);
	lzma_info_free(coder->info, allocator);
	lzma_free(coder->header, allocator);
	lzma_free(coder, allocator);
	return;
}


static lzma_ret
stream_encoder_init(lzma_next_coder *next,
		lzma_allocator *allocator, const lzma_options_stream *options)
{
	if (options == NULL)
		return LZMA_PROG_ERROR;

	if (next->coder == NULL) {
		next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
		if (next->coder == NULL)
			return LZMA_MEM_ERROR;

		next->code = &stream_encode;
		next->end = &stream_encoder_end;

		next->coder->next = LZMA_NEXT_CODER_INIT;
		next->coder->info = NULL;
	} else {
		lzma_free(next->coder->header, allocator);
	}

	next->coder->header = NULL;

	next->coder->info = lzma_info_init(next->coder->info, allocator);
	if (next->coder->info == NULL)
		return LZMA_MEM_ERROR;

	next->coder->sequence = SEQ_STREAM_HEADER_COPY;
	next->coder->stream_options = options;

	// Encode Stream Flags
	{
		lzma_stream_flags flags = {
			.check = options->check,
			.has_crc32 = options->has_crc32,
			.is_multi = true,
		};

		next->coder->header = lzma_alloc(LZMA_STREAM_HEADER_SIZE,
				allocator);
		if (next->coder->header == NULL)
			return LZMA_MEM_ERROR;

		return_if_error(lzma_stream_header_encode(
				next->coder->header, &flags));

		next->coder->header_pos = 0;
		next->coder->header_size = LZMA_STREAM_HEADER_SIZE;
	}

	if (lzma_info_size_set(next->coder->info, LZMA_INFO_STREAM_START,
			options->alignment) != LZMA_OK)
		return LZMA_PROG_ERROR;

	lzma_info_iter_begin(next->coder->info, &next->coder->iter);

	return LZMA_OK;
}


extern lzma_ret
lzma_stream_encoder_multi_init(lzma_next_coder *next,
		lzma_allocator *allocator, const lzma_options_stream *options)
{
	lzma_next_coder_init(stream_encoder_init, next, allocator, options);
}


extern LZMA_API lzma_ret
lzma_stream_encoder_multi(
		lzma_stream *strm, const lzma_options_stream *options)
{
	lzma_next_strm_init(strm, stream_encoder_init, options);

	strm->internal->supported_actions[LZMA_RUN] = true;
	strm->internal->supported_actions[LZMA_SYNC_FLUSH] = true;
	strm->internal->supported_actions[LZMA_FULL_FLUSH] = true;
	strm->internal->supported_actions[LZMA_FINISH] = true;

	return LZMA_OK;
}