aboutsummaryrefslogtreecommitdiff
path: root/src/liblzma/common/stream_encoder_multi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/liblzma/common/stream_encoder_multi.c')
-rw-r--r--src/liblzma/common/stream_encoder_multi.c460
1 files changed, 460 insertions, 0 deletions
diff --git a/src/liblzma/common/stream_encoder_multi.c b/src/liblzma/common/stream_encoder_multi.c
new file mode 100644
index 00000000..5955f858
--- /dev/null
+++ b/src/liblzma/common/stream_encoder_multi.c
@@ -0,0 +1,460 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file stream_encoder_multi.c
+/// \brief Encodes Multi-Block .lzma files
+//
+// Copyright (C) 2007 Lasse Collin
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "stream_common.h"
+#include "block_encoder.h"
+#include "metadata_encoder.h"
+
+
+struct lzma_coder_s {
+ enum {
+ SEQ_STREAM_HEADER_COPY,
+ SEQ_HEADER_METADATA_INIT,
+ SEQ_HEADER_METADATA_COPY,
+ SEQ_HEADER_METADATA_CODE,
+ SEQ_DATA_INIT,
+ SEQ_DATA_COPY,
+ SEQ_DATA_CODE,
+ SEQ_FOOTER_METADATA_INIT,
+ SEQ_FOOTER_METADATA_COPY,
+ SEQ_FOOTER_METADATA_CODE,
+ SEQ_STREAM_FOOTER_INIT,
+ SEQ_STREAM_FOOTER_COPY,
+ } sequence;
+
+ /// Block or Metadata encoder
+ lzma_next_coder next;
+
+ /// Options for the Block encoder
+ lzma_options_block block_options;
+
+ /// Information about the Stream
+ lzma_info *info;
+
+ /// Information about the current Data Block
+ lzma_info_iter iter;
+
+ /// Pointer to user-supplied options structure. We don't write to
+ /// it, only read instructions from the application, thus this is
+ /// const even though the user-supplied pointer from
+ /// lzma_options_filter structure isn't.
+ const lzma_options_stream *stream_options;
+
+ /// Stream Header or Stream Footer in encoded form
+ uint8_t *header;
+ size_t header_pos;
+ size_t header_size;
+};
+
+
+typedef enum {
+ BLOCK_HEADER_METADATA,
+ BLOCK_DATA,
+ BLOCK_FOOTER_METADATA,
+} block_type;
+
+
+static lzma_ret
+block_header_encode(lzma_coder *coder, lzma_allocator *allocator,
+ lzma_vli uncompressed_size, block_type type)
+{
+ assert(coder->header == NULL);
+
+ coder->block_options = (lzma_options_block){
+ .check = coder->stream_options->check,
+ .has_crc32 = coder->stream_options->has_crc32,
+ .has_eopm = true,
+ .is_metadata = type != BLOCK_DATA,
+ .has_uncompressed_size_in_footer = false,
+ .has_backward_size = type == BLOCK_FOOTER_METADATA,
+ .handle_padding = false,
+ .total_size = LZMA_VLI_VALUE_UNKNOWN,
+ .compressed_size = LZMA_VLI_VALUE_UNKNOWN,
+ .uncompressed_size = uncompressed_size,
+ .compressed_reserve = 0,
+ .uncompressed_reserve = 0,
+ .total_limit = LZMA_VLI_VALUE_UNKNOWN,
+ .uncompressed_limit = LZMA_VLI_VALUE_UNKNOWN,
+ .padding = LZMA_BLOCK_HEADER_PADDING_AUTO,
+ };
+
+ if (type == BLOCK_DATA) {
+ memcpy(coder->block_options.filters,
+ coder->stream_options->filters,
+ sizeof(coder->stream_options->filters));
+ coder->block_options.alignment = coder->iter.stream_offset;
+ } else {
+ memcpy(coder->block_options.filters,
+ coder->stream_options->metadata_filters,
+ sizeof(coder->stream_options->filters));
+ coder->block_options.alignment
+ = lzma_info_metadata_alignment_get(
+ coder->info, type == BLOCK_HEADER_METADATA);
+ }
+
+ lzma_ret ret = lzma_block_header_size(&coder->block_options);
+ if (ret != LZMA_OK)
+ return ret;
+
+ coder->header_size = coder->block_options.header_size;
+ coder->header = lzma_alloc(coder->header_size, allocator);
+ if (coder->header == NULL)
+ return LZMA_MEM_ERROR;
+
+ ret = lzma_block_header_encode(coder->header, &coder->block_options);
+ if (ret != LZMA_OK)
+ return ret;
+
+ coder->header_pos = 0;
+ return LZMA_OK;
+}
+
+
+static lzma_ret
+metadata_encoder_init(lzma_coder *coder, lzma_allocator *allocator,
+ lzma_metadata *metadata, block_type type)
+{
+ lzma_ret ret = lzma_info_metadata_set(coder->info, allocator,
+ metadata, type == BLOCK_HEADER_METADATA, false);
+ if (ret != LZMA_OK)
+ return ret;
+
+ const lzma_vli metadata_size = lzma_metadata_size(metadata);
+ if (metadata_size == 0)
+ return LZMA_PROG_ERROR;
+
+ ret = block_header_encode(coder, allocator, metadata_size, type);
+ if (ret != LZMA_OK)
+ return ret;
+
+ return lzma_metadata_encoder_init(&coder->next, allocator,
+ &coder->block_options, metadata);
+}
+
+
+static lzma_ret
+data_encoder_init(lzma_coder *coder, lzma_allocator *allocator)
+{
+ lzma_ret ret = lzma_info_iter_next(&coder->iter, allocator);
+ if (ret != LZMA_OK)
+ return ret;
+
+ ret = block_header_encode(coder, allocator,
+ LZMA_VLI_VALUE_UNKNOWN, BLOCK_DATA);
+ if (ret != LZMA_OK)
+ return ret;
+
+ return lzma_block_encoder_init(&coder->next, allocator,
+ &coder->block_options);
+}
+
+
+static lzma_ret
+stream_encode(lzma_coder *coder, lzma_allocator *allocator,
+ const uint8_t *restrict in, size_t *restrict in_pos,
+ size_t in_size, uint8_t *restrict out,
+ size_t *restrict out_pos, size_t out_size, lzma_action action)
+{
+ // Main loop
+ while (*out_pos < out_size)
+ switch (coder->sequence) {
+ case SEQ_STREAM_HEADER_COPY:
+ case SEQ_HEADER_METADATA_COPY:
+ case SEQ_DATA_COPY:
+ case SEQ_FOOTER_METADATA_COPY:
+ case SEQ_STREAM_FOOTER_COPY:
+ bufcpy(coder->header, &coder->header_pos, coder->header_size,
+ out, out_pos, out_size);
+ if (coder->header_pos < coder->header_size)
+ return LZMA_OK;
+
+ lzma_free(coder->header, allocator);
+ coder->header = NULL;
+
+ switch (coder->sequence) {
+ case SEQ_STREAM_HEADER_COPY:
+ // Write Header Metadata Block if we have Extra for it
+ // or known Uncompressed Size.
+ if (coder->stream_options->header != NULL
+ || coder->stream_options
+ ->uncompressed_size
+ != LZMA_VLI_VALUE_UNKNOWN) {
+ coder->sequence = SEQ_HEADER_METADATA_INIT;
+ } else {
+ // Mark that Header Metadata Block doesn't
+ // exist.
+ if (lzma_info_size_set(coder->info,
+ LZMA_INFO_HEADER_METADATA, 0)
+ != LZMA_OK)
+ return LZMA_PROG_ERROR;
+
+ coder->sequence = SEQ_DATA_INIT;
+ }
+ break;
+
+ case SEQ_HEADER_METADATA_COPY:
+ case SEQ_DATA_COPY:
+ case SEQ_FOOTER_METADATA_COPY:
+ ++coder->sequence;
+ break;
+
+ case SEQ_STREAM_FOOTER_COPY:
+ return LZMA_STREAM_END;
+
+ default:
+ assert(0);
+ }
+
+ break;
+
+ case SEQ_HEADER_METADATA_INIT: {
+ lzma_metadata metadata = {
+ .header_metadata_size = LZMA_VLI_VALUE_UNKNOWN,
+ .total_size = LZMA_VLI_VALUE_UNKNOWN,
+ .uncompressed_size = coder->stream_options
+ ->uncompressed_size,
+ .index = NULL,
+ .extra = coder->stream_options->header,
+ };
+
+ const lzma_ret ret = metadata_encoder_init(coder, allocator,
+ &metadata, BLOCK_HEADER_METADATA);
+ if (ret != LZMA_OK)
+ return ret;
+
+ coder->sequence = SEQ_HEADER_METADATA_COPY;
+ break;
+ }
+
+ case SEQ_FOOTER_METADATA_INIT: {
+ lzma_metadata metadata = {
+ .header_metadata_size
+ = lzma_info_size_get(coder->info,
+ LZMA_INFO_HEADER_METADATA),
+ .total_size = LZMA_VLI_VALUE_UNKNOWN,
+ .uncompressed_size = LZMA_VLI_VALUE_UNKNOWN,
+ .index = lzma_info_index_get(coder->info, false),
+ .extra = coder->stream_options->footer,
+ };
+
+ const lzma_ret ret = metadata_encoder_init(coder, allocator,
+ &metadata, BLOCK_FOOTER_METADATA);
+ if (ret != LZMA_OK)
+ return ret;
+
+ coder->sequence = SEQ_FOOTER_METADATA_COPY;
+ break;
+ }
+
+ case SEQ_HEADER_METADATA_CODE:
+ case SEQ_FOOTER_METADATA_CODE: {
+ size_t dummy = 0;
+ lzma_ret ret = coder->next.code(coder->next.coder,
+ allocator, NULL, &dummy, 0,
+ out, out_pos, out_size, LZMA_RUN);
+ if (ret != LZMA_STREAM_END)
+ return ret;
+
+ ret = lzma_info_size_set(coder->info,
+ coder->sequence == SEQ_HEADER_METADATA_CODE
+ ? LZMA_INFO_HEADER_METADATA
+ : LZMA_INFO_FOOTER_METADATA,
+ coder->block_options.total_size);
+ if (ret != LZMA_OK)
+ return ret;
+
+ ++coder->sequence;
+ break;
+ }
+
+ case SEQ_DATA_INIT: {
+ // Don't create an empty Block unless it would be
+ // the only Data Block.
+ if (*in_pos == in_size) {
+ if (action != LZMA_FINISH)
+ return LZMA_OK;
+
+ if (lzma_info_index_count_get(coder->info) != 0) {
+ if (lzma_info_index_finish(coder->info))
+ return LZMA_DATA_ERROR;
+
+ coder->sequence = SEQ_FOOTER_METADATA_INIT;
+ break;
+ }
+ }
+
+ const lzma_ret ret = data_encoder_init(coder, allocator);
+ if (ret != LZMA_OK)
+ return ret;
+
+ coder->sequence = SEQ_DATA_COPY;
+ break;
+ }
+
+ case SEQ_DATA_CODE: {
+ static const lzma_action convert[4] = {
+ LZMA_RUN,
+ LZMA_SYNC_FLUSH,
+ LZMA_FINISH,
+ LZMA_FINISH,
+ };
+
+ lzma_ret ret = coder->next.code(coder->next.coder,
+ allocator, in, in_pos, in_size,
+ out, out_pos, out_size, convert[action]);
+ if (ret != LZMA_STREAM_END || action == LZMA_SYNC_FLUSH)
+ return ret;
+
+ ret = lzma_info_iter_set(&coder->iter,
+ coder->block_options.total_size,
+ coder->block_options.uncompressed_size);
+ if (ret != LZMA_OK)
+ return ret;
+
+ coder->sequence = SEQ_DATA_INIT;
+ break;
+ }
+
+ case SEQ_STREAM_FOOTER_INIT: {
+ assert(coder->header == NULL);
+
+ lzma_stream_flags flags = {
+ .check = coder->stream_options->check,
+ .has_crc32 = coder->stream_options->has_crc32,
+ .is_multi = true,
+ };
+
+ coder->header = lzma_alloc(LZMA_STREAM_TAIL_SIZE, allocator);
+ if (coder->header == NULL)
+ return LZMA_MEM_ERROR;
+
+ const lzma_ret ret = lzma_stream_tail_encode(
+ coder->header, &flags);
+ if (ret != LZMA_OK)
+ return ret;
+
+ coder->header_size = LZMA_STREAM_TAIL_SIZE;
+ coder->header_pos = 0;
+
+ coder->sequence = SEQ_STREAM_FOOTER_COPY;
+ break;
+ }
+
+ default:
+ return LZMA_PROG_ERROR;
+ }
+
+ return LZMA_OK;
+}
+
+
+static void
+stream_encoder_end(lzma_coder *coder, lzma_allocator *allocator)
+{
+ lzma_next_coder_end(&coder->next, allocator);
+ lzma_info_free(coder->info, allocator);
+ lzma_free(coder->header, allocator);
+ lzma_free(coder, allocator);
+ return;
+}
+
+
+static lzma_ret
+stream_encoder_init(lzma_next_coder *next,
+ lzma_allocator *allocator, const lzma_options_stream *options)
+{
+ if (options == NULL)
+ return LZMA_PROG_ERROR;
+
+ if (next->coder == NULL) {
+ next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
+ if (next->coder == NULL)
+ return LZMA_MEM_ERROR;
+
+ next->code = &stream_encode;
+ next->end = &stream_encoder_end;
+
+ next->coder->next = LZMA_NEXT_CODER_INIT;
+ next->coder->info = NULL;
+ } else {
+ lzma_free(next->coder->header, allocator);
+ }
+
+ next->coder->header = NULL;
+
+ next->coder->info = lzma_info_init(next->coder->info, allocator);
+ if (next->coder->info == NULL)
+ return LZMA_MEM_ERROR;
+
+ next->coder->sequence = SEQ_STREAM_HEADER_COPY;
+ next->coder->stream_options = options;
+
+ // Encode Stream Flags
+ {
+ lzma_stream_flags flags = {
+ .check = options->check,
+ .has_crc32 = options->has_crc32,
+ .is_multi = true,
+ };
+
+ next->coder->header = lzma_alloc(LZMA_STREAM_HEADER_SIZE,
+ allocator);
+ if (next->coder->header == NULL)
+ return LZMA_MEM_ERROR;
+
+ return_if_error(lzma_stream_header_encode(
+ next->coder->header, &flags));
+
+ next->coder->header_pos = 0;
+ next->coder->header_size = LZMA_STREAM_HEADER_SIZE;
+ }
+
+ if (lzma_info_size_set(next->coder->info, LZMA_INFO_STREAM_START,
+ options->alignment) != LZMA_OK)
+ return LZMA_PROG_ERROR;
+
+ lzma_info_iter_begin(next->coder->info, &next->coder->iter);
+
+ return LZMA_OK;
+}
+
+
+/*
+extern lzma_ret
+lzma_stream_encoder_multi_init(lzma_next_coder *next,
+ lzma_allocator *allocator, const lzma_options_stream *options)
+{
+ lzma_next_coder_init(stream_encoder_init, next, allocator, options);
+}
+*/
+
+
+extern LZMA_API lzma_ret
+lzma_stream_encoder_multi(
+ lzma_stream *strm, const lzma_options_stream *options)
+{
+ lzma_next_strm_init(strm, stream_encoder_init, options);
+
+ strm->internal->supported_actions[LZMA_RUN] = true;
+ strm->internal->supported_actions[LZMA_SYNC_FLUSH] = true;
+ strm->internal->supported_actions[LZMA_FULL_FLUSH] = true;
+ strm->internal->supported_actions[LZMA_FINISH] = true;
+
+ return LZMA_OK;
+}