xref: /minix3/external/public-domain/xz/dist/doc/examples/04_compress_easy_mt.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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