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