]> icculus.org git repositories - icculus/xz.git/blob - src/liblzma/common/block_encoder.c
Small LZMA_SYNC_FLUSH fixes to Block and Single-Stream encoders.
[icculus/xz.git] / src / liblzma / common / block_encoder.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       block_encoder.c
4 /// \brief      Encodes .lzma Blocks
5 //
6 //  Copyright (C) 2007 Lasse Collin
7 //
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.
12 //
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.
17 //
18 ///////////////////////////////////////////////////////////////////////////////
19
20 #include "block_encoder.h"
21 #include "block_private.h"
22 #include "raw_encoder.h"
23 #include "check.h"
24
25
26 struct lzma_coder_s {
27         /// The filters in the chain; initialized with lzma_raw_decoder_init().
28         lzma_next_coder next;
29
30         /// Encoding options; we also write Total Size, Compressed Size, and
31         /// Uncompressed Size back to this structure when the encoding has
32         /// been finished.
33         lzma_options_block *options;
34
35         enum {
36                 SEQ_CODE,
37                 SEQ_CHECK_FINISH,
38                 SEQ_CHECK_COPY,
39                 SEQ_UNCOMPRESSED_SIZE,
40                 SEQ_BACKWARD_SIZE,
41                 SEQ_PADDING,
42         } sequence;
43
44         /// Position in .header and .check.
45         size_t pos;
46
47         /// Check of the uncompressed data
48         lzma_check check;
49
50         /// Total Size calculated while encoding
51         lzma_vli total_size;
52
53         /// Compressed Size calculated while encoding
54         lzma_vli compressed_size;
55
56         /// Uncompressed Size calculated while encoding
57         lzma_vli uncompressed_size;
58
59         /// Maximum allowed total_size
60         lzma_vli total_limit;
61
62         /// Maximum allowed uncompressed_size
63         lzma_vli uncompressed_limit;
64
65         /// Backward Size - This is a copy of total_size right before
66         /// the Backward Size field.
67         lzma_vli backward_size;
68 };
69
70
71 static lzma_ret
72 block_encode(lzma_coder *coder, lzma_allocator *allocator,
73                 const uint8_t *restrict in, size_t *restrict in_pos,
74                 size_t in_size, uint8_t *restrict out,
75                 size_t *restrict out_pos, size_t out_size, lzma_action action)
76 {
77         // Check that our amount of input stays in proper limits.
78         if (coder->options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN) {
79                 if (action == LZMA_FINISH) {
80                         if (coder->options->uncompressed_size
81                                         - coder->uncompressed_size
82                                         != (lzma_vli)(in_size - *in_pos))
83                                 return LZMA_DATA_ERROR;
84                 } else {
85                         if (coder->options->uncompressed_size
86                                         - coder->uncompressed_size
87                                         <  (lzma_vli)(in_size - *in_pos))
88                                 return LZMA_DATA_ERROR;
89                 }
90         } else if (LZMA_VLI_VALUE_MAX - coder->uncompressed_size
91                         < (lzma_vli)(in_size - *in_pos)) {
92                 return LZMA_DATA_ERROR;
93         }
94
95         // Main loop
96         while (*out_pos < out_size
97                         && (*in_pos < in_size || action != LZMA_RUN))
98         switch (coder->sequence) {
99         case SEQ_CODE: {
100                 const size_t in_start = *in_pos;
101                 const size_t out_start = *out_pos;
102
103                 const lzma_ret ret = coder->next.code(coder->next.coder,
104                                 allocator, in, in_pos, in_size,
105                                 out, out_pos, out_size, action);
106
107                 const size_t in_used = *in_pos - in_start;
108                 const size_t out_used = *out_pos - out_start;
109
110                 if (update_size(&coder->total_size, out_used,
111                                         coder->total_limit)
112                                 || update_size(&coder->compressed_size,
113                                         out_used,
114                                         coder->options->compressed_size))
115                         return LZMA_DATA_ERROR;
116
117                 // No need to check for overflow because we have already
118                 // checked it at the beginning of this function.
119                 coder->uncompressed_size += in_used;
120
121                 lzma_check_update(&coder->check, coder->options->check,
122                                 in + in_start, in_used);
123
124                 if (ret != LZMA_STREAM_END || action == LZMA_SYNC_FLUSH)
125                         return ret;
126
127                 assert(*in_pos == in_size);
128
129                 // Compressed and Uncompressed Sizes are now at their final
130                 // values. Verify that they match the values give to us.
131                 if (!is_size_valid(coder->compressed_size,
132                                         coder->options->compressed_size)
133                                 || !is_size_valid(coder->uncompressed_size,
134                                         coder->options->uncompressed_size))
135                         return LZMA_DATA_ERROR;
136
137                 coder->sequence = SEQ_CHECK_FINISH;
138                 break;
139         }
140
141         case SEQ_CHECK_FINISH:
142                 if (coder->options->check == LZMA_CHECK_NONE) {
143                         coder->sequence = SEQ_UNCOMPRESSED_SIZE;
144                         break;
145                 }
146
147                 lzma_check_finish(&coder->check, coder->options->check);
148                 coder->sequence = SEQ_CHECK_COPY;
149
150         // Fall through
151
152         case SEQ_CHECK_COPY:
153                 assert(lzma_check_sizes[coder->options->check] > 0);
154
155                 switch (coder->options->check) {
156                 case LZMA_CHECK_CRC32:
157                         out[*out_pos] = coder->check.crc32 >> (coder->pos * 8);
158                         break;
159
160                 case LZMA_CHECK_CRC64:
161                         out[*out_pos] = coder->check.crc64 >> (coder->pos * 8);
162                         break;
163
164                 case LZMA_CHECK_SHA256:
165                         out[*out_pos] = coder->check.sha256.buffer[coder->pos];
166                         break;
167
168                 default:
169                         assert(0);
170                         return LZMA_PROG_ERROR;
171                 }
172
173                 ++*out_pos;
174
175                 if (update_size(&coder->total_size, 1, coder->total_limit))
176                         return LZMA_DATA_ERROR;
177
178                 if (++coder->pos == lzma_check_sizes[coder->options->check]) {
179                         coder->pos = 0;
180                         coder->sequence = SEQ_UNCOMPRESSED_SIZE;
181                 }
182
183                 break;
184
185         case SEQ_UNCOMPRESSED_SIZE:
186                 if (coder->options->has_uncompressed_size_in_footer) {
187                         const size_t out_start = *out_pos;
188
189                         const lzma_ret ret = lzma_vli_encode(
190                                         coder->uncompressed_size,
191                                         &coder->pos, 1,
192                                         out, out_pos, out_size);
193
194                         // Updating the size this way instead of doing in a
195                         // single chunk using lzma_vli_size(), because this
196                         // way we detect when exactly we are going out of
197                         // our limits.
198                         if (update_size(&coder->total_size,
199                                         *out_pos - out_start,
200                                         coder->total_limit))
201                                 return LZMA_DATA_ERROR;
202
203                         if (ret != LZMA_STREAM_END)
204                                 return ret;
205
206                         coder->pos = 0;
207                 }
208
209                 coder->backward_size = coder->total_size;
210                 coder->sequence = SEQ_BACKWARD_SIZE;
211                 break;
212
213         case SEQ_BACKWARD_SIZE:
214                 if (coder->options->has_backward_size) {
215                         const size_t out_start = *out_pos;
216
217                         const lzma_ret ret = lzma_vli_encode(
218                                         coder->backward_size, &coder->pos, 1,
219                                         out, out_pos, out_size);
220
221                         if (update_size(&coder->total_size,
222                                         *out_pos - out_start,
223                                         coder->total_limit))
224                                 return LZMA_DATA_ERROR;
225
226                         if (ret != LZMA_STREAM_END)
227                                 return ret;
228                 }
229
230                 coder->sequence = SEQ_PADDING;
231                 break;
232
233         case SEQ_PADDING:
234                 if (coder->options->handle_padding) {
235                         assert(!coder->options
236                                         ->has_uncompressed_size_in_footer);
237                         assert(!coder->options->has_backward_size);
238                         assert(coder->options->total_size != LZMA_VLI_VALUE_UNKNOWN);
239
240                         if (coder->total_size < coder->options->total_size) {
241                                 out[*out_pos] = 0x00;
242                                 ++*out_pos;
243
244                                 if (update_size(&coder->total_size, 1,
245                                                 coder->total_limit))
246                                         return LZMA_DATA_ERROR;
247
248                                 break;
249                         }
250                 }
251
252                 // Now also Total Size is known. Verify it.
253                 if (!is_size_valid(coder->total_size,
254                                         coder->options->total_size))
255                         return LZMA_DATA_ERROR;
256
257                 // Copy the values into coder->options. The caller
258                 // may use this information to construct Index.
259                 coder->options->total_size = coder->total_size;
260                 coder->options->compressed_size = coder->compressed_size;
261                 coder->options->uncompressed_size = coder->uncompressed_size;
262
263                 return LZMA_STREAM_END;
264
265         default:
266                 return LZMA_PROG_ERROR;
267         }
268
269         return LZMA_OK;
270 }
271
272
273 static void
274 block_encoder_end(lzma_coder *coder, lzma_allocator *allocator)
275 {
276         lzma_next_coder_end(&coder->next, allocator);
277         lzma_free(coder, allocator);
278         return;
279 }
280
281
282 static lzma_ret
283 block_encoder_init(lzma_next_coder *next, lzma_allocator *allocator,
284                 lzma_options_block *options)
285 {
286         // Validate some options.
287         if (options == NULL
288                         || !lzma_vli_is_valid(options->total_size)
289                         || !lzma_vli_is_valid(options->compressed_size)
290                         || !lzma_vli_is_valid(options->uncompressed_size)
291                         || !lzma_vli_is_valid(options->total_size)
292                         || !lzma_vli_is_valid(options->total_limit)
293                         || !lzma_vli_is_valid(options->uncompressed_limit)
294                         || (options->uncompressed_size
295                                         != LZMA_VLI_VALUE_UNKNOWN
296                                 && options->uncompressed_size
297                                         > options->uncompressed_limit)
298                         || (options->total_size != LZMA_VLI_VALUE_UNKNOWN
299                                 && options->total_size
300                                         > options->total_limit)
301                         || (!options->has_eopm && options->uncompressed_size
302                                 == LZMA_VLI_VALUE_UNKNOWN)
303                         || (options->handle_padding && (options->total_size
304                                         == LZMA_VLI_VALUE_UNKNOWN
305                                 || options->has_uncompressed_size_in_footer
306                                 || options->has_backward_size))
307                         || options->header_size > options->total_size)
308                 return LZMA_PROG_ERROR;
309
310         // Allocate and initialize *next->coder if needed.
311         if (next->coder == NULL) {
312                 next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
313                 if (next->coder == NULL)
314                         return LZMA_MEM_ERROR;
315
316                 next->code = &block_encode;
317                 next->end = &block_encoder_end;
318                 next->coder->next = LZMA_NEXT_CODER_INIT;
319         }
320
321         // Initialize the check.
322         return_if_error(lzma_check_init(&next->coder->check, options->check));
323
324         // If End of Payload Marker is not used and Uncompressed Size is zero,
325         // Compressed Data is empty. That is, we don't call the encoder at all.
326         // We initialize it though; it allows detecting invalid options.
327         if (!options->has_eopm && options->uncompressed_size == 0) {
328                 // Also Compressed Size must also be zero if it has been
329                 // given to us.
330                 if (!is_size_valid(0, options->compressed_size))
331                         return LZMA_PROG_ERROR;
332
333                 next->coder->sequence = SEQ_CHECK_FINISH;
334         } else {
335                 next->coder->sequence = SEQ_CODE;
336         }
337
338         // Other initializations
339         next->coder->options = options;
340         next->coder->pos = 0;
341         next->coder->total_size = options->header_size;
342         next->coder->compressed_size = 0;
343         next->coder->uncompressed_size = 0;
344         next->coder->total_limit
345                         = MIN(options->total_size, options->total_limit);
346         next->coder->uncompressed_limit = MIN(options->uncompressed_size,
347                         options->uncompressed_limit);
348
349         // Initialize the requested filters.
350         return lzma_raw_encoder_init(&next->coder->next, allocator,
351                         options->filters, options->has_eopm
352                                 ? LZMA_VLI_VALUE_UNKNOWN
353                                 : options->uncompressed_size,
354                         true);
355 }
356
357
358 extern lzma_ret
359 lzma_block_encoder_init(lzma_next_coder *next, lzma_allocator *allocator,
360                 lzma_options_block *options)
361 {
362         lzma_next_coder_init(block_encoder_init, next, allocator, options);
363 }
364
365
366 extern LZMA_API lzma_ret
367 lzma_block_encoder(lzma_stream *strm, lzma_options_block *options)
368 {
369         lzma_next_strm_init(strm, block_encoder_init, options);
370
371         strm->internal->supported_actions[LZMA_RUN] = true;
372         strm->internal->supported_actions[LZMA_FINISH] = true;
373
374         return LZMA_OK;
375 }