extern uint64_t lzma_block_buffer_bound64(uint64_t uncompressed_size) { // If the data doesn't compress, we always use uncompressed // LZMA2 chunks. uint64_t lzma2_size = lzma2_bound(uncompressed_size); if (lzma2_size == 0) return 0; // Take Block Padding into account. lzma2_size = (lzma2_size + 3) & ~UINT64_C(3); // No risk of integer overflow because lzma2_bound() already takes // into account the size of the headers in the Block. return HEADERS_BOUND + lzma2_size; }
lzma_block_buffer_bound(size_t uncompressed_size) { // For now, if the data doesn't compress, we always use uncompressed // chunks of LZMA2. In future we may use Subblock filter too, but // but for simplicity we probably will still use the same bound // calculation even though Subblock filter would have slightly less // overhead. lzma_vli lzma2_size = lzma2_bound(uncompressed_size); if (lzma2_size == 0) return 0; // Take Block Padding into account. lzma2_size = (lzma2_size + 3) & ~LZMA_VLI_C(3); #if SIZE_MAX < LZMA_VLI_MAX // 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. if (SIZE_MAX - HEADERS_BOUND < lzma2_size) return 0; #endif return HEADERS_BOUND + lzma2_size; }
static lzma_ret block_encode_uncompressed(lzma_block *block, const uint8_t *in, size_t in_size, uint8_t *out, size_t *out_pos, size_t out_size) { size_t in_pos = 0; uint8_t control = 0x01; // Dictionary reset lzma_filter *filters_orig; // 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 // all, but LZMA2 always requires a dictionary, so use the minimum // value to minimize memory usage of the decoder. lzma_options_lzma lzma2 = { LZMA_DICT_SIZE_MIN }; lzma_filter filters[2]; filters[0].id = LZMA_FILTER_LZMA2; filters[0].options = &lzma2; filters[1].id = LZMA_VLI_UNKNOWN; // Set the above filter options to *block temporarily so that we can // encode the Block Header. filters_orig = block->filters; block->filters = filters; if (lzma_block_header_size(block) != LZMA_OK) { block->filters = filters_orig; return LZMA_PROG_ERROR; } // Check that there's enough output space. The caller has already // set block->compressed_size to what lzma2_bound() has returned, // so we can reuse that value. We know that compressed_size is a // known valid VLI and header_size is a small value so their sum // will never overflow. assert(block->compressed_size == lzma2_bound(in_size)); if (out_size - *out_pos < block->header_size + block->compressed_size) { block->filters = filters_orig; return LZMA_BUF_ERROR; } if (lzma_block_header_encode(block, out + *out_pos) != LZMA_OK) { block->filters = filters_orig; return LZMA_PROG_ERROR; } block->filters = filters_orig; *out_pos += block->header_size; // Encode the data using LZMA2 uncompressed chunks. while (in_pos < in_size) { size_t copy_size; // Control byte: Indicate uncompressed chunk, of which // the first resets the dictionary. out[(*out_pos)++] = control; control = 0x02; // No dictionary reset // Size of the uncompressed chunk copy_size = my_min(in_size - in_pos, LZMA2_CHUNK_MAX); out[(*out_pos)++] = (copy_size - 1) >> 8; out[(*out_pos)++] = (copy_size - 1) & 0xFF; // The actual data assert(*out_pos + copy_size <= out_size); memcpy(out + *out_pos, in + in_pos, copy_size); in_pos += copy_size; *out_pos += copy_size; } // End marker out[(*out_pos)++] = 0x00; assert(*out_pos <= out_size); return LZMA_OK; }
static lzma_ret block_encode_normal(lzma_block *block, lzma_allocator *allocator, const uint8_t *in, size_t in_size, uint8_t *out, size_t *out_pos, size_t out_size) { size_t out_start; lzma_next_coder raw_encoder = LZMA_NEXT_CODER_INIT; lzma_ret ret; // 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)); // Reserve space for the Block Header and skip it for now. if (out_size - *out_pos <= block->header_size) return LZMA_BUF_ERROR; out_start = *out_pos; *out_pos += block->header_size; // Limit out_size so that we stop encoding if the output would grow // bigger than what uncompressed Block would be. if (out_size - *out_pos > block->compressed_size) out_size = *out_pos + block->compressed_size; // TODO: In many common cases this could be optimized to use // significantly less memory. ret = lzma_raw_encoder_init( &raw_encoder, allocator, block->filters); if (ret == LZMA_OK) { size_t in_pos = 0; ret = raw_encoder.code(raw_encoder.coder, allocator, in, &in_pos, in_size, out, out_pos, out_size, LZMA_FINISH); } // NOTE: This needs to be run even if lzma_raw_encoder_init() failed. lzma_next_end(&raw_encoder, allocator); if (ret == LZMA_STREAM_END) { // Compression was successful. Write the Block Header. block->compressed_size = *out_pos - (out_start + block->header_size); ret = lzma_block_header_encode(block, out + out_start); if (ret != LZMA_OK) ret = LZMA_PROG_ERROR; } else if (ret == LZMA_OK) { // Output buffer became full. ret = LZMA_BUF_ERROR; } // Reset *out_pos if something went wrong. if (ret != LZMA_OK) *out_pos = out_start; return ret; }