1 ///////////////////////////////////////////////////////////////////////////////
3 /// \file stream_encoder_multi.c
4 /// \brief Encodes Multi-Block .lzma files
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 "stream_common.h"
21 #include "stream_encoder_multi.h"
22 #include "block_encoder.h"
23 #include "metadata_encoder.h"
28 SEQ_STREAM_HEADER_COPY,
29 SEQ_HEADER_METADATA_INIT,
30 SEQ_HEADER_METADATA_COPY,
31 SEQ_HEADER_METADATA_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,
42 /// Block or Metadata encoder
45 /// Options for the Block encoder
46 lzma_options_block block_options;
48 /// Information about the Stream
51 /// Information about the current Data Block
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;
60 /// Stream Header or Stream Footer in encoded form
68 BLOCK_HEADER_METADATA,
70 BLOCK_FOOTER_METADATA,
75 block_header_encode(lzma_coder *coder, lzma_allocator *allocator,
76 lzma_vli uncompressed_size, block_type type)
78 assert(coder->header == NULL);
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,
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;
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);
112 return_if_error(lzma_block_header_size(&coder->block_options));
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;
119 return_if_error(lzma_block_header_encode(
120 coder->header, &coder->block_options));
122 coder->header_pos = 0;
128 metadata_encoder_init(lzma_coder *coder, lzma_allocator *allocator,
129 lzma_metadata *metadata, block_type type)
131 return_if_error(lzma_info_metadata_set(coder->info, allocator,
132 metadata, type == BLOCK_HEADER_METADATA, false));
134 const lzma_vli metadata_size = lzma_metadata_size(metadata);
135 if (metadata_size == 0)
136 return LZMA_PROG_ERROR;
138 return_if_error(block_header_encode(
139 coder, allocator, metadata_size, type));
141 return lzma_metadata_encoder_init(&coder->next, allocator,
142 &coder->block_options, metadata);
147 data_encoder_init(lzma_coder *coder, lzma_allocator *allocator)
149 return_if_error(lzma_info_iter_next(&coder->iter, allocator));
151 return_if_error(block_header_encode(coder, allocator,
152 LZMA_VLI_VALUE_UNKNOWN, BLOCK_DATA));
154 return lzma_block_encoder_init(&coder->next, allocator,
155 &coder->block_options);
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)
166 while (*out_pos < out_size)
167 switch (coder->sequence) {
168 case SEQ_STREAM_HEADER_COPY:
169 case SEQ_HEADER_METADATA_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)
178 lzma_free(coder->header, allocator);
179 coder->header = NULL;
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
188 != LZMA_VLI_VALUE_UNKNOWN) {
189 coder->sequence = SEQ_HEADER_METADATA_INIT;
191 // Mark that Header Metadata Block doesn't
193 if (lzma_info_size_set(coder->info,
194 LZMA_INFO_HEADER_METADATA, 0)
196 return LZMA_PROG_ERROR;
198 coder->sequence = SEQ_DATA_INIT;
202 case SEQ_HEADER_METADATA_COPY:
204 case SEQ_FOOTER_METADATA_COPY:
208 case SEQ_STREAM_FOOTER_COPY:
209 return LZMA_STREAM_END;
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
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),
231 return_if_error(metadata_encoder_init(coder, allocator,
232 &metadata, BLOCK_HEADER_METADATA));
234 coder->sequence = SEQ_HEADER_METADATA_COPY;
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),
249 return_if_error(metadata_encoder_init(coder, allocator,
250 &metadata, BLOCK_FOOTER_METADATA));
252 coder->sequence = SEQ_FOOTER_METADATA_COPY;
256 case SEQ_HEADER_METADATA_CODE:
257 case SEQ_FOOTER_METADATA_CODE: {
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)
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));
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
282 if (action != LZMA_FINISH)
283 return action == LZMA_RUN
284 ? LZMA_OK : LZMA_STREAM_END;
286 if (lzma_info_index_count_get(coder->info) != 0) {
287 if (lzma_info_index_finish(coder->info))
288 return LZMA_DATA_ERROR;
290 coder->sequence = SEQ_FOOTER_METADATA_INIT;
295 return_if_error(data_encoder_init(coder, allocator));
297 coder->sequence = SEQ_DATA_COPY;
301 case SEQ_DATA_CODE: {
302 static const lzma_action convert[4] = {
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)
315 return_if_error(lzma_info_iter_set(&coder->iter,
316 coder->block_options.total_size,
317 coder->block_options.uncompressed_size));
319 coder->sequence = SEQ_DATA_INIT;
323 case SEQ_STREAM_FOOTER_INIT: {
324 assert(coder->header == NULL);
326 lzma_stream_flags flags = {
327 .check = coder->stream_options->check,
328 .has_crc32 = coder->stream_options->has_crc32,
332 coder->header = lzma_alloc(LZMA_STREAM_TAIL_SIZE, allocator);
333 if (coder->header == NULL)
334 return LZMA_MEM_ERROR;
336 return_if_error(lzma_stream_tail_encode(
337 coder->header, &flags));
339 coder->header_size = LZMA_STREAM_TAIL_SIZE;
340 coder->header_pos = 0;
342 coder->sequence = SEQ_STREAM_FOOTER_COPY;
347 return LZMA_PROG_ERROR;
355 stream_encoder_end(lzma_coder *coder, lzma_allocator *allocator)
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);
366 stream_encoder_init(lzma_next_coder *next,
367 lzma_allocator *allocator, const lzma_options_stream *options)
370 return LZMA_PROG_ERROR;
372 if (next->coder == NULL) {
373 next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
374 if (next->coder == NULL)
375 return LZMA_MEM_ERROR;
377 next->code = &stream_encode;
378 next->end = &stream_encoder_end;
380 next->coder->next = LZMA_NEXT_CODER_INIT;
381 next->coder->info = NULL;
383 lzma_free(next->coder->header, allocator);
386 next->coder->header = NULL;
388 next->coder->info = lzma_info_init(next->coder->info, allocator);
389 if (next->coder->info == NULL)
390 return LZMA_MEM_ERROR;
392 next->coder->sequence = SEQ_STREAM_HEADER_COPY;
393 next->coder->stream_options = options;
395 // Encode Stream Flags
397 lzma_stream_flags flags = {
398 .check = options->check,
399 .has_crc32 = options->has_crc32,
403 next->coder->header = lzma_alloc(LZMA_STREAM_HEADER_SIZE,
405 if (next->coder->header == NULL)
406 return LZMA_MEM_ERROR;
408 return_if_error(lzma_stream_header_encode(
409 next->coder->header, &flags));
411 next->coder->header_pos = 0;
412 next->coder->header_size = LZMA_STREAM_HEADER_SIZE;
415 if (lzma_info_size_set(next->coder->info, LZMA_INFO_STREAM_START,
416 options->alignment) != LZMA_OK)
417 return LZMA_PROG_ERROR;
419 lzma_info_iter_begin(next->coder->info, &next->coder->iter);
426 lzma_stream_encoder_multi_init(lzma_next_coder *next,
427 lzma_allocator *allocator, const lzma_options_stream *options)
429 lzma_next_coder_init(stream_encoder_init, next, allocator, options);
433 extern LZMA_API lzma_ret
434 lzma_stream_encoder_multi(
435 lzma_stream *strm, const lzma_options_stream *options)
437 lzma_next_strm_init(strm, stream_encoder_init, options);
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;