1 // SPDX-License-Identifier: 0BSD 2 3 /////////////////////////////////////////////////////////////////////////////// 4 // 5 /// \file microlzma_decoder.c 6 /// \brief Decode MicroLZMA format 7 // 8 // Author: Lasse Collin 9 // 10 /////////////////////////////////////////////////////////////////////////////// 11 12 #include "lzma_decoder.h" 13 #include "lz_decoder.h" 14 15 16 typedef struct { 17 /// LZMA1 decoder 18 lzma_next_coder lzma; 19 20 /// Compressed size of the stream as given by the application. 21 /// This must be exactly correct. 22 /// 23 /// This will be decremented when input is read. 24 uint64_t comp_size; 25 26 /// Uncompressed size of the stream as given by the application. 27 /// This may be less than the actual uncompressed size if 28 /// uncomp_size_is_exact is false. 29 /// 30 /// This will be decremented when output is produced. 31 lzma_vli uncomp_size; 32 33 /// LZMA dictionary size as given by the application 34 uint32_t dict_size; 35 36 /// If true, the exact uncompressed size is known. If false, 37 /// uncomp_size may be smaller than the real uncompressed size; 38 /// uncomp_size may never be bigger than the real uncompressed size. 39 bool uncomp_size_is_exact; 40 41 /// True once the first byte of the MicroLZMA stream 42 /// has been processed. 43 bool props_decoded; 44 } lzma_microlzma_coder; 45 46 47 static lzma_ret 48 microlzma_decode(void *coder_ptr, const lzma_allocator *allocator, 49 const uint8_t *restrict in, size_t *restrict in_pos, 50 size_t in_size, uint8_t *restrict out, 51 size_t *restrict out_pos, size_t out_size, lzma_action action) 52 { 53 lzma_microlzma_coder *coder = coder_ptr; 54 55 // Remember the in start position so that we can update comp_size. 56 const size_t in_start = *in_pos; 57 58 // Remember the out start position so that we can update uncomp_size. 59 const size_t out_start = *out_pos; 60 61 // Limit the amount of input so that the decoder won't read more than 62 // comp_size. This is required when uncomp_size isn't exact because 63 // in that case the LZMA decoder will try to decode more input even 64 // when it has no output space (it can be looking for EOPM). 65 if (in_size - *in_pos > coder->comp_size) 66 in_size = *in_pos + (size_t)(coder->comp_size); 67 68 // When the exact uncompressed size isn't known, we must limit 69 // the available output space to prevent the LZMA decoder from 70 // trying to decode too much. 71 if (!coder->uncomp_size_is_exact 72 && out_size - *out_pos > coder->uncomp_size) 73 out_size = *out_pos + (size_t)(coder->uncomp_size); 74 75 if (!coder->props_decoded) { 76 // There must be at least one byte of input to decode 77 // the properties byte. 78 if (*in_pos >= in_size) 79 return LZMA_OK; 80 81 lzma_options_lzma options = { 82 .dict_size = coder->dict_size, 83 .preset_dict = NULL, 84 .preset_dict_size = 0, 85 .ext_flags = 0, // EOPM not allowed when size is known 86 .ext_size_low = UINT32_MAX, // Unknown size by default 87 .ext_size_high = UINT32_MAX, 88 }; 89 90 if (coder->uncomp_size_is_exact) 91 lzma_set_ext_size(options, coder->uncomp_size); 92 93 // The properties are stored as bitwise-negation 94 // of the typical encoding. 95 if (lzma_lzma_lclppb_decode(&options, ~in[*in_pos])) 96 return LZMA_OPTIONS_ERROR; 97 98 ++*in_pos; 99 100 // Initialize the decoder. 101 lzma_filter_info filters[2] = { 102 { 103 .id = LZMA_FILTER_LZMA1EXT, 104 .init = &lzma_lzma_decoder_init, 105 .options = &options, 106 }, { 107 .init = NULL, 108 } 109 }; 110 111 return_if_error(lzma_next_filter_init(&coder->lzma, 112 allocator, filters)); 113 114 // Pass one dummy 0x00 byte to the LZMA decoder since that 115 // is what it expects the first byte to be. 116 const uint8_t dummy_in = 0; 117 size_t dummy_in_pos = 0; 118 if (coder->lzma.code(coder->lzma.coder, allocator, 119 &dummy_in, &dummy_in_pos, 1, 120 out, out_pos, out_size, LZMA_RUN) != LZMA_OK) 121 return LZMA_PROG_ERROR; 122 123 assert(dummy_in_pos == 1); 124 coder->props_decoded = true; 125 } 126 127 // The rest is normal LZMA decoding. 128 lzma_ret ret = coder->lzma.code(coder->lzma.coder, allocator, 129 in, in_pos, in_size, 130 out, out_pos, out_size, action); 131 132 // Update the remaining compressed size. 133 assert(coder->comp_size >= *in_pos - in_start); 134 coder->comp_size -= *in_pos - in_start; 135 136 if (coder->uncomp_size_is_exact) { 137 // After successful decompression of the complete stream 138 // the compressed size must match. 139 if (ret == LZMA_STREAM_END && coder->comp_size != 0) 140 ret = LZMA_DATA_ERROR; 141 } else { 142 // Update the amount of output remaining. 143 assert(coder->uncomp_size >= *out_pos - out_start); 144 coder->uncomp_size -= *out_pos - out_start; 145 146 // - We must not get LZMA_STREAM_END because the stream 147 // shouldn't have EOPM. 148 // - We must use uncomp_size to determine when to 149 // return LZMA_STREAM_END. 150 if (ret == LZMA_STREAM_END) 151 ret = LZMA_DATA_ERROR; 152 else if (coder->uncomp_size == 0) 153 ret = LZMA_STREAM_END; 154 } 155 156 return ret; 157 } 158 159 160 static void 161 microlzma_decoder_end(void *coder_ptr, const lzma_allocator *allocator) 162 { 163 lzma_microlzma_coder *coder = coder_ptr; 164 lzma_next_end(&coder->lzma, allocator); 165 lzma_free(coder, allocator); 166 return; 167 } 168 169 170 static lzma_ret 171 microlzma_decoder_init(lzma_next_coder *next, const lzma_allocator *allocator, 172 uint64_t comp_size, 173 uint64_t uncomp_size, bool uncomp_size_is_exact, 174 uint32_t dict_size) 175 { 176 lzma_next_coder_init(µlzma_decoder_init, next, allocator); 177 178 lzma_microlzma_coder *coder = next->coder; 179 180 if (coder == NULL) { 181 coder = lzma_alloc(sizeof(lzma_microlzma_coder), allocator); 182 if (coder == NULL) 183 return LZMA_MEM_ERROR; 184 185 next->coder = coder; 186 next->code = µlzma_decode; 187 next->end = µlzma_decoder_end; 188 189 coder->lzma = LZMA_NEXT_CODER_INIT; 190 } 191 192 // The public API is uint64_t but the internal LZ decoder API uses 193 // lzma_vli. 194 if (uncomp_size > LZMA_VLI_MAX) 195 return LZMA_OPTIONS_ERROR; 196 197 coder->comp_size = comp_size; 198 coder->uncomp_size = uncomp_size; 199 coder->uncomp_size_is_exact = uncomp_size_is_exact; 200 coder->dict_size = dict_size; 201 202 coder->props_decoded = false; 203 204 return LZMA_OK; 205 } 206 207 208 extern LZMA_API(lzma_ret) 209 lzma_microlzma_decoder(lzma_stream *strm, uint64_t comp_size, 210 uint64_t uncomp_size, lzma_bool uncomp_size_is_exact, 211 uint32_t dict_size) 212 { 213 lzma_next_strm_init(microlzma_decoder_init, strm, comp_size, 214 uncomp_size, uncomp_size_is_exact, dict_size); 215 216 strm->internal->supported_actions[LZMA_RUN] = true; 217 strm->internal->supported_actions[LZMA_FINISH] = true; 218 219 return LZMA_OK; 220 } 221