aboutsummaryrefslogblamecommitdiff
path: root/src/liblzma/common/stream_decoder.c
blob: 1bf7f1f83fda9dc7f368ca510438c67aa15ac83f (plain) (tree)



















                                                                               
                           


                                 



                     




                                  

                   




                                                                            

                                                                         

                                         
                                           
                                       
 


                                                                             
 

                                      
 


                                                                          



               




                                                                              

                                                                         
                                                        
                                                         
                                  













                                                                              
 






                                                                            



                                                                             
                                                                       




                                                      






                                                            
 





                                                                            

                 


                                                                              
 


                                                                         
 
                                      
 




                                                                         
 


                                                                               
 



                                                                  
 



                                                                          
 
                                                    
 



                                                                          

                                   
                                            


                      
                         







                                                                            





                                                                         
 
                                                   


                      





                                                                              

                                   

                                                    

         



                                                                              
 


                                                                       
 














                                                                            


                                               

                
                          










                                                                
                                                          





                                    
                                                                     









                                                                        
                                               

         



                                                              

                                      



                                                  





                       
                                                                          
 
                                                                    



                        
                                      
 
                                                        





                                                                  
///////////////////////////////////////////////////////////////////////////////
//
/// \file       stream_decoder.c
/// \brief      Decodes .lzma Streams
//
//  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_decoder.h"
#include "check.h"
#include "stream_flags_decoder.h"
#include "block_decoder.h"


struct lzma_coder_s {
	enum {
		SEQ_STREAM_HEADER,
		SEQ_BLOCK_HEADER,
		SEQ_BLOCK,
		SEQ_INDEX,
		SEQ_STREAM_FOOTER,
	} sequence;

	/// Block or Metadata decoder. This takes little memory and the same
	/// data structure can be used to decode every Block Header, so it's
	/// a good idea to have a separate lzma_next_coder structure for it.
	lzma_next_coder block_decoder;

	/// Block options decoded by the Block Header decoder and used by
	/// the Block decoder.
	lzma_options_block block_options;

	/// Stream Flags from Stream Header
	lzma_stream_flags stream_flags;

	/// Index is hashed so that it can be compared to the sizes of Blocks
	/// with O(1) memory usage.
	lzma_index_hash *index_hash;

	/// Write position in buffer[]
	size_t buffer_pos;

	/// Buffer to hold Stream Header, Block Header, and Stream Footer.
	/// Block Header has biggest maximum size.
	uint8_t buffer[LZMA_BLOCK_HEADER_SIZE_MAX];
};


