1/////////////////////////////////////////////////////////////////////////////// 2// 3/// \file known_sizes.c 4/// \brief Encodes .lzma Stream with sizes known in Block Header 5/// 6/// The input file is encoded in RAM, and the known Compressed Size 7/// and/or Uncompressed Size values are stored in the Block Header. 8/// As of writing there's no such Stream encoder in liblzma. 9// 10// Author: Lasse Collin 11// 12// This file has been put into the public domain. 13// You can do whatever you want with this file. 14// 15/////////////////////////////////////////////////////////////////////////////// 16 17#include "sysdefs.h" 18#include "lzma.h" 19#include <sys/types.h> 20#include <sys/stat.h> 21#include <sys/unistd.h> 22#include <stdio.h> 23 24 25// Support file sizes up to 1 MiB. We use this for output space too, so files 26// close to 1 MiB had better compress at least a little or we have a buffer 27// overflow. 28#define BUFFER_SIZE (1U << 20) 29 30 31int 32main(void) 33{ 34 // Allocate the buffers. 35 uint8_t *in = malloc(BUFFER_SIZE); 36 uint8_t *out = malloc(BUFFER_SIZE); 37 if (in == NULL || out == NULL) 38 return 1; 39 40 // Fill the input buffer. 41 const size_t in_size = fread(in, 1, BUFFER_SIZE, stdin); 42 43 // Filter setup 44 lzma_options_lzma opt_lzma; 45 if (lzma_lzma_preset(&opt_lzma, 1)) 46 return 1; 47 48 lzma_filter filters[] = { 49 { 50 .id = LZMA_FILTER_LZMA2, 51 .options = &opt_lzma 52 }, 53 { 54 .id = LZMA_VLI_UNKNOWN 55 } 56 }; 57 58 lzma_block block = { 59 .check = LZMA_CHECK_CRC32, 60 .compressed_size = BUFFER_SIZE, // Worst case reserve 61 .uncompressed_size = in_size, 62 .filters = filters, 63 }; 64 65 lzma_stream strm = LZMA_STREAM_INIT; 66 if (lzma_block_encoder(&strm, &block) != LZMA_OK) 67 return 1; 68 69 // Reserve space for Stream Header and Block Header. We need to 70 // calculate the size of the Block Header first. 71 if (lzma_block_header_size(&block) != LZMA_OK) 72 return 1; 73 74 size_t out_size = LZMA_STREAM_HEADER_SIZE + block.header_size; 75 76 strm.next_in = in; 77 strm.avail_in = in_size; 78 strm.next_out = out + out_size; 79 strm.avail_out = BUFFER_SIZE - out_size; 80 81 if (lzma_code(&strm, LZMA_FINISH) != LZMA_STREAM_END) 82 return 1; 83 84 out_size += strm.total_out; 85 86 if (lzma_block_header_encode(&block, out + LZMA_STREAM_HEADER_SIZE) 87 != LZMA_OK) 88 return 1; 89 90 lzma_index *idx = lzma_index_init(NULL); 91 if (idx == NULL) 92 return 1; 93 94 if (lzma_index_append(idx, NULL, block.header_size + strm.total_out, 95 strm.total_in) != LZMA_OK) 96 return 1; 97 98 if (lzma_index_encoder(&strm, idx) != LZMA_OK) 99 return 1; 100 101 if (lzma_code(&strm, LZMA_RUN) != LZMA_STREAM_END) 102 return 1; 103 104 out_size += strm.total_out; 105 106 lzma_end(&strm); 107 108 lzma_index_end(idx, NULL); 109 110 // Encode the Stream Header and Stream Footer. backwards_size is 111 // needed only for the Stream Footer. 112 lzma_stream_flags sf = { 113 .backward_size = strm.total_out, 114 .check = block.check, 115 }; 116 117 if (lzma_stream_header_encode(&sf, out) != LZMA_OK) 118 return 1; 119 120 if (lzma_stream_footer_encode(&sf, out + out_size) != LZMA_OK) 121 return 1; 122 123 out_size += LZMA_STREAM_HEADER_SIZE; 124 125 // Write out the file. 126 fwrite(out, 1, out_size, stdout); 127 128 return 0; 129} 130