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 "block_encoder.h"
22 #include "metadata_encoder.h"
27 SEQ_STREAM_HEADER_COPY,
28 SEQ_HEADER_METADATA_INIT,
29 SEQ_HEADER_METADATA_COPY,
30 SEQ_HEADER_METADATA_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,
41 /// Block or Metadata encoder
44 /// Options for the Block encoder
45 lzma_options_block block_options;
47 /// Information about the Stream
50 /// Information about the current Data Block
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;
59 /// Stream Header or Stream Footer in encoded form
67 BLOCK_HEADER_METADATA,
69 BLOCK_FOOTER_METADATA,
74 block_header_encode(lzma_coder *coder, lzma_allocator *allocator,
75 lzma_vli uncompressed_size, block_type type)
77 assert(coder->header == NULL);
79 coder->block_options = (lzma_options_block){
80 .check = coder->stream_options->check,
81 .has_crc32 = coder->stream_options->has_crc32,
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,
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;
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);
111 lzma_ret ret = lzma_block_header_size(&coder->block_options);
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;
120 ret = lzma_block_header_encode(coder->header, &coder->block_options);
124 coder->header_pos = 0;
130 metadata_encoder_init(lzma_coder *coder, lzma_allocator *allocator,
131 lzma_metadata *metadata, block_type type)
133 lzma_ret ret = lzma_info_metadata_set(coder->info, allocator,
134 metadata, type == BLOCK_HEADER_METADATA, false);
138 const lzma_vli metadata_size = lzma_metadata_size(metadata);
139 if (metadata_size == 0)
140 return LZMA_PROG_ERROR;
142 ret = block_header_encode(coder, allocator, metadata_size, type);
146 return lzma_metadata_encoder_init(&coder->next, allocator,
147 &coder->block_options, metadata);
152 data_encoder_init(lzma_coder *coder, lzma_allocator *allocator)
154 lzma_ret ret = lzma_info_iter_next(&coder->iter, allocator);
158 ret = block_header_encode(coder, allocator,
159 LZMA_VLI_VALUE_UNKNOWN, BLOCK_DATA);
163 return lzma_block_encoder_init(&coder->next, allocator,
164 &coder->block_options);
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)
175 while (*out_pos < out_size)
176 switch (coder->sequence) {
177 case SEQ_STREAM_HEADER_COPY:
178 case SEQ_HEADER_METADATA_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)
187 lzma_free(coder->header, allocator);
188 coder->header = NULL;
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
197 != LZMA_VLI_VALUE_UNKNOWN) {
198 coder->sequence = SEQ_HEADER_METADATA_INIT;
200 // Mark that Header Metadata Block doesn't
202 if (lzma_info_size_set(coder->info,
203 LZMA_INFO_HEADER_METADATA, 0)
205 return LZMA_PROG_ERROR;
207 coder->sequence = SEQ_DATA_INIT;
211 case SEQ_HEADER_METADATA_COPY:
213 case SEQ_FOOTER_METADATA_COPY:
217 case SEQ_STREAM_FOOTER_COPY:
218 return LZMA_STREAM_END;
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
233 .extra = coder->stream_options->header,
236 const lzma_ret ret = metadata_encoder_init(coder, allocator,
237 &metadata, BLOCK_HEADER_METADATA);
241 coder->sequence = SEQ_HEADER_METADATA_COPY;
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,
256 const lzma_ret ret = metadata_encoder_init(coder, allocator,
257 &metadata, BLOCK_FOOTER_METADATA);
261 coder->sequence = SEQ_FOOTER_METADATA_COPY;
265 case SEQ_HEADER_METADATA_CODE:
266 case SEQ_FOOTER_METADATA_CODE: {
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)
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);
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)
293 if (lzma_info_index_count_get(coder->info) != 0) {
294 if (lzma_info_index_finish(coder->info))
295 return LZMA_DATA_ERROR;
297 coder->sequence = SEQ_FOOTER_METADATA_INIT;
302 const lzma_ret ret = data_encoder_init(coder, allocator);
306 coder->sequence = SEQ_DATA_COPY;
310 case SEQ_DATA_CODE: {
311 static const lzma_action convert[4] = {
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)
324 ret = lzma_info_iter_set(&coder->iter,
325 coder->block_options.total_size,
326 coder->block_options.uncompressed_size);
330 coder->sequence = SEQ_DATA_INIT;
334 case SEQ_STREAM_FOOTER_INIT: {
335 assert(coder->header == NULL);
337 lzma_stream_flags flags = {
338 .check = coder->stream_options->check,
339 .has_crc32 = coder->stream_options->has_crc32,
343 coder->header = lzma_alloc(LZMA_STREAM_TAIL_SIZE, allocator);
344 if (coder->header == NULL)
345 return LZMA_MEM_ERROR;
347 const lzma_ret ret = lzma_stream_tail_encode(
348 coder->header, &flags);
352 coder->header_size = LZMA_STREAM_TAIL_SIZE;
353 coder->header_pos = 0;
355 coder->sequence = SEQ_STREAM_FOOTER_COPY;
360 return LZMA_PROG_ERROR;
368 stream_encoder_end(lzma_coder *coder, lzma_allocator *allocator)
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);
379 stream_encoder_init(lzma_next_coder *next,
380 lzma_allocator *allocator, const lzma_options_stream *options)
383 return LZMA_PROG_ERROR;
385 if (next->coder == NULL) {
386 next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
387 if (next->coder == NULL)
388 return LZMA_MEM_ERROR;
390 next->code = &stream_encode;
391 next->end = &stream_encoder_end;
393 next->coder->next = LZMA_NEXT_CODER_INIT;
394 next->coder->info = NULL;
396 lzma_free(next->coder->header, allocator);
399 next->coder->header = NULL;
401 next->coder->info = lzma_info_init(next->coder->info, allocator);
402 if (next->coder->info == NULL)
403 return LZMA_MEM_ERROR;
405 next->coder->sequence = SEQ_STREAM_HEADER_COPY;
406 next->coder->stream_options = options;
408 // Encode Stream Flags
410 lzma_stream_flags flags = {
411 .check = options->check,
412 .has_crc32 = options->has_crc32,
416 next->coder->header = lzma_alloc(LZMA_STREAM_HEADER_SIZE,
418 if (next->coder->header == NULL)
419 return LZMA_MEM_ERROR;
421 return_if_error(lzma_stream_header_encode(
422 next->coder->header, &flags));
424 next->coder->header_pos = 0;
425 next->coder->header_size = LZMA_STREAM_HEADER_SIZE;
428 if (lzma_info_size_set(next->coder->info, LZMA_INFO_STREAM_START,
429 options->alignment) != LZMA_OK)
430 return LZMA_PROG_ERROR;
432 lzma_info_iter_begin(next->coder->info, &next->coder->iter);
440 lzma_stream_encoder_multi_init(lzma_next_coder *next,
441 lzma_allocator *allocator, const lzma_options_stream *options)
443 lzma_next_coder_init(stream_encoder_init, next, allocator, options);
448 extern LZMA_API lzma_ret
449 lzma_stream_encoder_multi(
450 lzma_stream *strm, const lzma_options_stream *options)
452 lzma_next_strm_init(strm, stream_encoder_init, options);
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;