xref: /netbsd-src/external/public-domain/xz/dist/doc/examples/04_compress_easy_mt.c (revision 880b10faa82e7724136925c670f27d59c8a678b6)
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