// SPDX-License-Identifier: 0BSD

///////////////////////////////////////////////////////////////////////////////
//
/// \file       file_info.c
/// \brief      Decode .xz file information into a lzma_index structure
//
//  Author:     Lasse Collin
//
///////////////////////////////////////////////////////////////////////////////

#include "index_decoder.h"


typedef struct {
	enum {
		SEQ_MAGIC_BYTES,
		SEQ_PADDING_SEEK,
		SEQ_PADDING_DECODE,
		SEQ_FOOTER,
		SEQ_INDEX_INIT,
		SEQ_INDEX_DECODE,
		SEQ_HEADER_DECODE,
		SEQ_HEADER_COMPARE,
	} sequence;

	/// Absolute position of in[*in_pos] in the file. All code that
	/// modifies *in_pos also updates this. seek_to_pos() needs this
	/// to determine if we need to request the application to seek for
	/// us or if we can do the seeking internally by adjusting *in_pos.
	uint64_t file_cur_pos;

	/// This refers to absolute positions of interesting parts of the
	/// input file. Sometimes it points to the *beginning* of a specific
	/// field and sometimes to the *end* of a field. The current target
	/// position at each moment is explained in the comments.
	uint64_t file_target_pos;

	/// Size of the .xz file (from the application).
	uint64_t file_size;

	/// Index decoder
	lzma_next_coder index_decoder;

	/// Number of bytes remaining in the Index field that is currently
	/// being decoded.
	lzma_vli index_remaining;

	/// The Index decoder will store the decoded Index in this pointer.
	lzma_index *this_index;

	/// Amount of Stream Padding in the current Stream.
	lzma_vli stream_padding;

	/// The final combined index is collected here.
	lzma_index *combined_index;

	/// Pointer from the application where to store the index information
	/// after successful decoding.
	lzma_index **dest_index;

	/// Pointer to lzma_stream.seek_pos to be used when returning
	/// LZMA_SEEK_NEEDED. This is set by seek_to_pos() when needed.
	uint64_t *external_seek_pos;

	/// Memory usage limit
	uint64_t memlimit;

	/// Stream Flags from the very beginning of the file.
	lzma_stream_flags first_header_flags;

	/// Stream Flags from Stream Header of the current Stream.
	lzma_stream_flags header_flags;

	/// Stream Flags from Stream Footer of the current Stream.
	lzma_stream_flags footer_flags;

	size_t temp_pos;
	size_t temp_size;
	uint8_t temp[8192];

} lzma_file_info_coder;


/// Copies data from in[*in_pos] into coder->temp until
/// coder->temp_pos == coder->temp_size. This also keeps coder->file_cur_pos
/// in sync with *in_pos. Returns true if more input is needed.
static bool
fill_temp(lzma_file_info_coder *coder, const uint8_t *restrict in,
		size_t *restrict in_pos, size_t in_size)
{
	coder->file_cur_pos += lzma_bufcpy(in, in_pos, in_size,
			coder->temp, &coder->temp_pos, coder->temp_size);
	return coder->temp_pos < coder->temp_size;
}


/// Seeks to the absolute file position specified by target_pos.
/// This tries to do the seeking by only modifying *in_pos, if possible.
/// The main benefit of this is that if one passes the whole file at once
/// to lzma_code(), the decoder will never need to return LZMA_SEEK_NEEDED
/// as all the seeking can be done by adjusting *in_pos in this function.
///
/// Returns true if an external seek is needed and the caller must return
/// LZMA_SEEK_NEEDED.
static bool
seek_to_pos(lzma_file_info_coder *coder, uint64_t target_pos,
		size_t in_start, size_t *in_pos, size_t in_size)
{
	// The input buffer doesn't extend beyond the end of the file.
	// This has been checked by file_info_decode() already.
	assert(coder->file_size - coder->file_cur_pos >= in_size - *in_pos);

	const uint64_t pos_min = coder->file_cur_pos - (*in_pos - in_start);
	const uint64_t pos_max = coder->file_cur_pos + (in_size - *in_pos);

	bool external_seek_needed;

	if (target_pos >= pos_min && target_pos <= pos_max) {
		// The requested position is available in the current input
		// buffer or right after it. That is, in a corner case we
		// end up setting *in_pos == in_size and thus will immediately
		// need new input bytes from the application.
		*in_pos += (size_t)(target_pos - coder->file_cur_pos);
		external_seek_needed = false;
	} else {
		// Ask the application to seek the input file.
		*coder->external_seek_pos = target_pos;
		external_seek_needed = true;

		// Mark the whole input buffer as used. This way
		// lzma_stream.total_in will have a better estimate
		// of the amount of data read. It still won't be perfect
		// as the value will depend on the input buffer size that
		// the application uses, but it should be good enough for
		// those few who want an estimate.
		*in_pos = in_size;
	}

	// After seeking (internal or external) the current position
	// will match the requested target position.
	coder->file_cur_pos = target_pos;

	return external_seek_needed;
}


