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