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