1*880b10faSchristos ///////////////////////////////////////////////////////////////////////////////
2*880b10faSchristos //
3*880b10faSchristos /// \file 04_compress_easy_mt.c
4*880b10faSchristos /// \brief Compress in multi-call mode using LZMA2 in multi-threaded mode
5*880b10faSchristos ///
6*880b10faSchristos /// Usage: ./04_compress_easy_mt < INFILE > OUTFILE
7*880b10faSchristos ///
8*880b10faSchristos /// Example: ./04_compress_easy_mt < 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 // The threaded encoder takes the options as pointer to
29*880b10faSchristos // a lzma_mt structure.
30*880b10faSchristos lzma_mt mt = {
31*880b10faSchristos // No flags are needed.
32*880b10faSchristos .flags = 0,
33*880b10faSchristos
34*880b10faSchristos // Let liblzma determine a sane block size.
35*880b10faSchristos .block_size = 0,
36*880b10faSchristos
37*880b10faSchristos // Use no timeout for lzma_code() calls by setting timeout
38*880b10faSchristos // to zero. That is, sometimes lzma_code() might block for
39*880b10faSchristos // a long time (from several seconds to even minutes).
40*880b10faSchristos // If this is not OK, for example due to progress indicator
41*880b10faSchristos // needing updates, specify a timeout in milliseconds here.
42*880b10faSchristos // See the documentation of lzma_mt in lzma/container.h for
43*880b10faSchristos // information how to choose a reasonable timeout.
44*880b10faSchristos .timeout = 0,
45*880b10faSchristos
46*880b10faSchristos // Use the default preset (6) for LZMA2.
47*880b10faSchristos // To use a preset, filters must be set to NULL.
48*880b10faSchristos .preset = LZMA_PRESET_DEFAULT,
49*880b10faSchristos .filters = NULL,
50*880b10faSchristos
51*880b10faSchristos // Use CRC64 for integrity checking. See also
52*880b10faSchristos // 01_compress_easy.c about choosing the integrity check.
53*880b10faSchristos .check = LZMA_CHECK_CRC64,
54*880b10faSchristos };
55*880b10faSchristos
56*880b10faSchristos // Detect how many threads the CPU supports.
57*880b10faSchristos mt.threads = lzma_cputhreads();
58*880b10faSchristos
59*880b10faSchristos // If the number of CPU cores/threads cannot be detected,
60*880b10faSchristos // use one thread. Note that this isn't the same as the normal
61*880b10faSchristos // single-threaded mode as this will still split the data into
62*880b10faSchristos // blocks and use more RAM than the normal single-threaded mode.
63*880b10faSchristos // You may want to consider using lzma_easy_encoder() or
64*880b10faSchristos // lzma_stream_encoder() instead of lzma_stream_encoder_mt() if
65*880b10faSchristos // lzma_cputhreads() returns 0 or 1.
66*880b10faSchristos if (mt.threads == 0)
67*880b10faSchristos mt.threads = 1;
68*880b10faSchristos
69*880b10faSchristos // If the number of CPU cores/threads exceeds threads_max,
70*880b10faSchristos // limit the number of threads to keep memory usage lower.
71*880b10faSchristos // The number 8 is arbitrarily chosen and may be too low or
72*880b10faSchristos // high depending on the compression preset and the computer
73*880b10faSchristos // being used.
74*880b10faSchristos //
75*880b10faSchristos // FIXME: A better way could be to check the amount of RAM
76*880b10faSchristos // (or available RAM) and use lzma_stream_encoder_mt_memusage()
77*880b10faSchristos // to determine if the number of threads should be reduced.
78*880b10faSchristos const uint32_t threads_max = 8;
79*880b10faSchristos if (mt.threads > threads_max)
80*880b10faSchristos mt.threads = threads_max;
81*880b10faSchristos
82*880b10faSchristos // Initialize the threaded encoder.
83*880b10faSchristos lzma_ret ret = lzma_stream_encoder_mt(strm, &mt);
84*880b10faSchristos
85*880b10faSchristos if (ret == LZMA_OK)
86*880b10faSchristos return true;
87*880b10faSchristos
88*880b10faSchristos const char *msg;
89*880b10faSchristos switch (ret) {
90*880b10faSchristos case LZMA_MEM_ERROR:
91*880b10faSchristos msg = "Memory allocation failed";
92*880b10faSchristos break;
93*880b10faSchristos
94*880b10faSchristos case LZMA_OPTIONS_ERROR:
95*880b10faSchristos // We are no longer using a plain preset so this error
96*880b10faSchristos // message has been edited accordingly compared to
97*880b10faSchristos // 01_compress_easy.c.
98*880b10faSchristos msg = "Specified filter chain is not supported";
99*880b10faSchristos break;
100*880b10faSchristos
101*880b10faSchristos case LZMA_UNSUPPORTED_CHECK:
102*880b10faSchristos msg = "Specified integrity check is not supported";
103*880b10faSchristos break;
104*880b10faSchristos
105*880b10faSchristos default:
106*880b10faSchristos msg = "Unknown error, possibly a bug";
107*880b10faSchristos break;
108*880b10faSchristos }
109*880b10faSchristos
110*880b10faSchristos fprintf(stderr, "Error initializing the encoder: %s (error code %u)\n",
111*880b10faSchristos msg, ret);
112*880b10faSchristos return false;
113*880b10faSchristos }
114*880b10faSchristos
115*880b10faSchristos
116*880b10faSchristos // This function is identical to the one in 01_compress_easy.c.
117*880b10faSchristos static bool
compress(lzma_stream * strm,FILE * infile,FILE * outfile)118*880b10faSchristos compress(lzma_stream *strm, FILE *infile, FILE *outfile)
119*880b10faSchristos {
120*880b10faSchristos lzma_action action = LZMA_RUN;
121*880b10faSchristos
122*880b10faSchristos uint8_t inbuf[BUFSIZ];
123*880b10faSchristos uint8_t outbuf[BUFSIZ];
124*880b10faSchristos
125*880b10faSchristos strm->next_in = NULL;
126*880b10faSchristos strm->avail_in = 0;
127*880b10faSchristos strm->next_out = outbuf;
128*880b10faSchristos strm->avail_out = sizeof(outbuf);
129*880b10faSchristos
130*880b10faSchristos while (true) {
131*880b10faSchristos if (strm->avail_in == 0 && !feof(infile)) {
132*880b10faSchristos strm->next_in = inbuf;
133*880b10faSchristos strm->avail_in = fread(inbuf, 1, sizeof(inbuf),
134*880b10faSchristos infile);
135*880b10faSchristos
136*880b10faSchristos if (ferror(infile)) {
137*880b10faSchristos fprintf(stderr, "Read error: %s\n",
138*880b10faSchristos strerror(errno));
139*880b10faSchristos return false;
140*880b10faSchristos }
141*880b10faSchristos
142*880b10faSchristos if (feof(infile))
143*880b10faSchristos action = LZMA_FINISH;
144*880b10faSchristos }
145*880b10faSchristos
146*880b10faSchristos lzma_ret ret = lzma_code(strm, action);
147*880b10faSchristos
148*880b10faSchristos if (strm->avail_out == 0 || ret == LZMA_STREAM_END) {
149*880b10faSchristos size_t write_size = sizeof(outbuf) - strm->avail_out;
150*880b10faSchristos
151*880b10faSchristos if (fwrite(outbuf, 1, write_size, outfile)
152*880b10faSchristos != write_size) {
153*880b10faSchristos fprintf(stderr, "Write error: %s\n",
154*880b10faSchristos strerror(errno));
155*880b10faSchristos return false;
156*880b10faSchristos }
157*880b10faSchristos
158*880b10faSchristos strm->next_out = outbuf;
159*880b10faSchristos strm->avail_out = sizeof(outbuf);
160*880b10faSchristos }
161*880b10faSchristos
162*880b10faSchristos if (ret != LZMA_OK) {
163*880b10faSchristos if (ret == LZMA_STREAM_END)
164*880b10faSchristos return true;
165*880b10faSchristos
166*880b10faSchristos const char *msg;
167*880b10faSchristos switch (ret) {
168*880b10faSchristos case LZMA_MEM_ERROR:
169*880b10faSchristos msg = "Memory allocation failed";
170*880b10faSchristos break;
171*880b10faSchristos
172*880b10faSchristos case LZMA_DATA_ERROR:
173*880b10faSchristos msg = "File size limits exceeded";
174*880b10faSchristos break;
175*880b10faSchristos
176*880b10faSchristos default:
177*880b10faSchristos msg = "Unknown error, possibly a bug";
178*880b10faSchristos break;
179*880b10faSchristos }
180*880b10faSchristos
181*880b10faSchristos fprintf(stderr, "Encoder error: %s (error code %u)\n",
182*880b10faSchristos msg, ret);
183*880b10faSchristos return false;
184*880b10faSchristos }
185*880b10faSchristos }
186*880b10faSchristos }
187*880b10faSchristos
188*880b10faSchristos
189*880b10faSchristos extern int
main(void)190*880b10faSchristos main(void)
191*880b10faSchristos {
192*880b10faSchristos lzma_stream strm = LZMA_STREAM_INIT;
193*880b10faSchristos
194*880b10faSchristos bool success = init_encoder(&strm);
195*880b10faSchristos if (success)
196*880b10faSchristos success = compress(&strm, stdin, stdout);
197*880b10faSchristos
198*880b10faSchristos lzma_end(&strm);
199*880b10faSchristos
200*880b10faSchristos if (fclose(stdout)) {
201*880b10faSchristos fprintf(stderr, "Write error: %s\n", strerror(errno));
202*880b10faSchristos success = false;
203*880b10faSchristos }
204*880b10faSchristos
205*880b10faSchristos return success ? EXIT_SUCCESS : EXIT_FAILURE;
206*880b10faSchristos }
207