diff --git a/src/liblzma/api/lzma/container.h b/src/liblzma/api/lzma/container.h index b2b912d5..564c6aaf 100644 --- a/src/liblzma/api/lzma/container.h +++ b/src/liblzma/api/lzma/container.h @@ -73,7 +73,7 @@ typedef struct { * * Decoder: Bitwise-or of zero or more of the decoder flags: * LZMA_TELL_NO_CHECK, LZMA_TELL_UNSUPPORTED_CHECK, - * LZMA_TELL_ANY_CHECK, LZMA_CONCATENATED + * LZMA_TELL_ANY_CHECK, LZMA_CONCATENATED, LZMA_FAIL_FAST */ uint32_t flags; @@ -615,6 +615,29 @@ extern LZMA_API(lzma_ret) lzma_microlzma_encoder( #define LZMA_CONCATENATED UINT32_C(0x08) +/** + * This flag makes the threaded decoder report errors (like LZMA_DATA_ERROR) + * as soon as they are detected. This saves time when the application has no + * interest in a partially decompressed truncated or corrupt file. Note that + * due to timing randomness, if the same truncated or corrupt input is + * decompressed multiple times with this flag, a different amount of output + * may be produced by different runs, and even the error code might vary. + * + * Without this flag the threaded decoder will provide as much output as + * possible at first and then report the pending error. This default behavior + * matches the single-threaded decoder and provides repeatable behavior + * with truncated or corrupt input. There are a few special cases where the + * behavior can still differ like memory allocation failures (LZMA_MEM_ERROR). + * + * Single-threaded decoders currently ignore this flag. + * + * Support for this flag was added in liblzma 5.3.3alpha. Note that in older + * versions this flag isn't supported (LZMA_OPTIONS_ERROR) even by functions + * that ignore this flag in newer liblzma versions. + */ +#define LZMA_FAIL_FAST UINT32_C(0x20) + + /** * \brief Initialize .xz Stream decoder * diff --git a/src/liblzma/common/common.h b/src/liblzma/common/common.h index 67996228..36366dbc 100644 --- a/src/liblzma/common/common.h +++ b/src/liblzma/common/common.h @@ -67,14 +67,15 @@ #define LZMA_FILTER_RESERVED_START (LZMA_VLI_C(1) << 62) -/// Supported flags that can be passed to lzma_stream_decoder() -/// or lzma_auto_decoder(). +/// Supported flags that can be passed to lzma_stream_decoder(), +/// lzma_auto_decoder(), or lzma_stream_decoder_mt(). #define LZMA_SUPPORTED_FLAGS \ ( LZMA_TELL_NO_CHECK \ | LZMA_TELL_UNSUPPORTED_CHECK \ | LZMA_TELL_ANY_CHECK \ | LZMA_IGNORE_CHECK \ - | LZMA_CONCATENATED ) + | LZMA_CONCATENATED \ + | LZMA_FAIL_FAST ) /// Largest valid lzma_action value as unsigned integer. diff --git a/src/liblzma/common/stream_decoder_mt.c b/src/liblzma/common/stream_decoder_mt.c index 7f445982..32e0b892 100644 --- a/src/liblzma/common/stream_decoder_mt.c +++ b/src/liblzma/common/stream_decoder_mt.c @@ -300,6 +300,10 @@ struct lzma_stream_coder { /// Stream Padding is a multiple of four bytes. bool concatenated; + /// If true, we will return any errors immediately instead of first + /// producing all output before the location of the error. + bool fail_fast; + /// When decoding concatenated Streams, this is true as long as we /// are decoding the first Stream. This is needed to avoid misleading @@ -711,13 +715,12 @@ read_output_and_wait(struct lzma_stream_coder *coder, coder->pending_error = coder->thread_error; - // FIXME? Add a flag to do this conditionally? - // That way errors would get reported to the - // application without a delay. -// if (coder->fast_errors) { -// ret = coder->thread_error; -// break; -// } + // If LZMA_FAIL_FAST was used, report errors + // from worker threads immediately. + if (coder->fail_fast) { + ret = coder->thread_error; + break; + } } // Check if decoding of the next Block can be started. @@ -1690,22 +1693,24 @@ stream_decode_mt(void *coder_ptr, const lzma_allocator *allocator, break; case SEQ_ERROR: - // Let the application get all data before the point where - // the error was detected. This matches the behavior of - // single-threaded use. - // - // FIXME? Some errors (LZMA_MEM_ERROR) don't get here, - // they are returned immediately. Thus in rare cases the - // output will be less than in single-threaded mode. But - // maybe this doesn't matter much in practice. - return_if_error(read_output_and_wait(coder, allocator, - out, out_pos, out_size, - NULL, true, &wait_abs, &has_blocked)); + if (!coder->fail_fast) { + // Let the application get all data before the point + // where the error was detected. This matches the + // behavior of single-threaded use. + // + // FIXME? Some errors (LZMA_MEM_ERROR) don't get here, + // they are returned immediately. Thus in rare cases + // the output will be less than in the single-threaded + // mode. Maybe this doesn't matter much in practice. + return_if_error(read_output_and_wait(coder, allocator, + out, out_pos, out_size, + NULL, true, &wait_abs, &has_blocked)); - // We get here only if the error happened in the main thread, - // for example, unsupported Block Header. - if (!lzma_outq_is_empty(&coder->outq)) - return LZMA_OK; + // We get here only if the error happened in the main + // thread, for example, unsupported Block Header. + if (!lzma_outq_is_empty(&coder->outq)) + return LZMA_OK; + } return coder->pending_error; @@ -1900,6 +1905,7 @@ stream_decoder_mt_init(lzma_next_coder *next, const lzma_allocator *allocator, coder->tell_any_check = (options->flags & LZMA_TELL_ANY_CHECK) != 0; coder->ignore_check = (options->flags & LZMA_IGNORE_CHECK) != 0; coder->concatenated = (options->flags & LZMA_CONCATENATED) != 0; + coder->fail_fast = (options->flags & LZMA_FAIL_FAST) != 0; coder->first_stream = true; coder->out_was_filled = false;