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 const lzma_ret ret = lzma_next_filter_init(&coder->lzma, 112 allocator, filters); 113 if (ret != LZMA_OK) { 114 lzma_next_end(&coder->lzma, allocator); 115 return ret; 116 } 117 118 // Pass one dummy 0x00 byte to the LZMA decoder since that 119 // is what it expects the first byte to be. 120 const uint8_t dummy_in = 0; 121 size_t dummy_in_pos = 0; 122 if (coder->lzma.code(coder->lzma.coder, allocator, 123 &dummy_in, &dummy_in_pos, 1, 124 out, out_pos, out_size, LZMA_RUN) != LZMA_OK) 125 return LZMA_PROG_ERROR; 126 127 assert(dummy_in_pos == 1); 128 coder->props_decoded = true; 129 } 130 131 // The rest is normal LZMA decoding. 132 lzma_ret ret = coder->lzma.code(coder->lzma.coder, allocator, 133 in, in_pos, in_size, 134 out, out_pos, out_size, action); 135 136 // Update the remaining compressed size. 137 assert(coder->comp_size >= *in_pos - in_start); 138 coder->comp_size -= *in_pos - in_start; 139 140 if (coder->uncomp_size_is_exact) { 141 // After successful decompression of the complete stream 142 // the compressed size must match. 143 if (ret == LZMA_STREAM_END && coder->comp_size != 0) 144 ret = LZMA_DATA_ERROR; 145 } else { 146 // Update the amount of output remaining. 147 assert(coder->uncomp_size >= *out_pos - out_start); 148 coder->uncomp_size -= *out_pos - out_start; 149 150 // - We must not get LZMA_STREAM_END because the stream 151 // shouldn't have EOPM. 152 // - We must use uncomp_size to determine when to 153 // return LZMA_STREAM_END. 154 if (ret == LZMA_STREAM_END) 155 ret = LZMA_DATA_ERROR; 156 else if (coder->uncomp_size == 0) 157 ret = LZMA_STREAM_END; 158 } 159 160 return ret; 161 } 162 163 164 static void 165 microlzma_decoder_end(void *coder_ptr, const lzma_allocator *allocator) 166 { 167 lzma_microlzma_coder *coder = coder_ptr; 168 lzma_next_end(&coder->lzma, allocator); 169 lzma_free(coder, allocator); 170 return; 171 } 172 173 174 static lzma_ret 175 microlzma_decoder_init(lzma_next_coder *next, const lzma_allocator *allocator, 176 uint64_t comp_size, 177 uint64_t uncomp_size, bool uncomp_size_is_exact, 178 uint32_t dict_size) 179 { 180 lzma_next_coder_init(µlzma_decoder_init, next, allocator); 181 182 lzma_microlzma_coder *coder = next->coder; 183 184 if (coder == NULL) { 185 coder = lzma_alloc(sizeof(lzma_microlzma_coder), allocator); 186 if (coder == NULL) 187 return LZMA_MEM_ERROR; 188 189 next->coder = coder; 190 next->code = µlzma_decode; 191 next->end = µlzma_decoder_end; 192 193 coder->lzma = LZMA_NEXT_CODER_INIT; 194 } 195 196 // The public API is uint64_t but the internal LZ decoder API uses 197 // lzma_vli. 198 if (uncomp_size > LZMA_VLI_MAX) 199 return LZMA_OPTIONS_ERROR; 200 201 coder->comp_size = comp_size; 202 coder->uncomp_size = uncomp_size; 203 coder->uncomp_size_is_exact = uncomp_size_is_exact; 204 coder->dict_size = dict_size; 205 206 coder->props_decoded = false; 207 208 return LZMA_OK; 209 } 210 211 212 extern LZMA_API(lzma_ret) 213 lzma_microlzma_decoder(lzma_stream *strm, uint64_t comp_size, 214 uint64_t uncomp_size, lzma_bool uncomp_size_is_exact, 215 uint32_t dict_size) 216 { 217 lzma_next_strm_init(microlzma_decoder_init, strm, comp_size, 218 uncomp_size, uncomp_size_is_exact, dict_size); 219 220 strm->internal->supported_actions[LZMA_RUN] = true; 221 strm->internal->supported_actions[LZMA_FINISH] = true; 222 223 return LZMA_OK; 224 } 225