stream_buffer_encoder.c revision 223935
118334Speter///////////////////////////////////////////////////////////////////////////////
218334Speter//
318334Speter/// \file       stream_buffer_encoder.c
418334Speter/// \brief      Single-call .xz Stream encoder
5132718Skan//
6169689Skan//  Author:     Lasse Collin
750397Sobrien//
8132718Skan//  This file has been put into the public domain.
990075Sobrien//  You can do whatever you want with this file.
1018334Speter//
1190075Sobrien///////////////////////////////////////////////////////////////////////////////
1290075Sobrien
1390075Sobrien#include "index.h"
1490075Sobrien
1518334Speter
1690075Sobrien/// Maximum size of Index that has exactly one Record.
1790075Sobrien/// Index Indicator + Number of Records + Record + CRC32 rounded up to
1890075Sobrien/// the next multiple of four.
1990075Sobrien#define INDEX_BOUND ((1 + 1 + 2 * LZMA_VLI_BYTES_MAX + 4 + 3) & ~3)
2018334Speter
2118334Speter/// Stream Header, Stream Footer, and Index
2290075Sobrien#define HEADERS_BOUND (2 * LZMA_STREAM_HEADER_SIZE + INDEX_BOUND)
23169689Skan
24169689Skan
2518334Speterextern LZMA_API(size_t)
2618334Speterlzma_stream_buffer_bound(size_t uncompressed_size)
2718334Speter{
2818334Speter	// Get the maximum possible size of a Block.
2918334Speter	const size_t block_bound = lzma_block_buffer_bound(uncompressed_size);
3018334Speter	if (block_bound == 0)
3118334Speter		return 0;
3218334Speter
3318334Speter	// Catch the possible integer overflow and also prevent the size of
3418334Speter	// the Stream exceeding LZMA_VLI_MAX (theoretically possible on
3518334Speter	// 64-bit systems).
3618334Speter	if (my_min(SIZE_MAX, LZMA_VLI_MAX) - block_bound < HEADERS_BOUND)
3718334Speter		return 0;
3818334Speter
3918334Speter	return block_bound + HEADERS_BOUND;
4018334Speter}
4118334Speter
4218334Speter
4318334Speterextern LZMA_API(lzma_ret)
4418334Speterlzma_stream_buffer_encode(lzma_filter *filters, lzma_check check,
4518334Speter		lzma_allocator *allocator, const uint8_t *in, size_t in_size,
4618334Speter		uint8_t *out, size_t *out_pos_ptr, size_t out_size)
4718334Speter{
4818334Speter	// Sanity checks
4918334Speter	if (filters == NULL || (unsigned int)(check) > LZMA_CHECK_ID_MAX
5018334Speter			|| (in == NULL && in_size != 0) || out == NULL
5118334Speter			|| out_pos_ptr == NULL || *out_pos_ptr > out_size)
5218334Speter		return LZMA_PROG_ERROR;
5318334Speter
5418334Speter	if (!lzma_check_is_supported(check))
5518334Speter		return LZMA_UNSUPPORTED_CHECK;
5618334Speter
5718334Speter	// Note for the paranoids: Index encoder prevents the Stream from
5818334Speter	// getting too big and still being accepted with LZMA_OK, and Block
5918334Speter	// encoder catches if the input is too big. So we don't need to
6018334Speter	// separately check if the buffers are too big.
6118334Speter
6218334Speter	// Use a local copy. We update *out_pos_ptr only if everything
6318334Speter	// succeeds.
6418334Speter	size_t out_pos = *out_pos_ptr;
6518334Speter
6618334Speter	// Check that there's enough space for both Stream Header and
6718334Speter	// Stream Footer.
6818334Speter	if (out_size - out_pos <= 2 * LZMA_STREAM_HEADER_SIZE)
6918334Speter		return LZMA_BUF_ERROR;
7018334Speter
7118334Speter	// Reserve space for Stream Footer so we don't need to check for
7218334Speter	// available space again before encoding Stream Footer.
7318334Speter	out_size -= LZMA_STREAM_HEADER_SIZE;
7418334Speter
7518334Speter	// Encode the Stream Header.
7618334Speter	lzma_stream_flags stream_flags = {
7718334Speter		.version = 0,
7818334Speter		.check = check,
7918334Speter	};
8018334Speter
8118334Speter	if (lzma_stream_header_encode(&stream_flags, out + out_pos)
8218334Speter			!= LZMA_OK)
8318334Speter		return LZMA_PROG_ERROR;
8418334Speter
8518334Speter	out_pos += LZMA_STREAM_HEADER_SIZE;
8618334Speter
8718334Speter	// Encode a Block but only if there is at least one byte of input.
8818334Speter	lzma_block block = {
8918334Speter		.version = 0,
9018334Speter		.check = check,
9118334Speter		.filters = filters,
9218334Speter	};
9318334Speter
9418334Speter	if (in_size > 0)
9518334Speter		return_if_error(lzma_block_buffer_encode(&block, allocator,
9618334Speter				in, in_size, out, &out_pos, out_size));
9718334Speter
9818334Speter	// Index
9918334Speter	{
10018334Speter		// Create an Index. It will have one Record if there was
101132718Skan		// at least one byte of input to encode. Otherwise the
10218334Speter		// Index will be empty.
103132718Skan		lzma_index *i = lzma_index_init(allocator);
104132718Skan		if (i == NULL)
105132718Skan			return LZMA_MEM_ERROR;
106132718Skan
107132718Skan		lzma_ret ret = LZMA_OK;
108132718Skan
109132718Skan		if (in_size > 0)
110132718Skan			ret = lzma_index_append(i, allocator,
111132718Skan					lzma_block_unpadded_size(&block),
112132718Skan					block.uncompressed_size);
113132718Skan
114132718Skan		// If adding the Record was successful, encode the Index
115132718Skan		// and get its size which will be stored into Stream Footer.
116132718Skan		if (ret == LZMA_OK) {
117132718Skan			ret = lzma_index_buffer_encode(
118132718Skan					i, out, &out_pos, out_size);
119132718Skan
120132718Skan			stream_flags.backward_size = lzma_index_size(i);
121132718Skan		}
122132718Skan
123132718Skan		lzma_index_end(i, allocator);
124132718Skan
125132718Skan		if (ret != LZMA_OK)
12618334Speter			return ret;
12718334Speter	}
12818334Speter
12918334Speter	// Stream Footer. We have already reserved space for this.
13018334Speter	if (lzma_stream_footer_encode(&stream_flags, out + out_pos)
13118334Speter			!= LZMA_OK)
13218334Speter		return LZMA_PROG_ERROR;
133132718Skan
134132718Skan	out_pos += LZMA_STREAM_HEADER_SIZE;
135132718Skan
136132718Skan	// Everything went fine, make the new output position available
137132718Skan	// to the application.
138132718Skan	*out_pos_ptr = out_pos;
13918334Speter	return LZMA_OK;
14018334Speter}
14118334Speter