/// The caller sets coder->file_target_pos so that it points to the *end*
/// of the desired file position. This function then determines how far
/// backwards from that position we can seek. After seeking fill_temp()
/// can be used to read data into coder->temp. When fill_temp() has finished,
/// coder->temp[coder->temp_size] will match coder->file_target_pos.
///
/// This also validates that coder->target_file_pos is sane in sense that
/// we aren't trying to seek too far backwards (too close or beyond the
/// beginning of the file).
static lzma_ret
reverse_seek(lzma_file_info_coder *coder,
		size_t in_start, size_t *in_pos, size_t in_size)
{
	// Check that there is enough data before the target position
	// to contain at least Stream Header and Stream Footer. If there
	// isn't, the file cannot be valid.
	if (coder->file_target_pos < 2 * LZMA_STREAM_HEADER_SIZE)
		return LZMA_DATA_ERROR;

	coder->temp_pos = 0;

	// The Stream Header at the very beginning of the file gets handled
	// specially in SEQ_MAGIC_BYTES and thus we will never need to seek
	// there. By not seeking to the first LZMA_STREAM_HEADER_SIZE bytes
	// we avoid a useless external seek after SEQ_MAGIC_BYTES if the
	// application uses an extremely small input buffer and the input
	// file is very small.
	if (coder->file_target_pos - LZMA_STREAM_HEADER_SIZE
			< sizeof(coder->temp))
		coder->temp_size = (size_t)(coder->file_target_pos
				- LZMA_STREAM_HEADER_SIZE);
	else
		coder->temp_size = sizeof(coder->temp);

	// The above if-statements guarantee this. This is important because
	// the Stream Header/Footer decoders assume that there's at least
	// LZMA_STREAM_HEADER_SIZE bytes in coder->temp.
	assert(coder->temp_size >= LZMA_STREAM_HEADER_SIZE);

	if (seek_to_pos(coder, coder->file_target_pos - coder->temp_size,
			in_start, in_pos, in_size))
		return LZMA_SEEK_NEEDED;

	return LZMA_OK;
}


/// Gets the number of zero-bytes at the end of the buffer.
static size_t
get_padding_size(const uint8_t *buf, size_t buf_size)
{
	size_t padding = 0;
	while (buf_size > 0 && buf[--buf_size] == 0x00)
		++padding;

	return padding;
}


/// With the Stream Header at the very beginning of the file, LZMA_FORMAT_ERROR
/// is used to tell the application that Magic Bytes didn't match. In other
/// Stream Header/Footer fields (in the middle/end of the file) it could be
/// a bit confusing to return LZMA_FORMAT_ERROR as we already know that there
/// is a valid Stream Header at the beginning of the file. For those cases
/// this function is used to convert LZMA_FORMAT_ERROR to LZMA_DATA_ERROR.
static lzma_ret
hide_format_error(lzma_ret ret)
{
	if (ret == LZMA_FORMAT_ERROR)
		ret = LZMA_DATA_ERROR;

	return ret;
}


/// Calls the Index decoder and updates coder->index_remaining.
/// This is a separate function because the input can be either directly
/// from the application or from coder->temp.
static lzma_ret
decode_index(lzma_file_info_coder *coder, const lzma_allocator *allocator,
		const uint8_t *restrict in, size_t *restrict in_pos,
		size_t in_size, bool update_file_cur_pos)
{
	const size_t in_start = *in_pos;

	const lzma_ret ret = coder->index_decoder.code(
			coder->index_decoder.coder,
			allocator, in, in_pos, in_size,
			NULL, NULL, 0, LZMA_RUN);

	coder->index_remaining -= *in_pos - in_start;

	if (update_file_cur_pos)
		coder->file_cur_pos += *in_pos - in_start;

	return ret;
}


