| /////////////////////////////////////////////////////////////////////////////// | |
| // | |
| /// \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 | |
| // | |
| // This file has been put into the public domain. | |
| // You can do whatever you want with this file. | |
| // | |
| /////////////////////////////////////////////////////////////////////////////// | |
| #include "sysdefs.h" | |
| #include "lzma.h" | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include <sys/unistd.h> | |
| #include <stdio.h> | |
| // 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; | |
| } |