]> icculus.org git repositories - icculus/xz.git/blob - src/liblzma/common/stream_encoder_multi.c
Return LZMA_STREAM_END instead of LZMA_OK if
[icculus/xz.git] / src / liblzma / common / stream_encoder_multi.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       stream_encoder_multi.c
4 /// \brief      Encodes Multi-Block .lzma files
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 "stream_common.h"
21 #include "stream_encoder_multi.h"
22 #include "block_encoder.h"
23 #include "metadata_encoder.h"
24
25
26 struct lzma_coder_s {
27         enum {
28                 SEQ_STREAM_HEADER_COPY,
29                 SEQ_HEADER_METADATA_INIT,
30                 SEQ_HEADER_METADATA_COPY,
31                 SEQ_HEADER_METADATA_CODE,
32                 SEQ_DATA_INIT,
33                 SEQ_DATA_COPY,
34                 SEQ_DATA_CODE,
35                 SEQ_FOOTER_METADATA_INIT,
36                 SEQ_FOOTER_METADATA_COPY,
37                 SEQ_FOOTER_METADATA_CODE,
38                 SEQ_STREAM_FOOTER_INIT,
39                 SEQ_STREAM_FOOTER_COPY,
40         } sequence;
41
42         /// Block or Metadata encoder
43         lzma_next_coder next;
44
45         /// Options for the Block encoder
46         lzma_options_block block_options;
47
48         /// Information about the Stream
49         lzma_info *info;
50
51         /// Information about the current Data Block
52         lzma_info_iter iter;
53
54         /// Pointer to user-supplied options structure. We don't write to
55         /// it, only read instructions from the application, thus this is
56         /// const even though the user-supplied pointer from
57         /// lzma_options_filter structure isn't.
58         const lzma_options_stream *stream_options;
59
60         /// Stream Header or Stream Footer in encoded form
61         uint8_t *header;
62         size_t header_pos;
63         size_t header_size;
64 };
65
66
67 typedef enum {
68         BLOCK_HEADER_METADATA,
69         BLOCK_DATA,
70         BLOCK_FOOTER_METADATA,
71 } block_type;
72
73
74 static lzma_ret
75 block_header_encode(lzma_coder *coder, lzma_allocator *allocator,
76                 lzma_vli uncompressed_size, block_type type)
77 {
78         assert(coder->header == NULL);
79
80         coder->block_options = (lzma_options_block){
81                 .check = coder->stream_options->check,
82                 .has_crc32 = coder->stream_options->has_crc32,
83                 .has_eopm = uncompressed_size == LZMA_VLI_VALUE_UNKNOWN,
84                 .is_metadata = type != BLOCK_DATA,
85                 .has_uncompressed_size_in_footer = false,
86                 .has_backward_size = type == BLOCK_FOOTER_METADATA,
87                 .handle_padding = false,
88                 .total_size = LZMA_VLI_VALUE_UNKNOWN,
89                 .compressed_size = LZMA_VLI_VALUE_UNKNOWN,
90                 .uncompressed_size = uncompressed_size,
91                 .compressed_reserve = 0,
92                 .uncompressed_reserve = 0,
93                 .total_limit = LZMA_VLI_VALUE_UNKNOWN,
94                 .uncompressed_limit = LZMA_VLI_VALUE_UNKNOWN,
95                 .padding = LZMA_BLOCK_HEADER_PADDING_AUTO,
96         };
97
98         if (type == BLOCK_DATA) {
99                 memcpy(coder->block_options.filters,
100                                 coder->stream_options->filters,
101                                 sizeof(coder->stream_options->filters));
102                 coder->block_options.alignment = coder->iter.stream_offset;
103         } else {
104                 memcpy(coder->block_options.filters,
105                                 coder->stream_options->metadata_filters,
106                                 sizeof(coder->stream_options->filters));
107                 coder->block_options.alignment
108                                 = lzma_info_metadata_alignment_get(
109                                 coder->info, type == BLOCK_HEADER_METADATA);
110         }
111
112         return_if_error(lzma_block_header_size(&coder->block_options));
113
114         coder->header_size = coder->block_options.header_size;
115         coder->header = lzma_alloc(coder->header_size, allocator);
116         if (coder->header == NULL)
117                 return LZMA_MEM_ERROR;
118
119         return_if_error(lzma_block_header_encode(
120                         coder->header, &coder->block_options));
121
122         coder->header_pos = 0;
123         return LZMA_OK;
124 }
125
126
127 static lzma_ret
128 metadata_encoder_init(lzma_coder *coder, lzma_allocator *allocator,
129                 lzma_metadata *metadata, block_type type)
130 {
131         return_if_error(lzma_info_metadata_set(coder->info, allocator,
132                         metadata, type == BLOCK_HEADER_METADATA, false));
133
134         const lzma_vli metadata_size = lzma_metadata_size(metadata);
135         if (metadata_size == 0)
136                 return LZMA_PROG_ERROR;
137
138         return_if_error(block_header_encode(
139                         coder, allocator, metadata_size, type));
140
141         return lzma_metadata_encoder_init(&coder->next, allocator,
142                         &coder->block_options, metadata);
143 }
144
145
146 static lzma_ret
147 data_encoder_init(lzma_coder *coder, lzma_allocator *allocator)
148 {
149         return_if_error(lzma_info_iter_next(&coder->iter, allocator));
150
151         return_if_error(block_header_encode(coder, allocator,
152                         LZMA_VLI_VALUE_UNKNOWN, BLOCK_DATA));
153
154         return lzma_block_encoder_init(&coder->next, allocator,
155                         &coder->block_options);
156 }
157
158
159 static lzma_ret
160 stream_encode(lzma_coder *coder, lzma_allocator *allocator,
161                 const uint8_t *restrict in, size_t *restrict in_pos,
162                 size_t in_size, uint8_t *restrict out,
163                 size_t *restrict out_pos, size_t out_size, lzma_action action)
164 {
165         // Main loop
166         while (*out_pos < out_size)
167         switch (coder->sequence) {
168         case SEQ_STREAM_HEADER_COPY:
169         case SEQ_HEADER_METADATA_COPY:
170         case SEQ_DATA_COPY:
171         case SEQ_FOOTER_METADATA_COPY:
172         case SEQ_STREAM_FOOTER_COPY:
173                 bufcpy(coder->header, &coder->header_pos, coder->header_size,
174                                 out, out_pos, out_size);
175                 if (coder->header_pos < coder->header_size)
176                         return LZMA_OK;
177
178                 lzma_free(coder->header, allocator);
179                 coder->header = NULL;
180
181                 switch (coder->sequence) {
182                 case SEQ_STREAM_HEADER_COPY:
183                         // Write Header Metadata Block if we have Extra for it
184                         // or known Uncompressed Size.
185                         if (coder->stream_options->header != NULL
186                                         || coder->stream_options
187                                                 ->uncompressed_size
188                                                 != LZMA_VLI_VALUE_UNKNOWN) {
189                                 coder->sequence = SEQ_HEADER_METADATA_INIT;
190                         } else {
191                                 // Mark that Header Metadata Block doesn't
192                                 // exist.
193                                 if (lzma_info_size_set(coder->info,
194                                                 LZMA_INFO_HEADER_METADATA, 0)
195                                                 != LZMA_OK)
196                                         return LZMA_PROG_ERROR;
197
198                                 coder->sequence = SEQ_DATA_INIT;
199                         }
200                         break;
201
202                 case SEQ_HEADER_METADATA_COPY:
203                 case SEQ_DATA_COPY:
204                 case SEQ_FOOTER_METADATA_COPY:
205                         ++coder->sequence;
206                         break;
207
208                 case SEQ_STREAM_FOOTER_COPY:
209                         return LZMA_STREAM_END;
210
211                 default:
212                         assert(0);
213                 }
214
215                 break;
216
217         case SEQ_HEADER_METADATA_INIT: {
218                 lzma_metadata metadata = {
219                         .header_metadata_size = LZMA_VLI_VALUE_UNKNOWN,
220                         .total_size = LZMA_VLI_VALUE_UNKNOWN,
221                         .uncompressed_size = coder->stream_options
222                                         ->uncompressed_size,
223                         .index = NULL,
224                         // Metadata encoder doesn't modify this, but since
225                         // the lzma_extra structure is used also when decoding
226                         // Metadata, the pointer is not const, and we need
227                         // to cast the constness away in the encoder.
228                         .extra = (lzma_extra *)(coder->stream_options->header),
229                 };
230
231                 return_if_error(metadata_encoder_init(coder, allocator,
232                                 &metadata, BLOCK_HEADER_METADATA));
233
234                 coder->sequence = SEQ_HEADER_METADATA_COPY;
235                 break;
236         }
237
238         case SEQ_FOOTER_METADATA_INIT: {
239                 lzma_metadata metadata = {
240                         .header_metadata_size
241                                         = lzma_info_size_get(coder->info,
242                                                 LZMA_INFO_HEADER_METADATA),
243                         .total_size = LZMA_VLI_VALUE_UNKNOWN,
244                         .uncompressed_size = LZMA_VLI_VALUE_UNKNOWN,
245                         .index = lzma_info_index_get(coder->info, false),
246                         .extra = (lzma_extra *)(coder->stream_options->footer),
247                 };
248
249                 return_if_error(metadata_encoder_init(coder, allocator,
250                                 &metadata, BLOCK_FOOTER_METADATA));
251
252                 coder->sequence = SEQ_FOOTER_METADATA_COPY;
253                 break;
254         }
255
256         case SEQ_HEADER_METADATA_CODE:
257         case SEQ_FOOTER_METADATA_CODE: {
258                 size_t dummy = 0;
259                 const lzma_ret ret = coder->next.code(coder->next.coder,
260                                 allocator, NULL, &dummy, 0,
261                                 out, out_pos, out_size, LZMA_RUN);
262                 if (ret != LZMA_STREAM_END)
263                         return ret;
264
265                 return_if_error(lzma_info_size_set(coder->info,
266                                 coder->sequence == SEQ_HEADER_METADATA_CODE
267                                         ? LZMA_INFO_HEADER_METADATA
268                                         : LZMA_INFO_FOOTER_METADATA,
269                                 coder->block_options.total_size));
270
271                 ++coder->sequence;
272                 break;
273         }
274
275         case SEQ_DATA_INIT: {
276                 // Don't create an empty Block unless it would be
277                 // the only Data Block.
278                 if (*in_pos == in_size) {
279                         // If we are LZMA_SYNC_FLUSHing or LZMA_FULL_FLUSHing,
280                         // return LZMA_STREAM_END since there's nothing to
281                         // flush.
282                         if (action != LZMA_FINISH)
283                                 return action == LZMA_RUN
284                                         ? LZMA_OK : LZMA_STREAM_END;
285
286                         if (lzma_info_index_count_get(coder->info) != 0) {
287                                 if (lzma_info_index_finish(coder->info))
288                                         return LZMA_DATA_ERROR;
289
290                                 coder->sequence = SEQ_FOOTER_METADATA_INIT;
291                                 break;
292                         }
293                 }
294
295                 return_if_error(data_encoder_init(coder, allocator));
296
297                 coder->sequence = SEQ_DATA_COPY;
298                 break;
299         }
300
301         case SEQ_DATA_CODE: {
302                 static const lzma_action convert[4] = {
303                         LZMA_RUN,
304                         LZMA_SYNC_FLUSH,
305                         LZMA_FINISH,
306                         LZMA_FINISH,
307                 };
308
309                 const lzma_ret ret = coder->next.code(coder->next.coder,
310                                 allocator, in, in_pos, in_size,
311                                 out, out_pos, out_size, convert[action]);
312                 if (ret != LZMA_STREAM_END || action == LZMA_SYNC_FLUSH)
313                         return ret;
314
315                 return_if_error(lzma_info_iter_set(&coder->iter,
316                                 coder->block_options.total_size,
317                                 coder->block_options.uncompressed_size));
318
319                 coder->sequence = SEQ_DATA_INIT;
320                 break;
321         }
322
323         case SEQ_STREAM_FOOTER_INIT: {
324                 assert(coder->header == NULL);
325
326                 lzma_stream_flags flags = {
327                         .check = coder->stream_options->check,
328                         .has_crc32 = coder->stream_options->has_crc32,
329                         .is_multi = true,
330                 };
331
332                 coder->header = lzma_alloc(LZMA_STREAM_TAIL_SIZE, allocator);
333                 if (coder->header == NULL)
334                         return LZMA_MEM_ERROR;
335
336                 return_if_error(lzma_stream_tail_encode(
337                                 coder->header, &flags));
338
339                 coder->header_size = LZMA_STREAM_TAIL_SIZE;
340                 coder->header_pos = 0;
341
342                 coder->sequence = SEQ_STREAM_FOOTER_COPY;
343                 break;
344         }
345
346         default:
347                 return LZMA_PROG_ERROR;
348         }
349
350         return LZMA_OK;
351 }
352
353
354 static void
355 stream_encoder_end(lzma_coder *coder, lzma_allocator *allocator)
356 {
357         lzma_next_coder_end(&coder->next, allocator);
358         lzma_info_free(coder->info, allocator);
359         lzma_free(coder->header, allocator);
360         lzma_free(coder, allocator);
361         return;
362 }
363
364
365 static lzma_ret
366 stream_encoder_init(lzma_next_coder *next,
367                 lzma_allocator *allocator, const lzma_options_stream *options)
368 {
369         if (options == NULL)
370                 return LZMA_PROG_ERROR;
371
372         if (next->coder == NULL) {
373                 next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
374                 if (next->coder == NULL)
375                         return LZMA_MEM_ERROR;
376
377                 next->code = &stream_encode;
378                 next->end = &stream_encoder_end;
379
380                 next->coder->next = LZMA_NEXT_CODER_INIT;
381                 next->coder->info = NULL;
382         } else {
383                 lzma_free(next->coder->header, allocator);
384         }
385
386         next->coder->header = NULL;
387
388         next->coder->info = lzma_info_init(next->coder->info, allocator);
389         if (next->coder->info == NULL)
390                 return LZMA_MEM_ERROR;
391
392         next->coder->sequence = SEQ_STREAM_HEADER_COPY;
393         next->coder->stream_options = options;
394
395         // Encode Stream Flags
396         {
397                 lzma_stream_flags flags = {
398                         .check = options->check,
399                         .has_crc32 = options->has_crc32,
400                         .is_multi = true,
401                 };
402
403                 next->coder->header = lzma_alloc(LZMA_STREAM_HEADER_SIZE,
404                                 allocator);
405                 if (next->coder->header == NULL)
406                         return LZMA_MEM_ERROR;
407
408                 return_if_error(lzma_stream_header_encode(
409                                 next->coder->header, &flags));
410
411                 next->coder->header_pos = 0;
412                 next->coder->header_size = LZMA_STREAM_HEADER_SIZE;
413         }
414
415         if (lzma_info_size_set(next->coder->info, LZMA_INFO_STREAM_START,
416                         options->alignment) != LZMA_OK)
417                 return LZMA_PROG_ERROR;
418
419         lzma_info_iter_begin(next->coder->info, &next->coder->iter);
420
421         return LZMA_OK;
422 }
423
424
425 extern lzma_ret
426 lzma_stream_encoder_multi_init(lzma_next_coder *next,
427                 lzma_allocator *allocator, const lzma_options_stream *options)
428 {
429         lzma_next_coder_init(stream_encoder_init, next, allocator, options);
430 }
431
432
433 extern LZMA_API lzma_ret
434 lzma_stream_encoder_multi(
435                 lzma_stream *strm, const lzma_options_stream *options)
436 {
437         lzma_next_strm_init(strm, stream_encoder_init, options);
438
439         strm->internal->supported_actions[LZMA_RUN] = true;
440         strm->internal->supported_actions[LZMA_SYNC_FLUSH] = true;
441         strm->internal->supported_actions[LZMA_FULL_FLUSH] = true;
442         strm->internal->supported_actions[LZMA_FINISH] = true;
443
444         return LZMA_OK;
445 }