// SPDX-License-Identifier: 0BSD /////////////////////////////////////////////////////////////////////////////// // /// \file known_sizes.c /// \brief Encodes .lzma Stream with sizes known in Block Header /// /// The input file is encoded in RAM, and the known Compressed Size /// and/or Uncompressed Size values are stored in the Block Header. /// As of writing there's no such Stream encoder in liblzma. // // Author: Lasse Collin // /////////////////////////////////////////////////////////////////////////////// #include "sysdefs.h" #include "lzma.h" #include #include #include #include // Support file sizes up to 1 MiB. We use this for output space too, so files // close to 1 MiB had better compress at least a little or we have a buffer // overflow. #define BUFFER_SIZE (1U << 20) int main(void) { // Allocate the buffers. uint8_t *in = malloc(BUFFER_SIZE); uint8_t *out = malloc(BUFFER_SIZE); if (in == NULL || out == NULL) return 1; // Fill the input buffer. const size_t in_size = fread(in, 1, BUFFER_SIZE, stdin); // Filter setup lzma_options_lzma opt_lzma; if (lzma_lzma_preset(&opt_lzma, 1)) return 1; lzma_filter filters[] = { { .id = LZMA_FILTER_LZMA2, .options = &opt_lzma }, { .id = LZMA_VLI_UNKNOWN } }; lzma_block block = { .check = LZMA_CHECK_CRC32, .compressed_size = BUFFER_SIZE, // Worst case reserve .uncompressed_size = in_size, .filters = filters, }; lzma_stream strm = LZMA_STREAM_INIT; if (lzma_block_encoder(&strm, &block) != LZMA_OK) return 1; // Reserve space for Stream Header and Block Header. We need to // calculate the size of the Block Header first. if (lzma_block_header_size(&block) != LZMA_OK) return 1; size_t out_size = LZMA_STREAM_HEADER_SIZE + block.header_size; strm.next_in = in; strm.avail_in = in_size; strm.next_out = out + out_size; strm.avail_out = BUFFER_SIZE - out_size; if (lzma_code(&strm, LZMA_FINISH) != LZMA_STREAM_END) return 1; out_size += strm.total_out; if (lzma_block_header_encode(&block, out + LZMA_STREAM_HEADER_SIZE) != LZMA_OK) return 1; lzma_index *idx = lzma_index_init(NULL); if (idx == NULL) return 1; if (lzma_index_append(idx, NULL, block.header_size + strm.total_out, strm.total_in) != LZMA_OK) return 1; if (lzma_index_encoder(&strm, idx) != LZMA_OK) return 1; if (lzma_code(&strm, LZMA_RUN) != LZMA_STREAM_END) return 1; out_size += strm.total_out; lzma_end(&strm); lzma_index_end(idx, NULL); // Encode the Stream Header and Stream Footer. backwards_size is // needed only for the Stream Footer. lzma_stream_flags sf = { .backward_size = strm.total_out, .check = block.check, }; if (lzma_stream_header_encode(&sf, out) != LZMA_OK) return 1; if (lzma_stream_footer_encode(&sf, out + out_size) != LZMA_OK) return 1; out_size += LZMA_STREAM_HEADER_SIZE; // Write out the file. fwrite(out, 1, out_size, stdout); return 0; }