static lzma_ret
file_info_decode(void *coder_ptr, const lzma_allocator *allocator,
		const uint8_t *restrict in, size_t *restrict in_pos,
		size_t in_size,
		uint8_t *restrict out lzma_attribute((__unused__)),
		size_t *restrict out_pos lzma_attribute((__unused__)),
		size_t out_size lzma_attribute((__unused__)),
		lzma_action action lzma_attribute((__unused__)))
{
	lzma_file_info_coder *coder = coder_ptr;
	const size_t in_start = *in_pos;

	// If the caller provides input past the end of the file, trim
	// the extra bytes from the buffer so that we won't read too far.
	assert(coder->file_size >= coder->file_cur_pos);
	if (coder->file_size - coder->file_cur_pos < in_size - in_start)
		in_size = in_start
			+ (size_t)(coder->file_size - coder->file_cur_pos);

	while (true)
	switch (coder->sequence) {
	case SEQ_MAGIC_BYTES:
		// Decode the Stream Header at the beginning of the file
		// first to check if the Magic Bytes match. The flags
		// are stored in coder->first_header_flags so that we
		// don't need to seek to it again.
		//
		// Check that the file is big enough to contain at least
		// Stream Header.
		if (coder->file_size < LZMA_STREAM_HEADER_SIZE)
			return LZMA_FORMAT_ERROR;

		// Read the Stream Header field into coder->temp.
		if (fill_temp(coder, in, in_pos, in_size))
			return LZMA_OK;

		// This is the only Stream Header/Footer decoding where we
		// want to return LZMA_FORMAT_ERROR if the Magic Bytes don't
		// match. Elsewhere it will be converted to LZMA_DATA_ERROR.
		return_if_error(lzma_stream_header_decode(
				&coder->first_header_flags, coder->temp));

		// Now that we know that the Magic Bytes match, check the
		// file size. It's better to do this here after checking the
		// Magic Bytes since this way we can give LZMA_FORMAT_ERROR
		// instead of LZMA_DATA_ERROR when the Magic Bytes don't
		// match in a file that is too big or isn't a multiple of
		// four bytes.
		if (coder->file_size > LZMA_VLI_MAX || (coder->file_size & 3))
			return LZMA_DATA_ERROR;

		// Start looking for Stream Padding and Stream Footer
		// at the end of the file.
		coder->file_target_pos = coder->file_size;
		FALLTHROUGH;

	case SEQ_PADDING_SEEK:
		coder->sequence = SEQ_PADDING_DECODE;
		return_if_error(reverse_seek(
				coder, in_start, in_pos, in_size));
		FALLTHROUGH;

	case SEQ_PADDING_DECODE: {
		// Copy to coder->temp first. This keeps the code simpler if
		// the application only provides input a few bytes at a time.
		if (fill_temp(coder, in, in_pos, in_size))
			return LZMA_OK;

		// Scan the buffer backwards to get the size of the
		// Stream Padding field (if any).
		const size_t new_padding = get_padding_size(
				coder->temp, coder->temp_size);
		coder->stream_padding += new_padding;

		// Set the target position to the beginning of Stream Padding
		// that has been observed so far. If all Stream Padding has
		// been seen, then the target position will be at the end
		// of the Stream Footer field.
		coder->file_target_pos -= new_padding;

		if (new_padding == coder->temp_size) {
			// The whole buffer was padding. Seek backwards in
			// the file to get more input.
			coder->sequence = SEQ_PADDING_SEEK;
			break;
		}

		// Size of Stream Padding must be a multiple of 4 bytes.
		if (coder->stream_padding & 3)
			return LZMA_DATA_ERROR;

		coder->sequence = SEQ_FOOTER;

		// Calculate the amount of non-padding data in coder->temp.
		coder->temp_size -= new_padding;
		coder->temp_pos = coder->temp_size;

		// We can avoid an external seek if the whole Stream Footer
		// is already in coder->temp. In that case SEQ_FOOTER won't
		// read more input and will find the Stream Footer from
		// coder->temp[coder->temp_size - LZMA_STREAM_HEADER_SIZE].
		//
		// Otherwise we will need to seek. The seeking is done so
		// that Stream Footer will be at the end of coder->temp.
		// This way it's likely that we also get a complete Index
		// field into coder->temp without needing a separate seek
		// for that (unless the Index field is big).
		if (coder->temp_size < LZMA_STREAM_HEADER_SIZE)
			return_if_error(reverse_seek(
					coder, in_start, in_pos, in_size));

		FALLTHROUGH;
	}

	case SEQ_FOOTER:
		// Copy the Stream Footer field into coder->temp.
		// If Stream Footer was already available in coder->temp
		// in SEQ_PADDING_DECODE, then this does nothing.
		if (fill_temp(coder, in, in_pos, in_size))
			return LZMA_OK;

		// Make coder->file_target_pos and coder->temp_size point
		// to the beginning of Stream Footer and thus to the end
		// of the Index field. coder->temp_pos will be updated
		// a bit later.
		coder->file_target_pos -= LZMA_STREAM_HEADER_SIZE;
		coder->temp_size -= LZMA_STREAM_HEADER_SIZE;

		// Decode Stream Footer.
		return_if_error(hide_format_error(lzma_stream_footer_decode(
				&coder->footer_flags,
				coder->temp + coder->temp_size)));

		// Check that we won't seek past the beginning of the file.
		//
		// LZMA_STREAM_HEADER_SIZE is added because there must be
		// space for Stream Header too even though we won't seek
		// there before decoding the Index field.
		//
		// There's no risk of integer overflow here because
		// Backward Size cannot be greater than 2^34.
		if (coder->file_target_pos < coder->footer_flags.backward_size
				+ LZMA_STREAM_HEADER_SIZE)
			return LZMA_DATA_ERROR;

		// Set the target position to the beginning of the Index field.
		coder->file_target_pos -= coder->footer_flags.backward_size;
		coder->sequence = SEQ_INDEX_INIT;

		// We can avoid an external seek if the whole Index field is
		// already available in coder->temp.
		if (coder->temp_size >= coder->footer_flags.backward_size) {
			// Set coder->temp_pos to point to the beginning
			// of the Index.
			coder->temp_pos = coder->temp_size
					- coder->footer_flags.backward_size;
		} else {
			// These are set to zero to indicate that there's no
			// useful data (Index or anything else) in coder->temp.
			coder->temp_pos = 0;
			coder->temp_size = 0;

			// Seek to the beginning of the Index field.
			if (seek_to_pos(coder, coder->file_target_pos,
					in_start, in_pos, in_size))
				return LZMA_SEEK_NEEDED;
		}

		FALLTHROUGH;

	case SEQ_INDEX_INIT: {
		// Calculate the amount of memory already used by the earlier
		// Indexes so that we know how big memory limit to pass to
		// the Index decoder.
		//
		// NOTE: When there are multiple Streams, the separate
		// lzma_index structures can use more RAM (as measured by
		// lzma_index_memused()) than the final combined lzma_index.
		// Thus memlimit may need to be slightly higher than the final
		// calculated memory usage will be. This is perhaps a bit
		// confusing to the application, but I think it shouldn't
		// cause problems in practice.
		uint64_t memused = 0;
		if (coder->combined_index != NULL) {
			memused = lzma_index_memused(coder->combined_index);
			assert(memused <= coder->memlimit);
			if (memused > coder->memlimit) // Extra sanity check
				return LZMA_PROG_ERROR;
		}

		// Initialize the Index decoder.
		return_if_error(lzma_index_decoder_init(
				&coder->index_decoder, allocator,
				&coder->this_index,
				coder->memlimit - memused));

		coder->index_remaining = coder->footer_flags.backward_size;
		coder->sequence = SEQ_INDEX_DECODE;
		FALLTHROUGH;
	}

	case SEQ_INDEX_DECODE: {
		// Decode (a part of) the Index. If the whole Index is already
		// in coder->temp, read it from there. Otherwise read from
		// in[*in_pos] onwards. Note that index_decode() updates
		// coder->index_remaining and optionally coder->file_cur_pos.
		lzma_ret ret;
		if (coder->temp_size != 0) {
			assert(coder->temp_size - coder->temp_pos
					== coder->index_remaining);
			ret = decode_index(coder, allocator, coder->temp,
					&coder->temp_pos, coder->temp_size,
					false);
		} else {
			// Don't give the decoder more input than the known
			// remaining size of the Index field.
			size_t in_stop = in_size;
			if (in_size - *in_pos > coder->index_remaining)
				in_stop = *in_pos
					+ (size_t)(coder->index_remaining);

			ret = decode_index(coder, allocator,
					in, in_pos, in_stop, true);
		}

		switch (ret) {
		case LZMA_OK:
			// If the Index docoder asks for more input when we
			// have already given it as much input as Backward Size
			// indicated, the file is invalid.
			if (coder->index_remaining == 0)
				return LZMA_DATA_ERROR;

			// We cannot get here if we were reading Index from
			// coder->temp because when reading from coder->temp
			// we give the Index decoder exactly
			// coder->index_remaining bytes of input.
			assert(coder->temp_size == 0);

			return LZMA_OK;

		case LZMA_STREAM_END:
			// If the decoding seems to be successful, check also
			// that the Index decoder consumed as much input as
			// indicated by the Backward Size field.
			if (coder->index_remaining != 0)
				return LZMA_DATA_ERROR;

			break;

		default:
			return ret;
		}

		// Calculate how much the Index tells us to seek backwards
		// (relative to the beginning of the Index): Total size of
		// all Blocks plus the size of the Stream Header field.
		// No integer overflow here because lzma_index_total_size()
		// cannot return a value greater than LZMA_VLI_MAX.
		const uint64_t seek_amount
				= lzma_index_total_size(coder->this_index)
					+ LZMA_STREAM_HEADER_SIZE;

		// Check that Index is sane in sense that seek_amount won't
		// make us seek past the beginning of the file when locating
		// the Stream Header.
		//
		// coder->file_target_pos still points to the beginning of
		// the Index field.
		if (coder->file_target_pos < seek_amount)
			return LZMA_DATA_ERROR;

		// Set the target to the beginning of Stream Header.
		coder->file_target_pos -= seek_amount;

		if (coder->file_target_pos == 0) {
			// We would seek to the beginning of the file, but
			// since we already decoded that Stream Header in
			// SEQ_MAGIC_BYTES, we can use the cached value from
			// coder->first_header_flags to avoid the seek.
			coder->header_flags = coder->first_header_flags;
			coder->sequence = SEQ_HEADER_COMPARE;
			break;
		}

		coder->sequence = SEQ_HEADER_DECODE;

		// Make coder->file_target_pos point to the end of
		// the Stream Header field.
		coder->file_target_pos += LZMA_STREAM_HEADER_SIZE;

		// If coder->temp_size is non-zero, it points to the end
		// of the Index field. Then the beginning of the Index
		// field is at coder->temp[coder->temp_size
		// - coder->footer_flags.backward_size].
		assert(coder->temp_size == 0 || coder->temp_size
				>= coder->footer_flags.backward_size);

		// If coder->temp contained the whole Index, see if it has
		// enough data to contain also the Stream Header. If so,
		// we avoid an external seek.
		//
		// NOTE: This can happen only with small .xz files and only
		// for the non-first Stream as the Stream Flags of the first
		// Stream are cached and already handled a few lines above.
		// So this isn't as useful as the other seek-avoidance cases.
		if (coder->temp_size != 0 && coder->temp_size
				- coder->footer_flags.backward_size
				>= seek_amount) {
			// Make temp_pos and temp_size point to the *end* of
			// Stream Header so that SEQ_HEADER_DECODE will find
			// the start of Stream Header from coder->temp[
			// coder->temp_size - LZMA_STREAM_HEADER_SIZE].
			coder->temp_pos = coder->temp_size
					- coder->footer_flags.backward_size
					- seek_amount
					+ LZMA_STREAM_HEADER_SIZE;
			coder->temp_size = coder->temp_pos;
		} else {
			// Seek so that Stream Header will be at the end of
			// coder->temp. With typical multi-Stream files we
			// will usually also get the Stream Footer and Index
			// of the *previous* Stream in coder->temp and thus
			// won't need a separate seek for them.
			return_if_error(reverse_seek(coder,
					in_start, in_pos, in_size));
		}

		FALLTHROUGH;
	}

	case SEQ_HEADER_DECODE:
		// Copy the Stream Header field into coder->temp.
		// If Stream Header was already available in coder->temp
		// in SEQ_INDEX_DECODE, then this does nothing.
		if (fill_temp(coder, in, in_pos, in_size))
			return LZMA_OK;

		// Make all these point to the beginning of Stream Header.
		coder->file_target_pos -= LZMA_STREAM_HEADER_SIZE;
		coder->temp_size -= LZMA_STREAM_HEADER_SIZE;
		coder->temp_pos = coder->temp_size;

		// Decode the Stream Header.
		return_if_error(hide_format_error(lzma_stream_header_decode(
				&coder->header_flags,
				coder->temp + coder->temp_size)));

		coder->sequence = SEQ_HEADER_COMPARE;
		FALLTHROUGH;

	case SEQ_HEADER_COMPARE:
		// Compare Stream Header against Stream Footer. They must
		// match.
		return_if_error(lzma_stream_flags_compare(
				&coder->header_flags, &coder->footer_flags));

		// Store the decoded Stream Flags into the Index. Use the
		// Footer Flags because it contains Backward Size, although
		// it shouldn't matter in practice.
		if (lzma_index_stream_flags(coder->this_index,
				&coder->footer_flags) != LZMA_OK)
			return LZMA_PROG_ERROR;

		// Store also the size of the Stream Padding field. It is
		// needed to calculate the offsets of the Streams correctly.
		if (lzma_index_stream_padding(coder->this_index,
				coder->stream_padding) != LZMA_OK)
			return LZMA_PROG_ERROR;

		// Reset it so that it's ready for the next Stream.
		coder->stream_padding = 0;

		// Append the earlier decoded Indexes after this_index.
		if (coder->combined_index != NULL)
			return_if_error(lzma_index_cat(coder->this_index,
					coder->combined_index, allocator));

		coder->combined_index = coder->this_index;
		coder->this_index = NULL;

		// If the whole file was decoded, tell the caller that we
		// are finished.
		if (coder->file_target_pos == 0) {
			// The combined index must indicate the same file
			// size as was told to us at initialization.
			assert(lzma_index_file_size(coder->combined_index)
					== coder->file_size);

			// Make the combined index available to
			// the application.
			*coder->dest_index = coder->combined_index;
			coder->combined_index = NULL;

			// Mark the input buffer as used since we may have
			// done internal seeking and thus don't know how
			// many input bytes were actually used. This way
			// lzma_stream.total_in gets a slightly better
			// estimate of the amount of input used.
			*in_pos = in_size;
			return LZMA_STREAM_END;
		}

		// We didn't hit the beginning of the file yet, so continue
		// reading backwards in the file. If we have unprocessed
		// data in coder->temp, use it before requesting more data
		// from the application.
		//
		// coder->file_target_pos, coder->temp_size, and
		// coder->temp_pos all point to the beginning of Stream Header
		// and thus the end of the previous Stream in the file.
		coder->sequence = coder->temp_size > 0
				? SEQ_PADDING_DECODE : SEQ_PADDING_SEEK;
		break;

	default:
		assert(0);
		return LZMA_PROG_ERROR;
	}
}


