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