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