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