It makes lzma_code() return LZMA_BLOCK_END after every .xz Block.
It can be useful in special cases where the boundaries of Blocks
have a meaning to the application. For example, if using the
seekable decoder, one can seek by Block number and then decode
exactly one Block by detecting LZMA_BLOCK_END.
Even if one knows the uncompressed size of the Block, the benefit
of waiting for LZMA_BLOCK_END is that then one knows that the
integrity check has been verified. A decoder might have provided all
output from the Block, but if the last input bytes of the Block
haven't been provided to the decoder, it cannot have verified the
integrity check.
* output bytes should be picked from strm->next_out.
*/
+ LZMA_BLOCK_END = 14,
+ /**<
+ * \brief End of .xz Block was reached
+ *
+ * This can only be returned if the LZMA_TELL_BLOCK_END flag
+ * was used when a .xz decoder was initialized. It indicates
+ * that end of a .xz Block was successfully reached (including
+ * verifying its integrity check). Other than providing this
+ * information, this can be handled like LZMA_OK.
+ */
+
LZMA_NO_CHECK = 2,
/**<
* \brief Input stream has no integrity check
* - LZMA_IGNORE_CHECK
* - LZMA_CONCATENATED
* - LZMA_FAIL_FAST
+ * - LZMA_TELL_BLOCK_END
*/
uint32_t flags;
#define LZMA_FAIL_FAST UINT32_C(0x20)
+/**
+ * This flag makes lzma_code() return LZMA_BLOCK_END at the end of every .xz
+ * Block when decoding a .xz file. Other than providing this information,
+ * LZMA_BLOCK_END can be handled like LZMA_OK.
+ *
+ * \since 5.9.1alpha
+ */
+#define LZMA_TELL_BLOCK_END UINT32_C(0x40)
+
+
/**
* \brief Initialize .xz Stream decoder
*
* \param flags Bitwise-or of zero or more of the decoder flags:
* LZMA_TELL_NO_CHECK, LZMA_TELL_UNSUPPORTED_CHECK,
* LZMA_TELL_ANY_CHECK, LZMA_IGNORE_CHECK,
- * LZMA_CONCATENATED, LZMA_FAIL_FAST
+ * LZMA_CONCATENATED, LZMA_FAIL_FAST,
+ * LZMA_TELL_BLOCK_END
*
* \return Possible lzma_ret values:
* - LZMA_OK: Initialization was successful.
* \param flags Bitwise-or of zero or more of the decoder flags:
* LZMA_TELL_NO_CHECK, LZMA_TELL_UNSUPPORTED_CHECK,
* LZMA_TELL_ANY_CHECK, LZMA_IGNORE_CHECK,
- * LZMA_CONCATENATED, LZMA_FAIL_FAST
+ * LZMA_CONCATENATED, LZMA_FAIL_FAST,
+ * LZMA_TELL_BLOCK_END
*
* \return Possible lzma_ret values:
* - LZMA_OK: Initialization was successful.
* although only LZMA_CONCATENATED and (in very rare
* cases) LZMA_IGNORE_CHECK are actually useful.
* LZMA_TELL_NO_CHECK, LZMA_TELL_UNSUPPORTED_CHECK,
- * and LZMA_FAIL_FAST do nothing. LZMA_TELL_ANY_CHECK
- * is supported for consistency only as CRC32 is
- * always used in the .lz format.
+ * LZMA_FAIL_FAST, and LZMA_TELL_BLOCK_END do nothing.
+ * LZMA_TELL_ANY_CHECK is supported for consistency
+ * only as CRC32 is always used in the .lz format.
*
* \return Possible lzma_ret values:
* - LZMA_OK: Initialization was successful.
* LZMA_TELL_NO_CHECK, LZMA_TELL_UNSUPPORTED_CHECK,
* LZMA_IGNORE_CHECK, LZMA_CONCATENATED,
* LZMA_FAIL_FAST. Note that LZMA_TELL_ANY_CHECK
- * is not allowed and will return LZMA_PROG_ERROR.
+ * and LZMA_TELL_BLOCK_END are not allowed and will
+ * return LZMA_PROG_ERROR.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
FALLTHROUGH;
+ case LZMA_BLOCK_END:
case LZMA_NO_CHECK:
case LZMA_UNSUPPORTED_CHECK:
case LZMA_GET_CHECK:
| LZMA_TELL_ANY_CHECK \
| LZMA_IGNORE_CHECK \
| LZMA_CONCATENATED \
- | LZMA_FAIL_FAST )
+ | LZMA_FAIL_FAST \
+ | LZMA_TELL_BLOCK_END )
/// Largest valid lzma_action value as unsigned integer.
/// and verifying the integrity check.
bool ignore_check;
+ /// If true, LZMA_BLOCK_END is returned every time we finish
+ /// decoding a Block.
+ bool tell_block_end;
+
/// Pointer to lzma_stream.seek_pos:
/// - This is written to pass the target input seek position to
/// the application when we return LZMA_SEEK_NEEDED.
if (action == LZMA_SEEK_TO_OFFSET) {
// Don't call locate if we already have the right Block.
//
- // NOTE: SEQ_ITER_NEXT_BLOCK means that we are seeking
- // right after the decoder initialization, and thus
- // the iterator doesn't contain valid data yet.
+ // NOTE: SEQ_ITER_NEXT_BLOCK means that we are
+ // (1) seeking right after the decoder initialization,
+ // and thus the iterator doesn't contain valid
+ // data yet; or
+ // (2) seeking right after returning LZMA_BLOCK_END,
+ // and thus the desired target offset cannot
+ // be in the current Block.
if (coder->sequence != SEQ_ITER_NEXT_BLOCK
&& coder->cur_out_offset <= target
&& target
// Block decoder, it has verified that they match the Index;
// we don't need to check them here.
coder->sequence = SEQ_ITER_NEXT_BLOCK;
+
+ // If the flag LZMA_TELL_BLOCK_END was used,
+ // return LZMA_BLOCK_END now.
+ if (coder->tell_block_end)
+ return LZMA_BLOCK_END;
+
break;
}
return LZMA_PROG_ERROR;
// Not many flags are supported.
- if (flags & ~LZMA_IGNORE_CHECK)
+ if (flags & ~(LZMA_IGNORE_CHECK | LZMA_TELL_BLOCK_END))
return LZMA_OPTIONS_ERROR;
lzma_seekable_coder *coder = next->coder;
coder->memusage = LZMA_MEMUSAGE_BASE;
coder->ignore_check = (flags & LZMA_IGNORE_CHECK) != 0;
+ coder->tell_block_end = (flags & LZMA_TELL_BLOCK_END) != 0;
lzma_index_iter_init(&coder->iter, idx);
coder->external_seek_pos = seek_pos;
return LZMA_PROG_ERROR;
// Catch flags that are not allowed in buffer-to-buffer decoding.
- if (flags & LZMA_TELL_ANY_CHECK)
+ if (flags & (LZMA_TELL_ANY_CHECK | LZMA_TELL_BLOCK_END))
return LZMA_PROG_ERROR;
// Initialize the Stream decoder.
/// and verifying the integrity check.
bool ignore_check;
+ /// If true, LZMA_BLOCK_END is returned every time we finish
+ /// decoding a Block.
+ bool tell_block_end;
+
/// If true, we will decode concatenated Streams that possibly have
/// Stream Padding between or after them. LZMA_STREAM_END is returned
/// once the application isn't giving us any new input (LZMA_FINISH),
coder->block_options.uncompressed_size));
coder->sequence = SEQ_BLOCK_HEADER;
+
+ if (coder->tell_block_end)
+ return LZMA_BLOCK_END;
+
break;
}
= (flags & LZMA_TELL_UNSUPPORTED_CHECK) != 0;
coder->tell_any_check = (flags & LZMA_TELL_ANY_CHECK) != 0;
coder->ignore_check = (flags & LZMA_IGNORE_CHECK) != 0;
+ coder->tell_block_end = (flags & LZMA_TELL_BLOCK_END) != 0;
coder->concatenated = (flags & LZMA_CONCATENATED) != 0;
coder->first_stream = true;
/// and verifying the integrity check.
bool ignore_check;
+ /// If true, LZMA_BLOCK_END is returned every time we finish
+ /// decoding a Block.
+ bool tell_block_end;
+
/// If true, we will decode concatenated Streams that possibly have
/// Stream Padding between or after them. LZMA_STREAM_END is returned
/// once the application isn't giving us any new input (LZMA_FINISH),
// thread of the next Block (if it is still
// running) to start telling the main thread
// when new output is available.
- if (ret == LZMA_STREAM_END)
+ if (ret == LZMA_STREAM_END) {
lzma_outq_enable_partial_output(
&coder->outq,
&worker_enable_partial_update);
- // Loop until a Block wasn't finished.
+ // If LZMA_TELL_BLOCK_END was used,
+ // return LZMA_BLOCK_END.
+ //
+ // Also set out_was_filled = true even
+ // though the out buffer might not
+ // have become full; LZMA_BLOCK_END
+ // is a kind of "no more output was
+ // possible" situation, and thus we
+ // don't want waiting_allowed to be
+ // true on the next call to
+ // stream_decode_mt().
+ if (coder->tell_block_end) {
+ coder->out_was_filled = true;
+ ret = LZMA_BLOCK_END;
+ }
+ }
+
+ // Unless LZMA_TELL_BLOCK_END was used,
+ // loop until a Block wasn't finished.
// It's important to loop around even if
// *out_pos == out_size because there could
// be an empty Block that will return
} while (ret == LZMA_STREAM_END);
// Check if lzma_outq_read reported an error from
- // the Block decoder.
+ // the Block decoder or if we are returning
+ // LZMA_BLOCK_END.
if (ret != LZMA_OK)
break;
// If we are returning an error, then the application cannot get
// more output from us and thus keeping the threads running is
// useless and waste of CPU time.
- if (ret != LZMA_OK && ret != LZMA_TIMED_OUT)
+ if (ret != LZMA_OK && ret != LZMA_TIMED_OUT && ret != LZMA_BLOCK_END)
threads_stop(coder);
return ret;
coder->block_options.uncompressed_size));
coder->sequence = SEQ_BLOCK_HEADER;
+
+ if (coder->tell_block_end)
+ return LZMA_BLOCK_END;
+
break;
}
= (options->flags & LZMA_TELL_UNSUPPORTED_CHECK) != 0;
coder->tell_any_check = (options->flags & LZMA_TELL_ANY_CHECK) != 0;
coder->ignore_check = (options->flags & LZMA_IGNORE_CHECK) != 0;
+ coder->tell_block_end = (options->flags & LZMA_TELL_BLOCK_END) != 0;
coder->concatenated = (options->flags & LZMA_CONCATENATED) != 0;
coder->fail_fast = (options->flags & LZMA_FAIL_FAST) != 0;