| /////////////////////////////////////////////////////////////////////////////// | |
| // | |
| /// \file 03_compress_custom.c | |
| /// \brief Compress in multi-call mode using x86 BCJ and LZMA2 | |
| /// | |
| /// Usage: ./03_compress_custom < INFILE > OUTFILE | |
| /// | |
| /// Example: ./03_compress_custom < foo > foo.xz | |
| // | |
| // Author: Lasse Collin | |
| // | |
| // This file has been put into the public domain. | |
| // You can do whatever you want with this file. | |
| // | |
| /////////////////////////////////////////////////////////////////////////////// | |
| #include <stdbool.h> | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <errno.h> | |
| #include <lzma.h> | |
| static bool | |
| init_encoder(lzma_stream *strm) | |
| { | |
| // Use the default preset (6) for LZMA2. | |
| // | |
| // The lzma_options_lzma structure and the lzma_lzma_preset() function | |
| // are declared in lzma/lzma.h (src/liblzma/api/lzma/lzma.h in the | |
| // source package or e.g. /usr/include/lzma/lzma.h depending on | |
| // the install prefix). | |
| lzma_options_lzma opt_lzma2; | |
| if (lzma_lzma_preset(&opt_lzma2, LZMA_PRESET_DEFAULT)) { | |
| // It should never fail because the default preset | |
| // (and presets 0-9 optionally with LZMA_PRESET_EXTREME) | |
| // are supported by all stable liblzma versions. | |
| // | |
| // (The encoder initialization later in this function may | |
| // still fail due to unsupported preset *if* the features | |
| // required by the preset have been disabled at build time, | |
| // but no-one does such things except on embedded systems.) | |
| fprintf(stderr, "Unsupported preset, possibly a bug\n"); | |
| return false; | |
| } | |
| // Now we could customize the LZMA2 options if we wanted. For example, | |
| // we could set the the dictionary size (opt_lzma2.dict_size) to | |
| // something else than the default (8 MiB) of the default preset. | |
| // See lzma/lzma.h for details of all LZMA2 options. | |
| // | |
| // The x86 BCJ filter will try to modify the x86 instruction stream so | |
| // that LZMA2 can compress it better. The x86 BCJ filter doesn't need | |
| // any options so it will be set to NULL below. | |
| // | |
| // Construct the filter chain. The uncompressed data goes first to | |
| // the first filter in the array, in this case the x86 BCJ filter. | |
| // The array is always terminated by setting .id = LZMA_VLI_UNKNOWN. | |
| // | |
| // See lzma/filter.h for more information about the lzma_filter | |
| // structure. | |
| lzma_filter filters[] = { | |
| { .id = LZMA_FILTER_X86, .options = NULL }, | |
| { .id = LZMA_FILTER_LZMA2, .options = &opt_lzma2 }, | |
| { .id = LZMA_VLI_UNKNOWN, .options = NULL }, | |
| }; | |
| // Initialize the encoder using the custom filter chain. | |
| lzma_ret ret = lzma_stream_encoder(strm, filters, LZMA_CHECK_CRC64); | |
| if (ret == LZMA_OK) | |
| return true; | |
| const char *msg; | |
| switch (ret) { | |
| case LZMA_MEM_ERROR: | |
| msg = "Memory allocation failed"; | |
| break; | |
| case LZMA_OPTIONS_ERROR: | |
| // We are no longer using a plain preset so this error | |
| // message has been edited accordingly compared to | |
| // 01_compress_easy.c. | |
| msg = "Specified filter chain is not supported"; | |
| break; | |
| case LZMA_UNSUPPORTED_CHECK: | |
| msg = "Specified integrity check is not supported"; | |
| break; | |
| default: | |
| msg = "Unknown error, possibly a bug"; | |
| break; | |
| } | |
| fprintf(stderr, "Error initializing the encoder: %s (error code %u)\n", | |
| msg, ret); | |
| return false; | |
| } | |
| // This function is identical to the one in 01_compress_easy.c. | |
| static bool | |
| compress(lzma_stream *strm, FILE *infile, FILE *outfile) | |
| { | |
| lzma_action action = LZMA_RUN; | |
| uint8_t inbuf[BUFSIZ]; | |
| uint8_t outbuf[BUFSIZ]; | |
| strm->next_in = NULL; | |
| strm->avail_in = 0; | |
| strm->next_out = outbuf; | |
| strm->avail_out = sizeof(outbuf); | |
| while (true) { | |
| if (strm->avail_in == 0 && !feof(infile)) { | |
| strm->next_in = inbuf; | |
| strm->avail_in = fread(inbuf, 1, sizeof(inbuf), | |
| infile); | |
| if (ferror(infile)) { | |
| fprintf(stderr, "Read error: %s\n", | |
| strerror(errno)); | |
| return false; | |
| } | |
| if (feof(infile)) | |
| action = LZMA_FINISH; | |
| } | |
| lzma_ret ret = lzma_code(strm, action); | |
| if (strm->avail_out == 0 || ret == LZMA_STREAM_END) { | |
| size_t write_size = sizeof(outbuf) - strm->avail_out; | |
| if (fwrite(outbuf, 1, write_size, outfile) | |
| != write_size) { | |
| fprintf(stderr, "Write error: %s\n", | |
| strerror(errno)); | |
| return false; | |
| } | |
| strm->next_out = outbuf; | |
| strm->avail_out = sizeof(outbuf); | |
| } | |
| if (ret != LZMA_OK) { | |
| if (ret == LZMA_STREAM_END) | |
| return true; | |
| const char *msg; | |
| switch (ret) { | |
| case LZMA_MEM_ERROR: | |
| msg = "Memory allocation failed"; | |
| break; | |
| case LZMA_DATA_ERROR: | |
| msg = "File size limits exceeded"; | |
| break; | |
| default: | |
| msg = "Unknown error, possibly a bug"; | |
| break; | |
| } | |
| fprintf(stderr, "Encoder error: %s (error code %u)\n", | |
| msg, ret); | |
| return false; | |
| } | |
| } | |
| } | |
| extern int | |
| main(void) | |
| { | |
| lzma_stream strm = LZMA_STREAM_INIT; | |
| bool success = init_encoder(&strm); | |
| if (success) | |
| success = compress(&strm, stdin, stdout); | |
| lzma_end(&strm); | |
| if (fclose(stdout)) { | |
| fprintf(stderr, "Write error: %s\n", strerror(errno)); | |
| success = false; | |
| } | |
| return success ? EXIT_SUCCESS : EXIT_FAILURE; | |
| } |