static lzma_ret
file_info_decoder_memconfig(void *coder_ptr, uint64_t *memusage,
		uint64_t *old_memlimit, uint64_t new_memlimit)
{
	lzma_file_info_coder *coder = coder_ptr;

	// The memory usage calculation comes from three things:
	//
	// (1) The Indexes that have already been decoded and processed into
	//     coder->combined_index.
	//
	// (2) The latest Index in coder->this_index that has been decoded but
	//     not yet put into coder->combined_index.
	//
	// (3) The latest Index that we have started decoding but haven't
	//     finished and thus isn't available in coder->this_index yet.
	//     Memory usage and limit information needs to be communicated
	//     from/to coder->index_decoder.
	//
	// Care has to be taken to not do both (2) and (3) when calculating
	// the memory usage.
	uint64_t combined_index_memusage = 0;
	uint64_t this_index_memusage = 0;

	// (1) If we have already successfully decoded one or more Indexes,
	// get their memory usage.
	if (coder->combined_index != NULL)
		combined_index_memusage = lzma_index_memused(
				coder->combined_index);

	// Choose between (2), (3), or neither.
	if (coder->this_index != NULL) {
		// (2) The latest Index is available. Use its memory usage.
		this_index_memusage = lzma_index_memused(coder->this_index);

	} else if (coder->sequence == SEQ_INDEX_DECODE) {
		// (3) The Index decoder is activate and hasn't yet stored
		// the new index in coder->this_index. Get the memory usage
		// information from the Index decoder.
		//
		// NOTE: If the Index decoder doesn't yet know how much memory
		// it will eventually need, it will return a tiny value here.
		uint64_t dummy;
		if (coder->index_decoder.memconfig(coder->index_decoder.coder,
					&this_index_memusage, &dummy, 0)
				!= LZMA_OK) {
			assert(0);
			return LZMA_PROG_ERROR;
		}
	}

	// Now we know the total memory usage/requirement. If we had neither
	// old Indexes nor a new Index, this will be zero which isn't
	// acceptable as lzma_memusage() has to return non-zero on success
	// and even with an empty .xz file we will end up with a lzma_index
	// that takes some memory.
	*memusage = combined_index_memusage + this_index_memusage;
	if (*memusage == 0)
		*memusage = lzma_index_memusage(1, 0);

	*old_memlimit = coder->memlimit;

	// If requested, set a new memory usage limit.
	if (new_memlimit != 0) {
		if (new_memlimit < *memusage)
			return LZMA_MEMLIMIT_ERROR;

		// In the condition (3) we need to tell the Index decoder
		// its new memory usage limit.
		if (coder->this_index == NULL
				&& coder->sequence == SEQ_INDEX_DECODE) {
			const uint64_t idec_new_memlimit = new_memlimit
					- combined_index_memusage;

			assert(this_index_memusage > 0);
			assert(idec_new_memlimit > 0);

			uint64_t dummy1;
			uint64_t dummy2;

			if (coder->index_decoder.memconfig(
					coder->index_decoder.coder,
					&dummy1, &dummy2, idec_new_memlimit)
					!= LZMA_OK) {
				assert(0);
				return LZMA_PROG_ERROR;
			}
		}

		coder->memlimit = new_memlimit;
	}

	return LZMA_OK;
}


