diff options
Diffstat (limited to 'src/liblzma/common/stream_encoder.c')
-rw-r--r-- | src/liblzma/common/stream_encoder.c | 76 |
1 files changed, 66 insertions, 10 deletions
diff --git a/src/liblzma/common/stream_encoder.c b/src/liblzma/common/stream_encoder.c index 292efc82..705ec0eb 100644 --- a/src/liblzma/common/stream_encoder.c +++ b/src/liblzma/common/stream_encoder.c @@ -25,12 +25,20 @@ struct lzma_coder_s { SEQ_STREAM_FOOTER, } sequence; + /// True if Block encoder has been initialized by + /// lzma_stream_encoder_init() or stream_encoder_update() + /// and thus doesn't need to be initialized in stream_encode(). + bool block_encoder_is_initialized; + /// Block lzma_next_coder block_encoder; /// Options for the Block encoder lzma_block block_options; + /// The filter chain currently in use + lzma_filter filters[LZMA_FILTERS_MAX + 1]; + /// Index encoder. This is separate from Block encoder, because this /// doesn't take much memory, and when encoding multiple Streams /// with the same encoding options we avoid reallocating memory. @@ -117,12 +125,16 @@ stream_encode(lzma_coder *coder, lzma_allocator *allocator, break; } - // Initialize the Block encoder except if this is the first - // Block, because stream_encoder_init() has already - // initialized it. - if (lzma_index_count(coder->index) != 0) + // Initialize the Block encoder unless it was already + // initialized by lzma_stream_encoder_init() or + // stream_encoder_update(). + if (!coder->block_encoder_is_initialized) return_if_error(block_encoder_init(coder, allocator)); + // Make it false so that we don't skip the initialization + // with the next Block. + coder->block_encoder_is_initialized = false; + // Encode the Block Header. This shouldn't fail since we have // already initialized the Block encoder. if (lzma_block_header_encode(&coder->block_options, @@ -202,11 +214,54 @@ stream_encoder_end(lzma_coder *coder, lzma_allocator *allocator) lzma_next_end(&coder->block_encoder, allocator); lzma_next_end(&coder->index_encoder, allocator); lzma_index_end(coder->index, allocator); + + for (size_t i = 0; coder->filters[i].id != LZMA_VLI_UNKNOWN; ++i) + lzma_free(coder->filters[i].options, allocator); + lzma_free(coder, allocator); return; } +static lzma_ret +stream_encoder_update(lzma_coder *coder, lzma_allocator *allocator, + const lzma_filter *filters, + const lzma_filter *reversed_filters) +{ + if (coder->sequence <= SEQ_BLOCK_INIT) { + // There is no incomplete Block waiting to be finished, + // thus we can change the whole filter chain. Start by + // trying to initialize the Block encoder with the new + // chain. This way we detect if the chain is valid. + coder->block_encoder_is_initialized = false; + coder->block_options.filters = (lzma_filter *)(filters); + const lzma_ret ret = block_encoder_init(coder, allocator); + coder->block_options.filters = coder->filters; + if (ret != LZMA_OK) + return ret; + + coder->block_encoder_is_initialized = true; + + } else if (coder->sequence <= SEQ_BLOCK_ENCODE) { + // We are in the middle of a Block. Try to update only + // the filter-specific options. + return_if_error(coder->block_encoder.update( + coder->block_encoder.coder, allocator, + filters, reversed_filters)); + } else { + // Trying to update the filter chain when we are already + // encoding Index or Stream Footer. + return LZMA_PROG_ERROR; + } + + // Free the copy of the old chain and make a copy of the new chain. + for (size_t i = 0; coder->filters[i].id != LZMA_VLI_UNKNOWN; ++i) + lzma_free(coder->filters[i].options, allocator); + + return lzma_filters_copy(filters, coder->filters, allocator); +} + + extern lzma_ret lzma_stream_encoder_init(lzma_next_coder *next, lzma_allocator *allocator, const lzma_filter *filters, lzma_check check) @@ -223,6 +278,7 @@ lzma_stream_encoder_init(lzma_next_coder *next, lzma_allocator *allocator, next->code = &stream_encode; next->end = &stream_encoder_end; + next->update = &stream_encoder_update; next->coder->block_encoder = LZMA_NEXT_CODER_INIT; next->coder->index_encoder = LZMA_NEXT_CODER_INIT; @@ -233,7 +289,7 @@ lzma_stream_encoder_init(lzma_next_coder *next, lzma_allocator *allocator, next->coder->sequence = SEQ_STREAM_HEADER; next->coder->block_options.version = 0; next->coder->block_options.check = check; - next->coder->block_options.filters = (lzma_filter *)(filters); + next->coder->filters[0].id = LZMA_VLI_UNKNOWN; // Initialize the Index next->coder->index = lzma_index_init(next->coder->index, allocator); @@ -251,11 +307,11 @@ lzma_stream_encoder_init(lzma_next_coder *next, lzma_allocator *allocator, next->coder->buffer_pos = 0; next->coder->buffer_size = LZMA_STREAM_HEADER_SIZE; - // Initialize the Block encoder. This way we detect if the given - // filters are supported by the current liblzma build, and the - // application doesn't need to keep the filters structure available - // unless it is going to use LZMA_FULL_FLUSH. - return block_encoder_init(next->coder, allocator); + // Initialize the Block encoder. This way we detect unsupported + // filter chains when initializing the Stream encoder instead of + // giving an error after Stream Header has already written out. + return stream_encoder_update( + next->coder, allocator, filters, NULL); } |