1 /////////////////////////////////////////////////////////////////////////////// 2 // 3 /// \file 03_compress_custom.c 4 /// \brief Compress in multi-call mode using x86 BCJ and LZMA2 5 /// 6 /// Usage: ./03_compress_custom < INFILE > OUTFILE 7 /// 8 /// Example: ./03_compress_custom < foo > foo.xz 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 <stdbool.h> 18 #include <stdlib.h> 19 #include <stdio.h> 20 #include <string.h> 21 #include <errno.h> 22 #include <lzma.h> 23 24 25 static bool 26 init_encoder(lzma_stream *strm) 27 { 28 // Use the default preset (6) for LZMA2. 29 // 30 // The lzma_options_lzma structure and the lzma_lzma_preset() function 31 // are declared in lzma/lzma12.h (src/liblzma/api/lzma/lzma12.h in the 32 // source package or e.g. /usr/include/lzma/lzma12.h depending on 33 // the install prefix). 34 lzma_options_lzma opt_lzma2; 35 if (lzma_lzma_preset(&opt_lzma2, LZMA_PRESET_DEFAULT)) { 36 // It should never fail because the default preset 37 // (and presets 0-9 optionally with LZMA_PRESET_EXTREME) 38 // are supported by all stable liblzma versions. 39 // 40 // (The encoder initialization later in this function may 41 // still fail due to unsupported preset *if* the features 42 // required by the preset have been disabled at build time, 43 // but no-one does such things except on embedded systems.) 44 fprintf(stderr, "Unsupported preset, possibly a bug\n"); 45 return false; 46 } 47 48 // Now we could customize the LZMA2 options if we wanted. For example, 49 // we could set the the dictionary size (opt_lzma2.dict_size) to 50 // something else than the default (8 MiB) of the default preset. 51 // See lzma/lzma12.h for details of all LZMA2 options. 52 // 53 // The x86 BCJ filter will try to modify the x86 instruction stream so 54 // that LZMA2 can compress it better. The x86 BCJ filter doesn't need 55 // any options so it will be set to NULL below. 56 // 57 // Construct the filter chain. The uncompressed data goes first to 58 // the first filter in the array, in this case the x86 BCJ filter. 59 // The array is always terminated by setting .id = LZMA_VLI_UNKNOWN. 60 // 61 // See lzma/filter.h for more information about the lzma_filter 62 // structure. 63 lzma_filter filters[] = { 64 { .id = LZMA_FILTER_X86, .options = NULL }, 65 { .id = LZMA_FILTER_LZMA2, .options = &opt_lzma2 }, 66 { .id = LZMA_VLI_UNKNOWN, .options = NULL }, 67 }; 68 69 // Initialize the encoder using the custom filter chain. 70 lzma_ret ret = lzma_stream_encoder(strm, filters, LZMA_CHECK_CRC64); 71 72 if (ret == LZMA_OK) 73 return true; 74 75 const char *msg; 76 switch (ret) { 77 case LZMA_MEM_ERROR: 78 msg = "Memory allocation failed"; 79 break; 80 81 case LZMA_OPTIONS_ERROR: 82 // We are no longer using a plain preset so this error 83 // message has been edited accordingly compared to 84 // 01_compress_easy.c. 85 msg = "Specified filter chain is not supported"; 86 break; 87 88 case LZMA_UNSUPPORTED_CHECK: 89 msg = "Specified integrity check is not supported"; 90 break; 91 92 default: 93 msg = "Unknown error, possibly a bug"; 94 break; 95 } 96 97 fprintf(stderr, "Error initializing the encoder: %s (error code %u)\n", 98 msg, ret); 99 return false; 100 } 101 102 103 // This function is identical to the one in 01_compress_easy.c. 104 static bool 105 compress(lzma_stream *strm, FILE *infile, FILE *outfile) 106 { 107 lzma_action action = LZMA_RUN; 108 109 uint8_t inbuf[BUFSIZ]; 110 uint8_t outbuf[BUFSIZ]; 111 112 strm->next_in = NULL; 113 strm->avail_in = 0; 114 strm->next_out = outbuf; 115 strm->avail_out = sizeof(outbuf); 116 117 while (true) { 118 if (strm->avail_in == 0 && !feof(infile)) { 119 strm->next_in = inbuf; 120 strm->avail_in = fread(inbuf, 1, sizeof(inbuf), 121 infile); 122 123 if (ferror(infile)) { 124 fprintf(stderr, "Read error: %s\n", 125 strerror(errno)); 126 return false; 127 } 128 129 if (feof(infile)) 130 action = LZMA_FINISH; 131 } 132 133 lzma_ret ret = lzma_code(strm, action); 134 135 if (strm->avail_out == 0 || ret == LZMA_STREAM_END) { 136 size_t write_size = sizeof(outbuf) - strm->avail_out; 137 138 if (fwrite(outbuf, 1, write_size, outfile) 139 != write_size) { 140 fprintf(stderr, "Write error: %s\n", 141 strerror(errno)); 142 return false; 143 } 144 145 strm->next_out = outbuf; 146 strm->avail_out = sizeof(outbuf); 147 } 148 149 if (ret != LZMA_OK) { 150 if (ret == LZMA_STREAM_END) 151 return true; 152 153 const char *msg; 154 switch (ret) { 155 case LZMA_MEM_ERROR: 156 msg = "Memory allocation failed"; 157 break; 158 159 case LZMA_DATA_ERROR: 160 msg = "File size limits exceeded"; 161 break; 162 163 default: 164 msg = "Unknown error, possibly a bug"; 165 break; 166 } 167 168 fprintf(stderr, "Encoder error: %s (error code %u)\n", 169 msg, ret); 170 return false; 171 } 172 } 173 } 174 175 176 extern int 177 main(void) 178 { 179 lzma_stream strm = LZMA_STREAM_INIT; 180 181 bool success = init_encoder(&strm); 182 if (success) 183 success = compress(&strm, stdin, stdout); 184 185 lzma_end(&strm); 186 187 if (fclose(stdout)) { 188 fprintf(stderr, "Write error: %s\n", strerror(errno)); 189 success = false; 190 } 191 192 return success ? EXIT_SUCCESS : EXIT_FAILURE; 193 } 194