Path: blob/master/Utilities/cmliblzma/liblzma/common/microlzma_decoder.c
3153 views
// SPDX-License-Identifier: 0BSD12///////////////////////////////////////////////////////////////////////////////3//4/// \file microlzma_decoder.c5/// \brief Decode MicroLZMA format6//7// Author: Lasse Collin8//9///////////////////////////////////////////////////////////////////////////////1011#include "lzma_decoder.h"12#include "lz_decoder.h"131415typedef struct {16/// LZMA1 decoder17lzma_next_coder lzma;1819/// Compressed size of the stream as given by the application.20/// This must be exactly correct.21///22/// This will be decremented when input is read.23uint64_t comp_size;2425/// Uncompressed size of the stream as given by the application.26/// This may be less than the actual uncompressed size if27/// uncomp_size_is_exact is false.28///29/// This will be decremented when output is produced.30lzma_vli uncomp_size;3132/// LZMA dictionary size as given by the application33uint32_t dict_size;3435/// If true, the exact uncompressed size is known. If false,36/// uncomp_size may be smaller than the real uncompressed size;37/// uncomp_size may never be bigger than the real uncompressed size.38bool uncomp_size_is_exact;3940/// True once the first byte of the MicroLZMA stream41/// has been processed.42bool props_decoded;43} lzma_microlzma_coder;444546static lzma_ret47microlzma_decode(void *coder_ptr, const lzma_allocator *allocator,48const uint8_t *restrict in, size_t *restrict in_pos,49size_t in_size, uint8_t *restrict out,50size_t *restrict out_pos, size_t out_size, lzma_action action)51{52lzma_microlzma_coder *coder = coder_ptr;5354// Remember the in start position so that we can update comp_size.55const size_t in_start = *in_pos;5657// Remember the out start position so that we can update uncomp_size.58const size_t out_start = *out_pos;5960// Limit the amount of input so that the decoder won't read more than61// comp_size. This is required when uncomp_size isn't exact because62// in that case the LZMA decoder will try to decode more input even63// when it has no output space (it can be looking for EOPM).64if (in_size - *in_pos > coder->comp_size)65in_size = *in_pos + (size_t)(coder->comp_size);6667// When the exact uncompressed size isn't known, we must limit68// the available output space to prevent the LZMA decoder from69// trying to decode too much.70if (!coder->uncomp_size_is_exact71&& out_size - *out_pos > coder->uncomp_size)72out_size = *out_pos + (size_t)(coder->uncomp_size);7374if (!coder->props_decoded) {75// There must be at least one byte of input to decode76// the properties byte.77if (*in_pos >= in_size)78return LZMA_OK;7980lzma_options_lzma options = {81.dict_size = coder->dict_size,82.preset_dict = NULL,83.preset_dict_size = 0,84.ext_flags = 0, // EOPM not allowed when size is known85.ext_size_low = UINT32_MAX, // Unknown size by default86.ext_size_high = UINT32_MAX,87};8889if (coder->uncomp_size_is_exact)90lzma_set_ext_size(options, coder->uncomp_size);9192// The properties are stored as bitwise-negation93// of the typical encoding.94if (lzma_lzma_lclppb_decode(&options, ~in[*in_pos]))95return LZMA_OPTIONS_ERROR;9697++*in_pos;9899// Initialize the decoder.100lzma_filter_info filters[2] = {101{102.id = LZMA_FILTER_LZMA1EXT,103.init = &lzma_lzma_decoder_init,104.options = &options,105}, {106.init = NULL,107}108};109110return_if_error(lzma_next_filter_init(&coder->lzma,111allocator, filters));112113// Pass one dummy 0x00 byte to the LZMA decoder since that114// is what it expects the first byte to be.115const uint8_t dummy_in = 0;116size_t dummy_in_pos = 0;117if (coder->lzma.code(coder->lzma.coder, allocator,118&dummy_in, &dummy_in_pos, 1,119out, out_pos, out_size, LZMA_RUN) != LZMA_OK)120return LZMA_PROG_ERROR;121122assert(dummy_in_pos == 1);123coder->props_decoded = true;124}125126// The rest is normal LZMA decoding.127lzma_ret ret = coder->lzma.code(coder->lzma.coder, allocator,128in, in_pos, in_size,129out, out_pos, out_size, action);130131// Update the remaining compressed size.132assert(coder->comp_size >= *in_pos - in_start);133coder->comp_size -= *in_pos - in_start;134135if (coder->uncomp_size_is_exact) {136// After successful decompression of the complete stream137// the compressed size must match.138if (ret == LZMA_STREAM_END && coder->comp_size != 0)139ret = LZMA_DATA_ERROR;140} else {141// Update the amount of output remaining.142assert(coder->uncomp_size >= *out_pos - out_start);143coder->uncomp_size -= *out_pos - out_start;144145// - We must not get LZMA_STREAM_END because the stream146// shouldn't have EOPM.147// - We must use uncomp_size to determine when to148// return LZMA_STREAM_END.149if (ret == LZMA_STREAM_END)150ret = LZMA_DATA_ERROR;151else if (coder->uncomp_size == 0)152ret = LZMA_STREAM_END;153}154155return ret;156}157158159static void160microlzma_decoder_end(void *coder_ptr, const lzma_allocator *allocator)161{162lzma_microlzma_coder *coder = coder_ptr;163lzma_next_end(&coder->lzma, allocator);164lzma_free(coder, allocator);165return;166}167168169static lzma_ret170microlzma_decoder_init(lzma_next_coder *next, const lzma_allocator *allocator,171uint64_t comp_size,172uint64_t uncomp_size, bool uncomp_size_is_exact,173uint32_t dict_size)174{175lzma_next_coder_init(µlzma_decoder_init, next, allocator);176177lzma_microlzma_coder *coder = next->coder;178179if (coder == NULL) {180coder = lzma_alloc(sizeof(lzma_microlzma_coder), allocator);181if (coder == NULL)182return LZMA_MEM_ERROR;183184next->coder = coder;185next->code = µlzma_decode;186next->end = µlzma_decoder_end;187188coder->lzma = LZMA_NEXT_CODER_INIT;189}190191// The public API is uint64_t but the internal LZ decoder API uses192// lzma_vli.193if (uncomp_size > LZMA_VLI_MAX)194return LZMA_OPTIONS_ERROR;195196coder->comp_size = comp_size;197coder->uncomp_size = uncomp_size;198coder->uncomp_size_is_exact = uncomp_size_is_exact;199coder->dict_size = dict_size;200201coder->props_decoded = false;202203return LZMA_OK;204}205206207extern LZMA_API(lzma_ret)208lzma_microlzma_decoder(lzma_stream *strm, uint64_t comp_size,209uint64_t uncomp_size, lzma_bool uncomp_size_is_exact,210uint32_t dict_size)211{212lzma_next_strm_init(microlzma_decoder_init, strm, comp_size,213uncomp_size, uncomp_size_is_exact, dict_size);214215strm->internal->supported_actions[LZMA_RUN] = true;216strm->internal->supported_actions[LZMA_FINISH] = true;217218return LZMA_OK;219}220221222