(root)/
xz-5.4.5/
src/
liblzma/
common/
microlzma_encoder.c
       1  ///////////////////////////////////////////////////////////////////////////////
       2  //
       3  /// \file       microlzma_encoder.c
       4  /// \brief      Encode into MicroLZMA format
       5  //
       6  //  Author:     Lasse Collin
       7  //
       8  //  This file has been put into the public domain.
       9  //  You can do whatever you want with this file.
      10  //
      11  ///////////////////////////////////////////////////////////////////////////////
      12  
      13  #include "lzma_encoder.h"
      14  
      15  
      16  typedef struct {
      17  	/// LZMA1 encoder
      18  	lzma_next_coder lzma;
      19  
      20  	/// LZMA properties byte (lc/lp/pb)
      21  	uint8_t props;
      22  } lzma_microlzma_coder;
      23  
      24  
      25  static lzma_ret
      26  microlzma_encode(void *coder_ptr, const lzma_allocator *allocator,
      27  		const uint8_t *restrict in, size_t *restrict in_pos,
      28  		size_t in_size, uint8_t *restrict out,
      29  		size_t *restrict out_pos, size_t out_size, lzma_action action)
      30  {
      31  	lzma_microlzma_coder *coder = coder_ptr;
      32  
      33  	// Remember *out_pos so that we can overwrite the first byte with
      34  	// the LZMA properties byte.
      35  	const size_t out_start = *out_pos;
      36  
      37  	// Remember *in_pos so that we can set it based on how many
      38  	// uncompressed bytes were actually encoded.
      39  	const size_t in_start = *in_pos;
      40  
      41  	// Set the output size limit based on the available output space.
      42  	// We know that the encoder supports set_out_limit() so
      43  	// LZMA_OPTIONS_ERROR isn't possible. LZMA_BUF_ERROR is possible
      44  	// but lzma_code() has an assertion to not allow it to be returned
      45  	// from here and I don't want to change that for now, so
      46  	// LZMA_BUF_ERROR becomes LZMA_PROG_ERROR.
      47  	uint64_t uncomp_size;
      48  	if (coder->lzma.set_out_limit(coder->lzma.coder,
      49  			&uncomp_size, out_size - *out_pos) != LZMA_OK)
      50  		return LZMA_PROG_ERROR;
      51  
      52  	// set_out_limit fails if this isn't true.
      53  	assert(out_size - *out_pos >= 6);
      54  
      55  	// Encode as much as possible.
      56  	const lzma_ret ret = coder->lzma.code(coder->lzma.coder, allocator,
      57  			in, in_pos, in_size, out, out_pos, out_size, action);
      58  
      59  	if (ret != LZMA_STREAM_END) {
      60  		if (ret == LZMA_OK) {
      61  			assert(0);
      62  			return LZMA_PROG_ERROR;
      63  		}
      64  
      65  		return ret;
      66  	}
      67  
      68  	// The first output byte is bitwise-negation of the properties byte.
      69  	// We know that there is space for this byte because set_out_limit
      70  	// and the actual encoding succeeded.
      71  	out[out_start] = (uint8_t)(~coder->props);
      72  
      73  	// The LZMA encoder likely read more input than it was able to encode.
      74  	// Set *in_pos based on uncomp_size.
      75  	assert(uncomp_size <= in_size - in_start);
      76  	*in_pos = in_start + (size_t)(uncomp_size);
      77  
      78  	return ret;
      79  }
      80  
      81  
      82  static void
      83  microlzma_encoder_end(void *coder_ptr, const lzma_allocator *allocator)
      84  {
      85  	lzma_microlzma_coder *coder = coder_ptr;
      86  	lzma_next_end(&coder->lzma, allocator);
      87  	lzma_free(coder, allocator);
      88  	return;
      89  }
      90  
      91  
      92  static lzma_ret
      93  microlzma_encoder_init(lzma_next_coder *next, const lzma_allocator *allocator,
      94  		const lzma_options_lzma *options)
      95  {
      96  	lzma_next_coder_init(&microlzma_encoder_init, next, allocator);
      97  
      98  	lzma_microlzma_coder *coder = next->coder;
      99  
     100  	if (coder == NULL) {
     101  		coder = lzma_alloc(sizeof(lzma_microlzma_coder), allocator);
     102  		if (coder == NULL)
     103  			return LZMA_MEM_ERROR;
     104  
     105  		next->coder = coder;
     106  		next->code = &microlzma_encode;
     107  		next->end = &microlzma_encoder_end;
     108  
     109  		coder->lzma = LZMA_NEXT_CODER_INIT;
     110  	}
     111  
     112  	// Encode the properties byte. Bitwise-negation of it will be the
     113  	// first output byte.
     114  	if (lzma_lzma_lclppb_encode(options, &coder->props))
     115  		return LZMA_OPTIONS_ERROR;
     116  
     117  	// Initialize the LZMA encoder.
     118  	const lzma_filter_info filters[2] = {
     119  		{
     120  			.id = LZMA_FILTER_LZMA1,
     121  			.init = &lzma_lzma_encoder_init,
     122  			.options = (void *)(options),
     123  		}, {
     124  			.init = NULL,
     125  		}
     126  	};
     127  
     128  	return lzma_next_filter_init(&coder->lzma, allocator, filters);
     129  }
     130  
     131  
     132  extern LZMA_API(lzma_ret)
     133  lzma_microlzma_encoder(lzma_stream *strm, const lzma_options_lzma *options)
     134  {
     135  	lzma_next_strm_init(microlzma_encoder_init, strm, options);
     136  
     137  	strm->internal->supported_actions[LZMA_FINISH] = true;
     138  
     139  	return LZMA_OK;
     140  
     141  }