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
init_encoder(lzma_stream * strm)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
compress(lzma_stream * strm,FILE * infile,FILE * outfile)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
main(void)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