static lzma_ret
stream_decode(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)
{
	// When decoding the actual Block, it may be able to produce more
	// output even if we don't give it any new input.
	while (*out_pos < out_size && (*in_pos < in_size
			|| coder->sequence == SEQ_BLOCK))
	switch (coder->sequence) {
	case SEQ_STREAM_HEADER: {
		// Copy the Stream Header to the internal buffer.
		bufcpy(in, in_pos, in_size, coder->buffer, &coder->buffer_pos,
				LZMA_STREAM_HEADER_SIZE);

		// Return if we didn't get the whole Stream Header yet.
		if (coder->buffer_pos < LZMA_STREAM_HEADER_SIZE)
			return LZMA_OK;

		coder->buffer_pos = 0;

		// Decode the Stream Header.
		return_if_error(lzma_stream_header_decode(
				&coder->stream_flags, coder->buffer));

		// Copy the type of the Check so that Block Header and Block
		// decoders see it.
		coder->block_options.check = coder->stream_flags.check;

		// Even if we return LZMA_UNSUPPORTED_CHECK below, we want
		// to continue from Block Header decoding.
		coder->sequence = SEQ_BLOCK_HEADER;

		// Detect if the Check type is supported and give appropriate
		// warning if it isn't. We don't warn every time a new Block
		// is started.
		if (!lzma_available_checks[coder->block_options.check])
			return LZMA_UNSUPPORTED_CHECK;

		break;
	}

	case SEQ_BLOCK_HEADER: {
		if (coder->buffer_pos == 0) {
			// Detect if it's Index.
			if (in[*in_pos] == 0x00) {
				coder->sequence = SEQ_INDEX;
				break;
			}

			// Calculate the size of the Block Header. Note that
			// Block Header decoder wants to see this byte too
			// so don't advance *in_pos.
			coder->block_options.header_size
					= lzma_block_header_size_decode(
						in[*in_pos]);
		}

		// Copy the Block Header to the internal buffer.
		bufcpy(in, in_pos, in_size, coder->buffer, &coder->buffer_pos,
				coder->block_options.header_size);

		// Return if we didn't get the whole Block Header yet.
		if (coder->buffer_pos < coder->block_options.header_size)
			return LZMA_OK;

		coder->buffer_pos = 0;

		// Set up a buffer to hold the filter chain. Block Header
		// decoder will initialize all members of this array so
		// we don't need to do it here.
		lzma_options_filter filters[LZMA_BLOCK_FILTERS_MAX + 1];
		coder->block_options.filters = filters;

		// Decode the Block Header.
		return_if_error(lzma_block_header_decode(&coder->block_options,
				allocator, coder->buffer));

		// Initialize the Block decoder.
		const lzma_ret ret = lzma_block_decoder_init(
				&coder->block_decoder,
				allocator, &coder->block_options);

		// Free the allocated filter options since they are needed
		// only to initialize the Block decoder.
		for (size_t i = 0; i < LZMA_BLOCK_FILTERS_MAX; ++i)
			lzma_free(filters[i].options, allocator);

		coder->block_options.filters = NULL;

		// Check if Block enocoder initialization succeeded. Don't
		// warn about unsupported check anymore since we did it
		// earlier if it was needed.
		if (ret != LZMA_OK && ret != LZMA_UNSUPPORTED_CHECK)
			return ret;

		coder->sequence = SEQ_BLOCK;
		break;
	}

	case SEQ_BLOCK: {
		lzma_ret ret = coder->block_decoder.code(
				coder->block_decoder.coder, allocator,
				in, in_pos, in_size, out, out_pos, out_size,
				action);

		if (ret != LZMA_STREAM_END)
			return ret;

		// Block decoded successfully. Add the new size pair to
		// the Index hash.
		return_if_error(lzma_index_hash_append(coder->index_hash,
				lzma_block_total_size_get(
					&coder->block_options),
				coder->block_options.uncompressed_size));

		coder->sequence = SEQ_BLOCK_HEADER;
		break;
	}

	case SEQ_INDEX: {
		// Decode the Index and compare it to the hash calculated
		// from the sizes of the Blocks (if any).
		const lzma_ret ret = lzma_index_hash_decode(coder->index_hash,
				in, in_pos, in_size);
		if (ret != LZMA_STREAM_END)
			return ret;

		coder->sequence = SEQ_STREAM_FOOTER;
		break;
	}

	case SEQ_STREAM_FOOTER:
		// Copy the Stream Footer to the internal buffer.
		bufcpy(in, in_pos, in_size, coder->buffer, &coder->buffer_pos,
				LZMA_STREAM_HEADER_SIZE);

		// Return if we didn't get the whole Stream Footer yet.
		if (coder->buffer_pos < LZMA_STREAM_HEADER_SIZE)
			return LZMA_OK;

		// Decode the Stream Footer.
		lzma_stream_flags footer_flags;
		return_if_error(lzma_stream_footer_decode(
				&footer_flags, coder->buffer));

		// Check that Index Size stored in the Stream Footer matches
		// the real size of the Index field.
		if (lzma_index_hash_size(coder->index_hash)
				!= footer_flags.backward_size)
			return LZMA_DATA_ERROR;

		// Compare that the Stream Flags fields are identical in
		// both Stream Header and Stream Footer.
		if (!lzma_stream_flags_equal(&coder->stream_flags,
				&footer_flags))
			return LZMA_DATA_ERROR;

		return LZMA_STREAM_END;

	default:
		assert(0);
		return LZMA_PROG_ERROR;
	}

	return LZMA_OK;
}


static void
stream_decoder_end(lzma_coder *coder, lzma_allocator *allocator)
{
	lzma_next_coder_end(&coder->block_decoder, allocator);
	lzma_index_hash_end(coder->index_hash, allocator);
	lzma_free(coder, allocator);
	return;
}


static lzma_ret
stream_decoder_init(lzma_next_coder *next, lzma_allocator *allocator)
{
	if (next->coder == NULL) {
		next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
		if (next->coder == NULL)
			return LZMA_MEM_ERROR;

		next->code = &stream_decode;
		next->end = &stream_decoder_end;

		next->coder->block_decoder = LZMA_NEXT_CODER_INIT;
		next->coder->index_hash = NULL;
	}

	// Initialize the Index hash used to verify the Index.
	next->coder->index_hash = lzma_index_hash_init(
			next->coder->index_hash, allocator);
	if (next->coder->index_hash == NULL)
		return LZMA_MEM_ERROR;

	// Reset the rest of the variables.
	next->coder->sequence = SEQ_STREAM_HEADER;
	next->coder->block_options.filters = NULL;
	next->coder->buffer_pos = 0;

	return LZMA_OK;
}


extern lzma_ret
lzma_stream_decoder_init(lzma_next_coder *next, lzma_allocator *allocator)
{
	lzma_next_coder_init0(stream_decoder_init, next, allocator);
}


extern LZMA_API lzma_ret
lzma_stream_decoder(lzma_stream *strm)
{
	lzma_next_strm_init0(strm, stream_decoder_init);

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

	return LZMA_OK;
}