static void
file_info_decoder_end(void *coder_ptr, const lzma_allocator *allocator)
{
	lzma_file_info_coder *coder = coder_ptr;

	lzma_next_end(&coder->index_decoder, allocator);
	lzma_index_end(coder->this_index, allocator);
	lzma_index_end(coder->combined_index, allocator);

	lzma_free(coder, allocator);
	return;
}


static lzma_ret
lzma_file_info_decoder_init(lzma_next_coder *next,
		const lzma_allocator *allocator, uint64_t *seek_pos,
		lzma_index **dest_index,
		uint64_t memlimit, uint64_t file_size)
{
	lzma_next_coder_init(&lzma_file_info_decoder_init, next, allocator);

	if (dest_index == NULL)
		return LZMA_PROG_ERROR;

	lzma_file_info_coder *coder = next->coder;
	if (coder == NULL) {
		coder = lzma_alloc(sizeof(lzma_file_info_coder), allocator);
		if (coder == NULL)
			return LZMA_MEM_ERROR;

		next->coder = coder;
		next->code = &file_info_decode;
		next->end = &file_info_decoder_end;
		next->memconfig = &file_info_decoder_memconfig;

		coder->index_decoder = LZMA_NEXT_CODER_INIT;
		coder->this_index = NULL;
		coder->combined_index = NULL;
	}

	coder->sequence = SEQ_MAGIC_BYTES;
	coder->file_cur_pos = 0;
	coder->file_target_pos = 0;
	coder->file_size = file_size;

	lzma_index_end(coder->this_index, allocator);
	coder->this_index = NULL;

	lzma_index_end(coder->combined_index, allocator);
	coder->combined_index = NULL;

	coder->stream_padding = 0;

	coder->dest_index = dest_index;
	coder->external_seek_pos = seek_pos;

	// If memlimit is 0, make it 1 to ensure that lzma_memlimit_get()
	// won't return 0 (which would indicate an error).
	coder->memlimit = my_max(1, memlimit);

	// Prepare these for reading the first Stream Header into coder->temp.
	coder->temp_pos = 0;
	coder->temp_size = LZMA_STREAM_HEADER_SIZE;

	return LZMA_OK;
}


extern LZMA_API(lzma_ret)
lzma_file_info_decoder(lzma_stream *strm, lzma_index **dest_index,
		uint64_t memlimit, uint64_t file_size)
{
	lzma_next_strm_init(lzma_file_info_decoder_init, strm, &strm->seek_pos,
			dest_index, memlimit, file_size);

	// We allow LZMA_FINISH in addition to LZMA_RUN for convenience.
	// lzma_code() is able to handle the LZMA_FINISH + LZMA_SEEK_NEEDED
	// combination in a sane way. Applications still need to be careful
	// if they use LZMA_FINISH so that they remember to reset it back
	// to LZMA_RUN after seeking if needed.
	strm->internal->supported_actions[LZMA_RUN] = true;
	strm->internal->supported_actions[LZMA_FINISH] = true;

	return LZMA_OK;
}
