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