Path: blob/master/Utilities/cmliblzma/liblzma/common/block_encoder.c
3153 views
// SPDX-License-Identifier: 0BSD12///////////////////////////////////////////////////////////////////////////////3//4/// \file block_encoder.c5/// \brief Encodes .xz Blocks6//7// Author: Lasse Collin8//9///////////////////////////////////////////////////////////////////////////////1011#include "block_encoder.h"12#include "filter_encoder.h"13#include "check.h"141516typedef struct {17/// The filters in the chain; initialized with lzma_raw_decoder_init().18lzma_next_coder next;1920/// Encoding options; we also write Unpadded Size, Compressed Size,21/// and Uncompressed Size back to this structure when the encoding22/// has been finished.23lzma_block *block;2425enum {26SEQ_CODE,27SEQ_PADDING,28SEQ_CHECK,29} sequence;3031/// Compressed Size calculated while encoding32lzma_vli compressed_size;3334/// Uncompressed Size calculated while encoding35lzma_vli uncompressed_size;3637/// Position in the Check field38size_t pos;3940/// Check of the uncompressed data41lzma_check_state check;42} lzma_block_coder;434445static lzma_ret46block_encode(void *coder_ptr, const lzma_allocator *allocator,47const uint8_t *restrict in, size_t *restrict in_pos,48size_t in_size, uint8_t *restrict out,49size_t *restrict out_pos, size_t out_size, lzma_action action)50{51lzma_block_coder *coder = coder_ptr;5253// Check that our amount of input stays in proper limits.54if (LZMA_VLI_MAX - coder->uncompressed_size < in_size - *in_pos)55return LZMA_DATA_ERROR;5657switch (coder->sequence) {58case SEQ_CODE: {59const size_t in_start = *in_pos;60const size_t out_start = *out_pos;6162const lzma_ret ret = coder->next.code(coder->next.coder,63allocator, in, in_pos, in_size,64out, out_pos, out_size, action);6566const size_t in_used = *in_pos - in_start;67const size_t out_used = *out_pos - out_start;6869if (COMPRESSED_SIZE_MAX - coder->compressed_size < out_used)70return LZMA_DATA_ERROR;7172coder->compressed_size += out_used;7374// No need to check for overflow because we have already75// checked it at the beginning of this function.76coder->uncompressed_size += in_used;7778// Call lzma_check_update() only if input was consumed. This79// avoids null pointer + 0 (undefined behavior) when in == 0.80if (in_used > 0)81lzma_check_update(&coder->check, coder->block->check,82in + in_start, in_used);8384if (ret != LZMA_STREAM_END || action == LZMA_SYNC_FLUSH)85return ret;8687assert(*in_pos == in_size);88assert(action == LZMA_FINISH);8990// Copy the values into coder->block. The caller91// may use this information to construct Index.92coder->block->compressed_size = coder->compressed_size;93coder->block->uncompressed_size = coder->uncompressed_size;9495coder->sequence = SEQ_PADDING;96}9798// Fall through99100case SEQ_PADDING:101// Pad Compressed Data to a multiple of four bytes. We can102// use coder->compressed_size for this since we don't need103// it for anything else anymore.104while (coder->compressed_size & 3) {105if (*out_pos >= out_size)106return LZMA_OK;107108out[*out_pos] = 0x00;109++*out_pos;110++coder->compressed_size;111}112113if (coder->block->check == LZMA_CHECK_NONE)114return LZMA_STREAM_END;115116lzma_check_finish(&coder->check, coder->block->check);117118coder->sequence = SEQ_CHECK;119120// Fall through121122case SEQ_CHECK: {123const size_t check_size = lzma_check_size(coder->block->check);124lzma_bufcpy(coder->check.buffer.u8, &coder->pos, check_size,125out, out_pos, out_size);126if (coder->pos < check_size)127return LZMA_OK;128129memcpy(coder->block->raw_check, coder->check.buffer.u8,130check_size);131return LZMA_STREAM_END;132}133}134135return LZMA_PROG_ERROR;136}137138139static void140block_encoder_end(void *coder_ptr, const lzma_allocator *allocator)141{142lzma_block_coder *coder = coder_ptr;143lzma_next_end(&coder->next, allocator);144lzma_free(coder, allocator);145return;146}147148149static lzma_ret150block_encoder_update(void *coder_ptr, const lzma_allocator *allocator,151const lzma_filter *filters lzma_attribute((__unused__)),152const lzma_filter *reversed_filters)153{154lzma_block_coder *coder = coder_ptr;155156if (coder->sequence != SEQ_CODE)157return LZMA_PROG_ERROR;158159return lzma_next_filter_update(160&coder->next, allocator, reversed_filters);161}162163164extern lzma_ret165lzma_block_encoder_init(lzma_next_coder *next, const lzma_allocator *allocator,166lzma_block *block)167{168lzma_next_coder_init(&lzma_block_encoder_init, next, allocator);169170if (block == NULL)171return LZMA_PROG_ERROR;172173// The contents of the structure may depend on the version so174// check the version first.175if (block->version > 1)176return LZMA_OPTIONS_ERROR;177178// If the Check ID is not supported, we cannot calculate the check and179// thus not create a proper Block.180if ((unsigned int)(block->check) > LZMA_CHECK_ID_MAX)181return LZMA_PROG_ERROR;182183if (!lzma_check_is_supported(block->check))184return LZMA_UNSUPPORTED_CHECK;185186// Allocate and initialize *next->coder if needed.187lzma_block_coder *coder = next->coder;188if (coder == NULL) {189coder = lzma_alloc(sizeof(lzma_block_coder), allocator);190if (coder == NULL)191return LZMA_MEM_ERROR;192193next->coder = coder;194next->code = &block_encode;195next->end = &block_encoder_end;196next->update = &block_encoder_update;197coder->next = LZMA_NEXT_CODER_INIT;198}199200// Basic initializations201coder->sequence = SEQ_CODE;202coder->block = block;203coder->compressed_size = 0;204coder->uncompressed_size = 0;205coder->pos = 0;206207// Initialize the check208lzma_check_init(&coder->check, block->check);209210// Initialize the requested filters.211return lzma_raw_encoder_init(&coder->next, allocator, block->filters);212}213214215extern LZMA_API(lzma_ret)216lzma_block_encoder(lzma_stream *strm, lzma_block *block)217{218lzma_next_strm_init(lzma_block_encoder_init, strm, block);219220strm->internal->supported_actions[LZMA_RUN] = true;221strm->internal->supported_actions[LZMA_SYNC_FLUSH] = true;222strm->internal->supported_actions[LZMA_FINISH] = true;223224return LZMA_OK;225}226227228