block_buffer_encoder.c   block_buffer_encoder.c 
skipping to change at line 13 skipping to change at line 13
/// \file block_buffer_encoder.c /// \file block_buffer_encoder.c
/// \brief Single-call .xz Block encoder /// \brief Single-call .xz Block encoder
// //
// Author: Lasse Collin // Author: Lasse Collin
// //
// This file has been put into the public domain. // This file has been put into the public domain.
// You can do whatever you want with this file. // You can do whatever you want with this file.
// //
/////////////////////////////////////////////////////////////////////////// //// /////////////////////////////////////////////////////////////////////////// ////
#include "block_buffer_encoder.h"
#include "block_encoder.h" #include "block_encoder.h"
#include "filter_encoder.h" #include "filter_encoder.h"
#include "lzma2_encoder.h" #include "lzma2_encoder.h"
#include "check.h" #include "check.h"
/// Estimate the maximum size of the Block Header and Check fields for /// Estimate the maximum size of the Block Header and Check fields for
/// a Block that uses LZMA2 uncompressed chunks. We could use /// a Block that uses LZMA2 uncompressed chunks. We could use
/// lzma_block_header_size() but this is simpler. /// lzma_block_header_size() but this is simpler.
/// ///
/// Block Header Size + Block Flags + Compressed Size /// Block Header Size + Block Flags + Compressed Size
/// + Uncompressed Size + Filter Flags for LZMA2 + CRC32 + Check /// + Uncompressed Size + Filter Flags for LZMA2 + CRC32 + Check
/// and round up to the next multiple of four to take Header Padding /// and round up to the next multiple of four to take Header Padding
/// into account. /// into account.
#define HEADERS_BOUND ((1 + 1 + 2 * LZMA_VLI_BYTES_MAX + 3 + 4 \ #define HEADERS_BOUND ((1 + 1 + 2 * LZMA_VLI_BYTES_MAX + 3 + 4 \
+ LZMA_CHECK_SIZE_MAX + 3) & ~3) + LZMA_CHECK_SIZE_MAX + 3) & ~3)
static lzma_vli static uint64_t
lzma2_bound(lzma_vli uncompressed_size) lzma2_bound(uint64_t uncompressed_size)
{ {
// Prevent integer overflow in overhead calculation. // Prevent integer overflow in overhead calculation.
if (uncompressed_size > COMPRESSED_SIZE_MAX) if (uncompressed_size > COMPRESSED_SIZE_MAX)
return 0; return 0;
// Calculate the exact overhead of the LZMA2 headers: Round // Calculate the exact overhead of the LZMA2 headers: Round
// uncompressed_size up to the next multiple of LZMA2_CHUNK_MAX, // uncompressed_size up to the next multiple of LZMA2_CHUNK_MAX,
// multiply by the size of per-chunk header, and add one byte for // multiply by the size of per-chunk header, and add one byte for
// the end marker. // the end marker.
const lzma_vli overhead = ((uncompressed_size + LZMA2_CHUNK_MAX - 1) const uint64_t overhead = ((uncompressed_size + LZMA2_CHUNK_MAX - 1)
/ LZMA2_CHUNK_MAX) / LZMA2_CHUNK_MAX)
* LZMA2_HEADER_UNCOMPRESSED + 1; * LZMA2_HEADER_UNCOMPRESSED + 1;
// Catch the possible integer overflow. // Catch the possible integer overflow.
if (COMPRESSED_SIZE_MAX - overhead < uncompressed_size) if (COMPRESSED_SIZE_MAX - overhead < uncompressed_size)
return 0; return 0;
return uncompressed_size + overhead; return uncompressed_size + overhead;
} }
extern LZMA_API(size_t) extern uint64_t
lzma_block_buffer_bound(size_t uncompressed_size) lzma_block_buffer_bound64(uint64_t uncompressed_size)
{ {
// For now, if the data doesn't compress, we always use uncompressed // If the data doesn't compress, we always use uncompressed
// chunks of LZMA2. In future we may use Subblock filter too, but // LZMA2 chunks.
// but for simplicity we probably will still use the same bound uint64_t lzma2_size = lzma2_bound(uncompressed_size);
// calculation even though Subblock filter would have slightly less
// overhead.
lzma_vli lzma2_size = lzma2_bound(uncompressed_size);
if (lzma2_size == 0) if (lzma2_size == 0)
return 0; return 0;
// Take Block Padding into account. // Take Block Padding into account.
lzma2_size = (lzma2_size + 3) & ~LZMA_VLI_C(3); lzma2_size = (lzma2_size + 3) & ~UINT64_C(3);
#if SIZE_MAX < LZMA_VLI_MAX // No risk of integer overflow because lzma2_bound() already takes
// Catch the possible integer overflow on 32-bit systems. There's no
// overflow on 64-bit systems, because lzma2_bound() already takes
// into account the size of the headers in the Block. // into account the size of the headers in the Block.
if (SIZE_MAX - HEADERS_BOUND < lzma2_size) return HEADERS_BOUND + lzma2_size;
}
extern LZMA_API(size_t)
lzma_block_buffer_bound(size_t uncompressed_size)
{
uint64_t ret = lzma_block_buffer_bound64(uncompressed_size);
#if SIZE_MAX < UINT64_MAX
// Catch the possible integer overflow on 32-bit systems.
if (ret > SIZE_MAX)
return 0; return 0;
#endif #endif
return HEADERS_BOUND + lzma2_size; return ret;
} }
static lzma_ret static lzma_ret
block_encode_uncompressed(lzma_block *block, const uint8_t *in, size_t in_s ize, block_encode_uncompressed(lzma_block *block, const uint8_t *in, size_t in_s ize,
uint8_t *out, size_t *out_pos, size_t out_size) uint8_t *out, size_t *out_pos, size_t out_size)
{ {
// TODO: Figure out if the last filter is LZMA2 or Subblock and use
// that filter to encode the uncompressed chunks.
// Use LZMA2 uncompressed chunks. We wouldn't need a dictionary at // Use LZMA2 uncompressed chunks. We wouldn't need a dictionary at
// all, but LZMA2 always requires a dictionary, so use the minimum // all, but LZMA2 always requires a dictionary, so use the minimum
// value to minimize memory usage of the decoder. // value to minimize memory usage of the decoder.
lzma_options_lzma lzma2 = { lzma_options_lzma lzma2 = {
.dict_size = LZMA_DICT_SIZE_MIN, .dict_size = LZMA_DICT_SIZE_MIN,
}; };
lzma_filter filters[2]; lzma_filter filters[2];
filters[0].id = LZMA_FILTER_LZMA2; filters[0].id = LZMA_FILTER_LZMA2;
filters[0].options = &lzma2; filters[0].options = &lzma2;
skipping to change at line 158 skipping to change at line 161
} }
// End marker // End marker
out[(*out_pos)++] = 0x00; out[(*out_pos)++] = 0x00;
assert(*out_pos <= out_size); assert(*out_pos <= out_size);
return LZMA_OK; return LZMA_OK;
} }
static lzma_ret static lzma_ret
block_encode_normal(lzma_block *block, lzma_allocator *allocator, block_encode_normal(lzma_block *block, const lzma_allocator *allocator,
const uint8_t *in, size_t in_size, const uint8_t *in, size_t in_size,
uint8_t *out, size_t *out_pos, size_t out_size) uint8_t *out, size_t *out_pos, size_t out_size)
{ {
// Find out the size of the Block Header. // Find out the size of the Block Header.
block->compressed_size = lzma2_bound(in_size);
if (block->compressed_size == 0)
return LZMA_DATA_ERROR;
block->uncompressed_size = in_size;
return_if_error(lzma_block_header_size(block)); return_if_error(lzma_block_header_size(block));
// Reserve space for the Block Header and skip it for now. // Reserve space for the Block Header and skip it for now.
if (out_size - *out_pos <= block->header_size) if (out_size - *out_pos <= block->header_size)
return LZMA_BUF_ERROR; return LZMA_BUF_ERROR;
const size_t out_start = *out_pos; const size_t out_start = *out_pos;
*out_pos += block->header_size; *out_pos += block->header_size;
// Limit out_size so that we stop encoding if the output would grow // Limit out_size so that we stop encoding if the output would grow
skipping to change at line 218 skipping to change at line 216
ret = LZMA_BUF_ERROR; ret = LZMA_BUF_ERROR;
} }
// Reset *out_pos if something went wrong. // Reset *out_pos if something went wrong.
if (ret != LZMA_OK) if (ret != LZMA_OK)
*out_pos = out_start; *out_pos = out_start;
return ret; return ret;
} }
extern LZMA_API(lzma_ret) static lzma_ret
lzma_block_buffer_encode(lzma_block *block, lzma_allocator *allocator, block_buffer_encode(lzma_block *block, const lzma_allocator *allocator,
const uint8_t *in, size_t in_size, const uint8_t *in, size_t in_size,
uint8_t *out, size_t *out_pos, size_t out_size) uint8_t *out, size_t *out_pos, size_t out_size,
bool try_to_compress)
{ {
// Validate the arguments. // Validate the arguments.
if (block == NULL || (in == NULL && in_size != 0) || out == NULL if (block == NULL || (in == NULL && in_size != 0) || out == NULL
|| out_pos == NULL || *out_pos > out_size) || out_pos == NULL || *out_pos > out_size)
return LZMA_PROG_ERROR; return LZMA_PROG_ERROR;
// The contents of the structure may depend on the version so // The contents of the structure may depend on the version so
// check the version before validating the contents of *block. // check the version before validating the contents of *block.
if (block->version != 0) if (block->version > 1)
return LZMA_OPTIONS_ERROR; return LZMA_OPTIONS_ERROR;
if ((unsigned int)(block->check) > LZMA_CHECK_ID_MAX if ((unsigned int)(block->check) > LZMA_CHECK_ID_MAX
|| block->filters == NULL) || (try_to_compress && block->filters == NULL))
return LZMA_PROG_ERROR; return LZMA_PROG_ERROR;
if (!lzma_check_is_supported(block->check)) if (!lzma_check_is_supported(block->check))
return LZMA_UNSUPPORTED_CHECK; return LZMA_UNSUPPORTED_CHECK;
// Size of a Block has to be a multiple of four, so limit the size // Size of a Block has to be a multiple of four, so limit the size
// here already. This way we don't need to check it again when addin g // here already. This way we don't need to check it again when addin g
// Block Padding. // Block Padding.
out_size -= (out_size - *out_pos) & 3; out_size -= (out_size - *out_pos) & 3;
// Get the size of the Check field. // Get the size of the Check field.
const size_t check_size = lzma_check_size(block->check); const size_t check_size = lzma_check_size(block->check);
assert(check_size != UINT32_MAX); assert(check_size != UINT32_MAX);
// Reserve space for the Check field. // Reserve space for the Check field.
if (out_size - *out_pos <= check_size) if (out_size - *out_pos <= check_size)
return LZMA_BUF_ERROR; return LZMA_BUF_ERROR;
out_size -= check_size; out_size -= check_size;
// Initialize block->uncompressed_size and calculate the worst-case
// value for block->compressed_size.
block->uncompressed_size = in_size;
block->compressed_size = lzma2_bound(in_size);
if (block->compressed_size == 0)
return LZMA_DATA_ERROR;
// Do the actual compression. // Do the actual compression.
const lzma_ret ret = block_encode_normal(block, allocator, lzma_ret ret = LZMA_BUF_ERROR;
in, in_size, out, out_pos, out_size); if (try_to_compress)
ret = block_encode_normal(block, allocator,
in, in_size, out, out_pos, out_size);
if (ret != LZMA_OK) { if (ret != LZMA_OK) {
// If the error was something else than output buffer // If the error was something else than output buffer
// becoming full, return the error now. // becoming full, return the error now.
if (ret != LZMA_BUF_ERROR) if (ret != LZMA_BUF_ERROR)
return ret; return ret;
// The data was uncompressible (at least with the options // The data was uncompressible (at least with the options
// given to us) or the output buffer was too small. Use the // given to us) or the output buffer was too small. Use the
// uncompressed chunks of LZMA2 to wrap the data into a vali d // uncompressed chunks of LZMA2 to wrap the data into a vali d
// Block. If we haven't been given enough output space, even // Block. If we haven't been given enough output space, even
skipping to change at line 300 skipping to change at line 309
lzma_check_update(&check, block->check, in, in_size); lzma_check_update(&check, block->check, in, in_size);
lzma_check_finish(&check, block->check); lzma_check_finish(&check, block->check);
memcpy(block->raw_check, check.buffer.u8, check_size); memcpy(block->raw_check, check.buffer.u8, check_size);
memcpy(out + *out_pos, check.buffer.u8, check_size); memcpy(out + *out_pos, check.buffer.u8, check_size);
*out_pos += check_size; *out_pos += check_size;
} }
return LZMA_OK; return LZMA_OK;
} }
extern LZMA_API(lzma_ret)
lzma_block_buffer_encode(lzma_block *block, const lzma_allocator *allocator
,
const uint8_t *in, size_t in_size,
uint8_t *out, size_t *out_pos, size_t out_size)
{
return block_buffer_encode(block, allocator,
in, in_size, out, out_pos, out_size, true);
}
extern LZMA_API(lzma_ret)
lzma_block_uncomp_encode(lzma_block *block,
const uint8_t *in, size_t in_size,
uint8_t *out, size_t *out_pos, size_t out_size)
{
// It won't allocate any memory from heap so no need
// for lzma_allocator.
return block_buffer_encode(block, NULL,
in, in_size, out, out_pos, out_size, false);
}
 End of changes. 19 change blocks. 
33 lines changed or deleted 42 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/