| /////////////////////////////////////////////////////////////////////////////// |
| // |
| /// \file stream_encoder_single.c |
| /// \brief Encodes Single-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" |
| |
| |
| struct lzma_coder_s { |
| /// Uncompressed Size, Backward Size, and Footer Magic Bytes are |
| /// part of Block in the file format specification, but it is simpler |
| /// to implement them as part of Stream. |
| enum { |
| SEQ_HEADERS, |
| SEQ_DATA, |
| SEQ_FOOTER, |
| } sequence; |
| |
| /// Block encoder |
| lzma_next_coder block_encoder; |
| |
| /// Block encoder options |
| lzma_options_block block_options; |
| |
| /// Stream Flags; we need to have these in this struct so that we |
| /// can encode Stream Footer. |
| lzma_stream_flags stream_flags; |
| |
| /// Stream Header + Block Header, or Stream Footer |
| uint8_t *header; |
| size_t header_pos; |
| size_t header_size; |
| }; |
| |
| |
| 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 *out_pos, |
| size_t out_size, lzma_action action) |
| { |
| // NOTE: We don't check if the amount of input is in the proper limits, |
| // because the Block encoder will do it for us. |
| |
| while (*out_pos < out_size) |
| switch (coder->sequence) { |
| case SEQ_HEADERS: |
| bufcpy(coder->header, &coder->header_pos, coder->header_size, |
| out, out_pos, out_size); |
| |
| if (coder->header_pos == coder->header_size) { |
| coder->header_pos = 0; |
| coder->sequence = SEQ_DATA; |
| } |
| |
| break; |
| |
| case SEQ_DATA: { |
| const lzma_ret ret = coder->block_encoder.code( |
| coder->block_encoder.coder, allocator, |
| in, in_pos, in_size, |
| out, out_pos, out_size, action); |
| if (ret != LZMA_STREAM_END || action == LZMA_SYNC_FLUSH) |
| return ret; |
| |
| assert(*in_pos == in_size); |
| |
| assert(coder->header_size >= LZMA_STREAM_TAIL_SIZE); |
| coder->header_size = LZMA_STREAM_TAIL_SIZE; |
| |
| return_if_error(lzma_stream_tail_encode( |
| coder->header, &coder->stream_flags)); |
| |
| coder->sequence = SEQ_FOOTER; |
| break; |
| } |
| |
| case SEQ_FOOTER: |
| bufcpy(coder->header, &coder->header_pos, coder->header_size, |
| out, out_pos, out_size); |
| |
| return coder->header_pos == coder->header_size |
| ? LZMA_STREAM_END : LZMA_OK; |
| |
| default: |
| return LZMA_PROG_ERROR; |
| } |
| |
| return LZMA_OK; |
| } |
| |
| |
| static void |
| stream_encoder_end(lzma_coder *coder, lzma_allocator *allocator) |
| { |
| lzma_next_coder_end(&coder->block_encoder, 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->block_encoder = LZMA_NEXT_CODER_INIT; |
| } else { |
| // Free the previous buffer, if any. |
| lzma_free(next->coder->header, allocator); |
| } |
| |
| // At this point, next->coder->header points to nothing useful. |
| next->coder->header = NULL; |
| |
| // Basic initializations |
| next->coder->sequence = SEQ_HEADERS; |
| next->coder->header_pos = 0; |
| |
| // Initialize next->coder->stream_flags. |
| next->coder->stream_flags = (lzma_stream_flags){ |
| .check = options->check, |
| .has_crc32 = options->has_crc32, |
| .is_multi = false, |
| }; |
| |
| // Initialize next->coder->block_options. |
| next->coder->block_options = (lzma_options_block){ |
| .check = options->check, |
| .has_crc32 = options->has_crc32, |
| .has_eopm = options->uncompressed_size |
| == LZMA_VLI_VALUE_UNKNOWN, |
| .is_metadata = false, |
| .has_uncompressed_size_in_footer = options->uncompressed_size |
| == LZMA_VLI_VALUE_UNKNOWN, |
| .has_backward_size = true, |
| .handle_padding = false, |
| .compressed_size = LZMA_VLI_VALUE_UNKNOWN, |
| .uncompressed_size = options->uncompressed_size, |
| .compressed_reserve = 0, |
| .uncompressed_reserve = 0, |
| .total_size = LZMA_VLI_VALUE_UNKNOWN, |
| .total_limit = LZMA_VLI_VALUE_UNKNOWN, |
| .uncompressed_limit = LZMA_VLI_VALUE_UNKNOWN, |
| .padding = LZMA_BLOCK_HEADER_PADDING_AUTO, |
| .alignment = options->alignment + LZMA_STREAM_HEADER_SIZE, |
| }; |
| memcpy(next->coder->block_options.filters, options->filters, |
| sizeof(options->filters)); |
| |
| return_if_error(lzma_block_header_size(&next->coder->block_options)); |
| |
| // Encode Stream Flags and Block Header into next->coder->header. |
| next->coder->header_size = (size_t)(LZMA_STREAM_HEADER_SIZE) |
| + next->coder->block_options.header_size; |
| next->coder->header = lzma_alloc(next->coder->header_size, allocator); |
| if (next->coder->header == NULL) |
| return LZMA_MEM_ERROR; |
| |
| return_if_error(lzma_stream_header_encode(next->coder->header, |
| &next->coder->stream_flags)); |
| |
| return_if_error(lzma_block_header_encode( |
| next->coder->header + LZMA_STREAM_HEADER_SIZE, |
| &next->coder->block_options)); |
| |
| // Initialize the Block encoder. |
| return lzma_block_encoder_init(&next->coder->block_encoder, allocator, |
| &next->coder->block_options); |
| } |
| |
| |
| /* |
| extern lzma_ret |
| lzma_stream_encoder_single_init(lzma_next_coder *next, |
| lzma_allocator *allocator, const lzma_options_stream *options) |
| { |
| lzma_next_coder_init(stream_encoder_init, allocator, options); |
| } |
| */ |
| |
| |
| extern LZMA_API lzma_ret |
| lzma_stream_encoder_single( |
| 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_FINISH] = true; |
| |
| return LZMA_OK; |
| } |