xref: /netbsd-src/external/public-domain/xz/dist/doc/examples/03_compress_custom.c (revision 880b10faa82e7724136925c670f27d59c8a678b6)
1*880b10faSchristos ///////////////////////////////////////////////////////////////////////////////
2*880b10faSchristos //
3*880b10faSchristos /// \file       03_compress_custom.c
4*880b10faSchristos /// \brief      Compress in multi-call mode using x86 BCJ and LZMA2
5*880b10faSchristos ///
6*880b10faSchristos /// Usage:      ./03_compress_custom < INFILE > OUTFILE
7*880b10faSchristos ///
8*880b10faSchristos /// Example:    ./03_compress_custom < 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 	// Use the default preset (6) for LZMA2.
29*880b10faSchristos 	//
30*880b10faSchristos 	// The lzma_options_lzma structure and the lzma_lzma_preset() function
31*880b10faSchristos 	// are declared in lzma/lzma12.h (src/liblzma/api/lzma/lzma12.h in the
32*880b10faSchristos 	// source package or e.g. /usr/include/lzma/lzma12.h depending on
33*880b10faSchristos 	// the install prefix).
34*880b10faSchristos 	lzma_options_lzma opt_lzma2;
35*880b10faSchristos 	if (lzma_lzma_preset(&opt_lzma2, LZMA_PRESET_DEFAULT)) {
36*880b10faSchristos 		// It should never fail because the default preset
37*880b10faSchristos 		// (and presets 0-9 optionally with LZMA_PRESET_EXTREME)
38*880b10faSchristos 		// are supported by all stable liblzma versions.
39*880b10faSchristos 		//
40*880b10faSchristos 		// (The encoder initialization later in this function may
41*880b10faSchristos 		// still fail due to unsupported preset *if* the features
42*880b10faSchristos 		// required by the preset have been disabled at build time,
43*880b10faSchristos 		// but no-one does such things except on embedded systems.)
44*880b10faSchristos 		fprintf(stderr, "Unsupported preset, possibly a bug\n");
45*880b10faSchristos 		return false;
46*880b10faSchristos 	}
47*880b10faSchristos 
48*880b10faSchristos 	// Now we could customize the LZMA2 options if we wanted. For example,
49*880b10faSchristos 	// we could set the the dictionary size (opt_lzma2.dict_size) to
50*880b10faSchristos 	// something else than the default (8 MiB) of the default preset.
51*880b10faSchristos 	// See lzma/lzma12.h for details of all LZMA2 options.
52*880b10faSchristos 	//
53*880b10faSchristos 	// The x86 BCJ filter will try to modify the x86 instruction stream so
54*880b10faSchristos 	// that LZMA2 can compress it better. The x86 BCJ filter doesn't need
55*880b10faSchristos 	// any options so it will be set to NULL below.
56*880b10faSchristos 	//
57*880b10faSchristos 	// Construct the filter chain. The uncompressed data goes first to
58*880b10faSchristos 	// the first filter in the array, in this case the x86 BCJ filter.
59*880b10faSchristos 	// The array is always terminated by setting .id = LZMA_VLI_UNKNOWN.
60*880b10faSchristos 	//
61*880b10faSchristos 	// See lzma/filter.h for more information about the lzma_filter
62*880b10faSchristos 	// structure.
63*880b10faSchristos 	lzma_filter filters[] = {
64*880b10faSchristos 		{ .id = LZMA_FILTER_X86, .options = NULL },
65*880b10faSchristos 		{ .id = LZMA_FILTER_LZMA2, .options = &opt_lzma2 },
66*880b10faSchristos 		{ .id = LZMA_VLI_UNKNOWN, .options = NULL },
67*880b10faSchristos 	};
68*880b10faSchristos 
69*880b10faSchristos 	// Initialize the encoder using the custom filter chain.
70*880b10faSchristos 	lzma_ret ret = lzma_stream_encoder(strm, filters, LZMA_CHECK_CRC64);
71*880b10faSchristos 
72*880b10faSchristos 	if (ret == LZMA_OK)
73*880b10faSchristos 		return true;
74*880b10faSchristos 
75*880b10faSchristos 	const char *msg;
76*880b10faSchristos 	switch (ret) {
77*880b10faSchristos 	case LZMA_MEM_ERROR:
78*880b10faSchristos 		msg = "Memory allocation failed";
79*880b10faSchristos 		break;
80*880b10faSchristos 
81*880b10faSchristos 	case LZMA_OPTIONS_ERROR:
82*880b10faSchristos 		// We are no longer using a plain preset so this error
83*880b10faSchristos 		// message has been edited accordingly compared to
84*880b10faSchristos 		// 01_compress_easy.c.
85*880b10faSchristos 		msg = "Specified filter chain is not supported";
86*880b10faSchristos 		break;
87*880b10faSchristos 
88*880b10faSchristos 	case LZMA_UNSUPPORTED_CHECK:
89*880b10faSchristos 		msg = "Specified integrity check is not supported";
90*880b10faSchristos 		break;
91*880b10faSchristos 
92*880b10faSchristos 	default:
93*880b10faSchristos 		msg = "Unknown error, possibly a bug";
94*880b10faSchristos 		break;
95*880b10faSchristos 	}
96*880b10faSchristos 
97*880b10faSchristos 	fprintf(stderr, "Error initializing the encoder: %s (error code %u)\n",
98*880b10faSchristos 			msg, ret);
99*880b10faSchristos 	return false;
100*880b10faSchristos }
101*880b10faSchristos 
102*880b10faSchristos 
103*880b10faSchristos // This function is identical to the one in 01_compress_easy.c.
104*880b10faSchristos static bool
compress(lzma_stream * strm,FILE * infile,FILE * outfile)105*880b10faSchristos compress(lzma_stream *strm, FILE *infile, FILE *outfile)
106*880b10faSchristos {
107*880b10faSchristos 	lzma_action action = LZMA_RUN;
108*880b10faSchristos 
109*880b10faSchristos 	uint8_t inbuf[BUFSIZ];
110*880b10faSchristos 	uint8_t outbuf[BUFSIZ];
111*880b10faSchristos 
112*880b10faSchristos 	strm->next_in = NULL;
113*880b10faSchristos 	strm->avail_in = 0;
114*880b10faSchristos 	strm->next_out = outbuf;
115*880b10faSchristos 	strm->avail_out = sizeof(outbuf);
116*880b10faSchristos 
117*880b10faSchristos 	while (true) {
118*880b10faSchristos 		if (strm->avail_in == 0 && !feof(infile)) {
119*880b10faSchristos 			strm->next_in = inbuf;
120*880b10faSchristos 			strm->avail_in = fread(inbuf, 1, sizeof(inbuf),
121*880b10faSchristos 					infile);
122*880b10faSchristos 
123*880b10faSchristos 			if (ferror(infile)) {
124*880b10faSchristos 				fprintf(stderr, "Read error: %s\n",
125*880b10faSchristos 						strerror(errno));
126*880b10faSchristos 				return false;
127*880b10faSchristos 			}
128*880b10faSchristos 
129*880b10faSchristos 			if (feof(infile))
130*880b10faSchristos 				action = LZMA_FINISH;
131*880b10faSchristos 		}
132*880b10faSchristos 
133*880b10faSchristos 		lzma_ret ret = lzma_code(strm, action);
134*880b10faSchristos 
135*880b10faSchristos 		if (strm->avail_out == 0 || ret == LZMA_STREAM_END) {
136*880b10faSchristos 			size_t write_size = sizeof(outbuf) - strm->avail_out;
137*880b10faSchristos 
138*880b10faSchristos 			if (fwrite(outbuf, 1, write_size, outfile)
139*880b10faSchristos 					!= write_size) {
140*880b10faSchristos 				fprintf(stderr, "Write error: %s\n",
141*880b10faSchristos 						strerror(errno));
142*880b10faSchristos 				return false;
143*880b10faSchristos 			}
144*880b10faSchristos 
145*880b10faSchristos 			strm->next_out = outbuf;
146*880b10faSchristos 			strm->avail_out = sizeof(outbuf);
147*880b10faSchristos 		}
148*880b10faSchristos 
149*880b10faSchristos 		if (ret != LZMA_OK) {
150*880b10faSchristos 			if (ret == LZMA_STREAM_END)
151*880b10faSchristos 				return true;
152*880b10faSchristos 
153*880b10faSchristos 			const char *msg;
154*880b10faSchristos 			switch (ret) {
155*880b10faSchristos 			case LZMA_MEM_ERROR:
156*880b10faSchristos 				msg = "Memory allocation failed";
157*880b10faSchristos 				break;
158*880b10faSchristos 
159*880b10faSchristos 			case LZMA_DATA_ERROR:
160*880b10faSchristos 				msg = "File size limits exceeded";
161*880b10faSchristos 				break;
162*880b10faSchristos 
163*880b10faSchristos 			default:
164*880b10faSchristos 				msg = "Unknown error, possibly a bug";
165*880b10faSchristos 				break;
166*880b10faSchristos 			}
167*880b10faSchristos 
168*880b10faSchristos 			fprintf(stderr, "Encoder error: %s (error code %u)\n",
169*880b10faSchristos 					msg, ret);
170*880b10faSchristos 			return false;
171*880b10faSchristos 		}
172*880b10faSchristos 	}
173*880b10faSchristos }
174*880b10faSchristos 
175*880b10faSchristos 
176*880b10faSchristos extern int
main(void)177*880b10faSchristos main(void)
178*880b10faSchristos {
179*880b10faSchristos 	lzma_stream strm = LZMA_STREAM_INIT;
180*880b10faSchristos 
181*880b10faSchristos 	bool success = init_encoder(&strm);
182*880b10faSchristos 	if (success)
183*880b10faSchristos 		success = compress(&strm, stdin, stdout);
184*880b10faSchristos 
185*880b10faSchristos 	lzma_end(&strm);
186*880b10faSchristos 
187*880b10faSchristos 	if (fclose(stdout)) {
188*880b10faSchristos 		fprintf(stderr, "Write error: %s\n", strerror(errno));
189*880b10faSchristos 		success = false;
190*880b10faSchristos 	}
191*880b10faSchristos 
192*880b10faSchristos 	return success ? EXIT_SUCCESS : EXIT_FAILURE;
193*880b10faSchristos }
194