1 ///////////////////////////////////////////////////////////////////////////////
3 /// \file block_decoder.c
4 /// \brief Decodes .lzma Blocks
6 // Copyright (C) 2007 Lasse Collin
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
18 ///////////////////////////////////////////////////////////////////////////////
20 #include "block_decoder.h"
21 #include "block_private.h"
22 #include "raw_decoder.h"
30 SEQ_UNCOMPRESSED_SIZE,
36 /// The filters in the chain; initialized with lzma_raw_decoder_init().
39 /// Decoding options; we also write Total Size, Compressed Size, and
40 /// Uncompressed Size back to this structure when the encoding has
42 lzma_options_block *options;
44 /// Position in variable-length integers (and in some other places).
47 /// Check of the uncompressed data
50 /// Total Size calculated while encoding
53 /// Compressed Size calculated while encoding
54 lzma_vli compressed_size;
56 /// Uncompressed Size calculated while encoding
57 lzma_vli uncompressed_size;
59 /// Maximum allowed total_size
62 /// Maximum allowed uncompressed_size
63 lzma_vli uncompressed_limit;
65 /// Temporary location for the Uncompressed Size and Backward Size
66 /// fields in Block Footer.
69 /// Size of the Backward Size field - This is needed so that we
70 /// can verify the Backward Size and still keep updating total_size.
71 size_t size_of_backward_size;
76 update_sequence(lzma_coder *coder)
78 switch (coder->sequence) {
80 if (coder->options->check != LZMA_CHECK_NONE) {
81 lzma_check_finish(&coder->check,
82 coder->options->check);
83 coder->sequence = SEQ_CHECK;
90 if (coder->options->has_uncompressed_size_in_footer) {
91 coder->sequence = SEQ_UNCOMPRESSED_SIZE;
97 case SEQ_UNCOMPRESSED_SIZE:
98 if (coder->options->has_backward_size) {
99 coder->sequence = SEQ_BACKWARD_SIZE;
105 case SEQ_BACKWARD_SIZE:
106 if (coder->options->handle_padding) {
107 coder->sequence = SEQ_PADDING;
112 if (!is_size_valid(coder->total_size,
113 coder->options->total_size)
114 || !is_size_valid(coder->compressed_size,
115 coder->options->compressed_size)
116 || !is_size_valid(coder->uncompressed_size,
117 coder->options->uncompressed_size))
118 return LZMA_DATA_ERROR;
120 // Copy the values into coder->options. The caller
121 // may use this information to construct Index.
122 coder->options->total_size = coder->total_size;
123 coder->options->compressed_size = coder->compressed_size;
124 coder->options->uncompressed_size = coder->uncompressed_size;
126 return LZMA_STREAM_END;
130 return LZMA_PROG_ERROR;
138 block_decode(lzma_coder *coder, lzma_allocator *allocator,
139 const uint8_t *restrict in, size_t *restrict in_pos,
140 size_t in_size, uint8_t *restrict out,
141 size_t *restrict out_pos, size_t out_size, lzma_action action)
143 // Special case when the Block has only Block Header.
144 if (coder->sequence == SEQ_END)
145 return LZMA_STREAM_END;
147 // FIXME: Termination condition should work but could be cleaner.
148 while (*out_pos < out_size && (*in_pos < in_size
149 || coder->sequence == SEQ_CODE))
150 switch (coder->sequence) {
152 const size_t in_start = *in_pos;
153 const size_t out_start = *out_pos;
155 const lzma_ret ret = coder->next.code(coder->next.coder,
156 allocator, in, in_pos, in_size,
157 out, out_pos, out_size, action);
159 const size_t in_used = *in_pos - in_start;
160 const size_t out_used = *out_pos - out_start;
162 if (update_size(&coder->total_size, in_used,
164 || update_size(&coder->compressed_size,
166 coder->options->compressed_size)
167 || update_size(&coder->uncompressed_size,
168 out_used, coder->uncompressed_limit))
169 return LZMA_DATA_ERROR;
171 lzma_check_update(&coder->check, coder->options->check,
172 out + out_start, out_used);
174 if (ret != LZMA_STREAM_END)
177 return_if_error(update_sequence(coder));
183 switch (coder->options->check) {
184 case LZMA_CHECK_CRC32:
185 if (((coder->check.crc32 >> (coder->pos * 8))
186 & 0xFF) != in[*in_pos])
187 return LZMA_DATA_ERROR;
190 case LZMA_CHECK_CRC64:
191 if (((coder->check.crc64 >> (coder->pos * 8))
192 & 0xFF) != in[*in_pos])
193 return LZMA_DATA_ERROR;
196 case LZMA_CHECK_SHA256:
197 if (coder->check.sha256.buffer[coder->pos]
199 return LZMA_DATA_ERROR;
203 assert(coder->options->check != LZMA_CHECK_NONE);
204 assert(coder->options->check <= LZMA_CHECK_ID_MAX);
208 if (update_size(&coder->total_size, 1, coder->total_limit))
209 return LZMA_DATA_ERROR;
213 if (++coder->pos == lzma_check_sizes[coder->options->check]) {
214 return_if_error(update_sequence(coder));
220 case SEQ_UNCOMPRESSED_SIZE: {
221 const size_t in_start = *in_pos;
223 const lzma_ret ret = lzma_vli_decode(&coder->tmp,
224 &coder->pos, in, in_pos, in_size);
226 if (update_size(&coder->total_size, *in_pos - in_start,
228 return LZMA_DATA_ERROR;
230 if (ret != LZMA_STREAM_END)
233 if (coder->tmp != coder->uncompressed_size)
234 return LZMA_DATA_ERROR;
239 return_if_error(update_sequence(coder));
244 case SEQ_BACKWARD_SIZE: {
245 const size_t in_start = *in_pos;
247 const lzma_ret ret = lzma_vli_decode(&coder->tmp,
248 &coder->pos, in, in_pos, in_size);
250 const size_t in_used = *in_pos - in_start;
252 if (update_size(&coder->total_size, in_used,
254 return LZMA_DATA_ERROR;
256 coder->size_of_backward_size += in_used;
258 if (ret != LZMA_STREAM_END)
261 if (coder->tmp != coder->total_size
262 - coder->size_of_backward_size)
263 return LZMA_DATA_ERROR;
265 return_if_error(update_sequence(coder));
271 if (in[*in_pos] == 0x00) {
272 if (update_size(&coder->total_size, 1,
274 return LZMA_DATA_ERROR;
280 return update_sequence(coder);
283 return LZMA_PROG_ERROR;
291 block_decoder_end(lzma_coder *coder, lzma_allocator *allocator)
293 lzma_next_coder_end(&coder->next, allocator);
294 lzma_free(coder, allocator);
300 block_decoder_init(lzma_next_coder *next, lzma_allocator *allocator,
301 lzma_options_block *options)
303 // This is pretty similar to lzma_block_encoder_init().
304 // See comments there.
306 if (next->coder == NULL) {
307 next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
308 if (next->coder == NULL)
309 return LZMA_MEM_ERROR;
311 next->code = &block_decode;
312 next->end = &block_decoder_end;
313 next->coder->next = LZMA_NEXT_CODER_INIT;
316 if (validate_options_1(options))
317 return LZMA_PROG_ERROR;
319 if (validate_options_2(options))
320 return LZMA_DATA_ERROR;
322 return_if_error(lzma_check_init(&next->coder->check, options->check));
324 next->coder->sequence = SEQ_CODE;
325 next->coder->options = options;
326 next->coder->pos = 0;
327 next->coder->total_size = options->header_size;
328 next->coder->compressed_size = 0;
329 next->coder->uncompressed_size = 0;
330 next->coder->total_limit
331 = MIN(options->total_size, options->total_limit);
332 next->coder->uncompressed_limit = MIN(options->uncompressed_size,
333 options->uncompressed_limit);
334 next->coder->tmp = 0;
335 next->coder->size_of_backward_size = 0;
337 if (!options->has_eopm && options->uncompressed_size == 0) {
338 // The Compressed Data field is empty, thus we skip SEQ_CODE
340 const lzma_ret ret = update_sequence(next->coder);
341 if (ret != LZMA_OK && ret != LZMA_STREAM_END)
342 return LZMA_PROG_ERROR;
345 return lzma_raw_decoder_init(&next->coder->next, allocator,
346 options->filters, options->has_eopm
347 ? LZMA_VLI_VALUE_UNKNOWN
348 : options->uncompressed_size,
354 lzma_block_decoder_init(lzma_next_coder *next, lzma_allocator *allocator,
355 lzma_options_block *options)
357 lzma_next_coder_init(block_decoder_init, next, allocator, options);
361 extern LZMA_API lzma_ret
362 lzma_block_decoder(lzma_stream *strm, lzma_options_block *options)
364 lzma_next_strm_init(strm, block_decoder_init, options);
366 strm->internal->supported_actions[LZMA_RUN] = true;
367 strm->internal->supported_actions[LZMA_SYNC_FLUSH] = true;