| /////////////////////////////////////////////////////////////////////////////// |
| // |
| /// \file lzma2_decoder.c |
| /// \brief LZMA2 decoder |
| /// |
| // Authors: Igor Pavlov |
| // Lasse Collin |
| // |
| // This file has been put into the public domain. |
| // You can do whatever you want with this file. |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include "lzma2_decoder.h" |
| #include "lz_decoder.h" |
| #include "lzma_decoder.h" |
| |
| |
| struct lzma_coder_s { |
| enum sequence { |
| SEQ_CONTROL, |
| SEQ_UNCOMPRESSED_1, |
| SEQ_UNCOMPRESSED_2, |
| SEQ_COMPRESSED_0, |
| SEQ_COMPRESSED_1, |
| SEQ_PROPERTIES, |
| SEQ_LZMA, |
| SEQ_COPY, |
| } sequence; |
| |
| /// Sequence after the size fields have been decoded. |
| enum sequence next_sequence; |
| |
| /// LZMA decoder |
| lzma_lz_decoder lzma; |
| |
| /// Uncompressed size of LZMA chunk |
| size_t uncompressed_size; |
| |
| /// Compressed size of the chunk (naturally equals to uncompressed |
| /// size of uncompressed chunk) |
| size_t compressed_size; |
| |
| /// True if properties are needed. This is false before the |
| /// first LZMA chunk. |
| bool need_properties; |
| |
| /// True if dictionary reset is needed. This is false before the |
| /// first chunk (LZMA or uncompressed). |
| bool need_dictionary_reset; |
| |
| lzma_options_lzma options; |
| }; |
| |
| |
| static lzma_ret |
| lzma2_decode(lzma_coder *restrict coder, lzma_dict *restrict dict, |
| const uint8_t *restrict in, size_t *restrict in_pos, |
| size_t in_size) |
| { |
| // With SEQ_LZMA it is possible that no new input is needed to do |
| // some progress. The rest of the sequences assume that there is |
| // at least one byte of input. |
| while (*in_pos < in_size || coder->sequence == SEQ_LZMA) |
| switch (coder->sequence) { |
| case SEQ_CONTROL: { |
| const uint32_t control = in[*in_pos]; |
| ++*in_pos; |
| |
| // End marker |
| if (control == 0x00) |
| return LZMA_STREAM_END; |
| |
| if (control >= 0xE0 || control == 1) { |
| // Dictionary reset implies that next LZMA chunk has |
| // to set new properties. |
| coder->need_properties = true; |
| coder->need_dictionary_reset = true; |
| } else if (coder->need_dictionary_reset) { |
| return LZMA_DATA_ERROR; |
| } |
| |
| if (control >= 0x80) { |
| // LZMA chunk. The highest five bits of the |
| // uncompressed size are taken from the control byte. |
| coder->uncompressed_size = (control & 0x1F) << 16; |
| coder->sequence = SEQ_UNCOMPRESSED_1; |
| |
| // See if there are new properties or if we need to |
| // reset the state. |
| if (control >= 0xC0) { |
| // When there are new properties, state reset |
| // is done at SEQ_PROPERTIES. |
| coder->need_properties = false; |
| coder->next_sequence = SEQ_PROPERTIES; |
| |
| } else if (coder->need_properties) { |
| return LZMA_DATA_ERROR; |
| |
| } else { |
| coder->next_sequence = SEQ_LZMA; |
| |
| // If only state reset is wanted with old |
| // properties, do the resetting here for |
| // simplicity. |
| if (control >= 0xA0) |
| coder->lzma.reset(coder->lzma.coder, |
| &coder->options); |
| } |
| } else { |
| // Invalid control values |
| if (control > 2) |
| return LZMA_DATA_ERROR; |
| |
| // It's uncompressed chunk |
| coder->sequence = SEQ_COMPRESSED_0; |
| coder->next_sequence = SEQ_COPY; |
| } |
| |
| if (coder->need_dictionary_reset) { |
| // Finish the dictionary reset and let the caller |
| // flush the dictionary to the actual output buffer. |
| coder->need_dictionary_reset = false; |
| dict_reset(dict); |
| return LZMA_OK; |
| } |
| |
| break; |
| } |
| |
| case SEQ_UNCOMPRESSED_1: |
| coder->uncompressed_size += (uint32_t)(in[(*in_pos)++]) << 8; |
| coder->sequence = SEQ_UNCOMPRESSED_2; |
| break; |
| |
| case SEQ_UNCOMPRESSED_2: |
| coder->uncompressed_size += in[(*in_pos)++] + 1; |
| coder->sequence = SEQ_COMPRESSED_0; |
| coder->lzma.set_uncompressed(coder->lzma.coder, |
| coder->uncompressed_size); |
| break; |
| |
| case SEQ_COMPRESSED_0: |
| coder->compressed_size = (uint32_t)(in[(*in_pos)++]) << 8; |
| coder->sequence = SEQ_COMPRESSED_1; |
| break; |
| |
| case SEQ_COMPRESSED_1: |
| coder->compressed_size += in[(*in_pos)++] + 1; |
| coder->sequence = coder->next_sequence; |
| break; |
| |
| case SEQ_PROPERTIES: |
| if (lzma_lzma_lclppb_decode(&coder->options, in[(*in_pos)++])) |
| return LZMA_DATA_ERROR; |
| |
| coder->lzma.reset(coder->lzma.coder, &coder->options); |
| |
| coder->sequence = SEQ_LZMA; |
| break; |
| |
| case SEQ_LZMA: { |
| // Store the start offset so that we can update |
| // coder->compressed_size later. |
| const size_t in_start = *in_pos; |
| |
| // Decode from in[] to *dict. |
| const lzma_ret ret = coder->lzma.code(coder->lzma.coder, |
| dict, in, in_pos, in_size); |
| |
| // Validate and update coder->compressed_size. |
| const size_t in_used = *in_pos - in_start; |
| if (in_used > coder->compressed_size) |
| return LZMA_DATA_ERROR; |
| |
| coder->compressed_size -= in_used; |
| |
| // Return if we didn't finish the chunk, or an error occurred. |
| if (ret != LZMA_STREAM_END) |
| return ret; |
| |
| // The LZMA decoder must have consumed the whole chunk now. |
| // We don't need to worry about uncompressed size since it |
| // is checked by the LZMA decoder. |
| if (coder->compressed_size != 0) |
| return LZMA_DATA_ERROR; |
| |
| coder->sequence = SEQ_CONTROL; |
| break; |
| } |
| |
| case SEQ_COPY: { |
| // Copy from input to the dictionary as is. |
| dict_write(dict, in, in_pos, in_size, &coder->compressed_size); |
| if (coder->compressed_size != 0) |
| return LZMA_OK; |
| |
| coder->sequence = SEQ_CONTROL; |
| break; |
| } |
| |
| default: |
| assert(0); |
| return LZMA_PROG_ERROR; |
| } |
| |
| return LZMA_OK; |
| } |
| |
| |
| static void |
| lzma2_decoder_end(lzma_coder *coder, const lzma_allocator *allocator) |
| { |
| assert(coder->lzma.end == NULL); |
| lzma_free(coder->lzma.coder, allocator); |
| |
| lzma_free(coder, allocator); |
| |
| return; |
| } |
| |
| |
| static lzma_ret |
| lzma2_decoder_init(lzma_lz_decoder *lz, const lzma_allocator *allocator, |
| const void *opt, lzma_lz_options *lz_options) |
| { |
| if (lz->coder == NULL) { |
| lz->coder = lzma_alloc(sizeof(lzma_coder), allocator); |
| if (lz->coder == NULL) |
| return LZMA_MEM_ERROR; |
| |
| lz->code = &lzma2_decode; |
| lz->end = &lzma2_decoder_end; |
| |
| lz->coder->lzma = LZMA_LZ_DECODER_INIT; |
| } |
| |
| const lzma_options_lzma *options = opt; |
| |
| lz->coder->sequence = SEQ_CONTROL; |
| lz->coder->need_properties = true; |
| lz->coder->need_dictionary_reset = options->preset_dict == NULL |
| || options->preset_dict_size == 0; |
| |
| return lzma_lzma_decoder_create(&lz->coder->lzma, |
| allocator, options, lz_options); |
| } |
| |
| |
| extern lzma_ret |
| lzma_lzma2_decoder_init(lzma_next_coder *next, const lzma_allocator *allocator, |
| const lzma_filter_info *filters) |
| { |
| // LZMA2 can only be the last filter in the chain. This is enforced |
| // by the raw_decoder initialization. |
| assert(filters[1].init == NULL); |
| |
| return lzma_lz_decoder_init(next, allocator, filters, |
| &lzma2_decoder_init); |
| } |
| |
| |
| extern uint64_t |
| lzma_lzma2_decoder_memusage(const void *options) |
| { |
| return sizeof(lzma_coder) |
| + lzma_lzma_decoder_memusage_nocheck(options); |
| } |
| |
| |
| extern lzma_ret |
| lzma_lzma2_props_decode(void **options, const lzma_allocator *allocator, |
| const uint8_t *props, size_t props_size) |
| { |
| if (props_size != 1) |
| return LZMA_OPTIONS_ERROR; |
| |
| // Check that reserved bits are unset. |
| if (props[0] & 0xC0) |
| return LZMA_OPTIONS_ERROR; |
| |
| // Decode the dictionary size. |
| if (props[0] > 40) |
| return LZMA_OPTIONS_ERROR; |
| |
| lzma_options_lzma *opt = lzma_alloc( |
| sizeof(lzma_options_lzma), allocator); |
| if (opt == NULL) |
| return LZMA_MEM_ERROR; |
| |
| if (props[0] == 40) { |
| opt->dict_size = UINT32_MAX; |
| } else { |
| opt->dict_size = 2 | (props[0] & 1); |
| opt->dict_size <<= props[0] / 2 + 11; |
| } |
| |
| opt->preset_dict = NULL; |
| opt->preset_dict_size = 0; |
| |
| *options = opt; |
| |
| return LZMA_OK; |
| } |