xref: /freebsd/contrib/xz/src/liblzma/common/stream_buffer_encoder.c (revision 3b35e7ee8de9b0260149a2b77e87a2b9c7a36244)
1*3b35e7eeSXin LI // SPDX-License-Identifier: 0BSD
2*3b35e7eeSXin LI 
381ad8388SMartin Matuska ///////////////////////////////////////////////////////////////////////////////
481ad8388SMartin Matuska //
581ad8388SMartin Matuska /// \file       stream_buffer_encoder.c
681ad8388SMartin Matuska /// \brief      Single-call .xz Stream encoder
781ad8388SMartin Matuska //
881ad8388SMartin Matuska //  Author:     Lasse Collin
981ad8388SMartin Matuska //
1081ad8388SMartin Matuska ///////////////////////////////////////////////////////////////////////////////
1181ad8388SMartin Matuska 
12047153b4SXin LI #include "common.h"
1381ad8388SMartin Matuska #include "index.h"
1481ad8388SMartin Matuska 
1581ad8388SMartin Matuska 
1681ad8388SMartin Matuska /// Maximum size of Index that has exactly one Record.
1781ad8388SMartin Matuska /// Index Indicator + Number of Records + Record + CRC32 rounded up to
1881ad8388SMartin Matuska /// the next multiple of four.
1981ad8388SMartin Matuska #define INDEX_BOUND ((1 + 1 + 2 * LZMA_VLI_BYTES_MAX + 4 + 3) & ~3)
2081ad8388SMartin Matuska 
2181ad8388SMartin Matuska /// Stream Header, Stream Footer, and Index
2281ad8388SMartin Matuska #define HEADERS_BOUND (2 * LZMA_STREAM_HEADER_SIZE + INDEX_BOUND)
2381ad8388SMartin Matuska 
2481ad8388SMartin Matuska 
2581ad8388SMartin Matuska extern LZMA_API(size_t)
2681ad8388SMartin Matuska lzma_stream_buffer_bound(size_t uncompressed_size)
2781ad8388SMartin Matuska {
2881ad8388SMartin Matuska 	// Get the maximum possible size of a Block.
2981ad8388SMartin Matuska 	const size_t block_bound = lzma_block_buffer_bound(uncompressed_size);
3081ad8388SMartin Matuska 	if (block_bound == 0)
3181ad8388SMartin Matuska 		return 0;
3281ad8388SMartin Matuska 
3381ad8388SMartin Matuska 	// Catch the possible integer overflow and also prevent the size of
3481ad8388SMartin Matuska 	// the Stream exceeding LZMA_VLI_MAX (theoretically possible on
3581ad8388SMartin Matuska 	// 64-bit systems).
36e0f0e66dSMartin Matuska 	if (my_min(SIZE_MAX, LZMA_VLI_MAX) - block_bound < HEADERS_BOUND)
3781ad8388SMartin Matuska 		return 0;
3881ad8388SMartin Matuska 
3981ad8388SMartin Matuska 	return block_bound + HEADERS_BOUND;
4081ad8388SMartin Matuska }
4181ad8388SMartin Matuska 
4281ad8388SMartin Matuska 
4381ad8388SMartin Matuska extern LZMA_API(lzma_ret)
4481ad8388SMartin Matuska lzma_stream_buffer_encode(lzma_filter *filters, lzma_check check,
4553200025SRui Paulo 		const lzma_allocator *allocator,
4653200025SRui Paulo 		const uint8_t *in, size_t in_size,
4781ad8388SMartin Matuska 		uint8_t *out, size_t *out_pos_ptr, size_t out_size)
4881ad8388SMartin Matuska {
4981ad8388SMartin Matuska 	// Sanity checks
5081ad8388SMartin Matuska 	if (filters == NULL || (unsigned int)(check) > LZMA_CHECK_ID_MAX
5181ad8388SMartin Matuska 			|| (in == NULL && in_size != 0) || out == NULL
5281ad8388SMartin Matuska 			|| out_pos_ptr == NULL || *out_pos_ptr > out_size)
5381ad8388SMartin Matuska 		return LZMA_PROG_ERROR;
5481ad8388SMartin Matuska 
55e24134bcSMartin Matuska 	if (!lzma_check_is_supported(check))
56e24134bcSMartin Matuska 		return LZMA_UNSUPPORTED_CHECK;
57e24134bcSMartin Matuska 
5881ad8388SMartin Matuska 	// Note for the paranoids: Index encoder prevents the Stream from
5981ad8388SMartin Matuska 	// getting too big and still being accepted with LZMA_OK, and Block
6081ad8388SMartin Matuska 	// encoder catches if the input is too big. So we don't need to
6181ad8388SMartin Matuska 	// separately check if the buffers are too big.
6281ad8388SMartin Matuska 
6381ad8388SMartin Matuska 	// Use a local copy. We update *out_pos_ptr only if everything
6481ad8388SMartin Matuska 	// succeeds.
6581ad8388SMartin Matuska 	size_t out_pos = *out_pos_ptr;
6681ad8388SMartin Matuska 
6781ad8388SMartin Matuska 	// Check that there's enough space for both Stream Header and
6881ad8388SMartin Matuska 	// Stream Footer.
6981ad8388SMartin Matuska 	if (out_size - out_pos <= 2 * LZMA_STREAM_HEADER_SIZE)
7081ad8388SMartin Matuska 		return LZMA_BUF_ERROR;
7181ad8388SMartin Matuska 
7281ad8388SMartin Matuska 	// Reserve space for Stream Footer so we don't need to check for
7381ad8388SMartin Matuska 	// available space again before encoding Stream Footer.
7481ad8388SMartin Matuska 	out_size -= LZMA_STREAM_HEADER_SIZE;
7581ad8388SMartin Matuska 
7681ad8388SMartin Matuska 	// Encode the Stream Header.
7781ad8388SMartin Matuska 	lzma_stream_flags stream_flags = {
7881ad8388SMartin Matuska 		.version = 0,
7981ad8388SMartin Matuska 		.check = check,
8081ad8388SMartin Matuska 	};
8181ad8388SMartin Matuska 
8281ad8388SMartin Matuska 	if (lzma_stream_header_encode(&stream_flags, out + out_pos)
8381ad8388SMartin Matuska 			!= LZMA_OK)
8481ad8388SMartin Matuska 		return LZMA_PROG_ERROR;
8581ad8388SMartin Matuska 
8681ad8388SMartin Matuska 	out_pos += LZMA_STREAM_HEADER_SIZE;
8781ad8388SMartin Matuska 
88e24134bcSMartin Matuska 	// Encode a Block but only if there is at least one byte of input.
8981ad8388SMartin Matuska 	lzma_block block = {
9081ad8388SMartin Matuska 		.version = 0,
9181ad8388SMartin Matuska 		.check = check,
9281ad8388SMartin Matuska 		.filters = filters,
9381ad8388SMartin Matuska 	};
9481ad8388SMartin Matuska 
95e24134bcSMartin Matuska 	if (in_size > 0)
9681ad8388SMartin Matuska 		return_if_error(lzma_block_buffer_encode(&block, allocator,
9781ad8388SMartin Matuska 				in, in_size, out, &out_pos, out_size));
9881ad8388SMartin Matuska 
9981ad8388SMartin Matuska 	// Index
10081ad8388SMartin Matuska 	{
101e24134bcSMartin Matuska 		// Create an Index. It will have one Record if there was
102e24134bcSMartin Matuska 		// at least one byte of input to encode. Otherwise the
103e24134bcSMartin Matuska 		// Index will be empty.
10481ad8388SMartin Matuska 		lzma_index *i = lzma_index_init(allocator);
10581ad8388SMartin Matuska 		if (i == NULL)
10681ad8388SMartin Matuska 			return LZMA_MEM_ERROR;
10781ad8388SMartin Matuska 
108e24134bcSMartin Matuska 		lzma_ret ret = LZMA_OK;
109e24134bcSMartin Matuska 
110e24134bcSMartin Matuska 		if (in_size > 0)
111e24134bcSMartin Matuska 			ret = lzma_index_append(i, allocator,
11281ad8388SMartin Matuska 					lzma_block_unpadded_size(&block),
11381ad8388SMartin Matuska 					block.uncompressed_size);
11481ad8388SMartin Matuska 
11581ad8388SMartin Matuska 		// If adding the Record was successful, encode the Index
11681ad8388SMartin Matuska 		// and get its size which will be stored into Stream Footer.
11781ad8388SMartin Matuska 		if (ret == LZMA_OK) {
11881ad8388SMartin Matuska 			ret = lzma_index_buffer_encode(
11981ad8388SMartin Matuska 					i, out, &out_pos, out_size);
12081ad8388SMartin Matuska 
12181ad8388SMartin Matuska 			stream_flags.backward_size = lzma_index_size(i);
12281ad8388SMartin Matuska 		}
12381ad8388SMartin Matuska 
12481ad8388SMartin Matuska 		lzma_index_end(i, allocator);
12581ad8388SMartin Matuska 
12681ad8388SMartin Matuska 		if (ret != LZMA_OK)
12781ad8388SMartin Matuska 			return ret;
12881ad8388SMartin Matuska 	}
12981ad8388SMartin Matuska 
13081ad8388SMartin Matuska 	// Stream Footer. We have already reserved space for this.
13181ad8388SMartin Matuska 	if (lzma_stream_footer_encode(&stream_flags, out + out_pos)
13281ad8388SMartin Matuska 			!= LZMA_OK)
13381ad8388SMartin Matuska 		return LZMA_PROG_ERROR;
13481ad8388SMartin Matuska 
13581ad8388SMartin Matuska 	out_pos += LZMA_STREAM_HEADER_SIZE;
13681ad8388SMartin Matuska 
13781ad8388SMartin Matuska 	// Everything went fine, make the new output position available
13881ad8388SMartin Matuska 	// to the application.
13981ad8388SMartin Matuska 	*out_pos_ptr = out_pos;
14081ad8388SMartin Matuska 	return LZMA_OK;
14181ad8388SMartin Matuska }
142