xref: /haiku/src/tests/kits/support/compression_test.cpp (revision 6ac3a280f4e2eb65b9fcc246fc5d3c7ec327f22d)
1 /*
2  * Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <errno.h>
8 #include <getopt.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include <File.h>
14 
15 #include <ZlibCompressionAlgorithm.h>
16 #include <ZstdCompressionAlgorithm.h>
17 
18 
19 extern const char* __progname;
20 const char* kCommandName = __progname;
21 
22 
23 enum CompressionType {
24 	ZlibCompression,
25 	GzipCompression,
26 	ZstdCompression,
27 };
28 
29 
30 static const char* kUsage =
31 	"Usage: %s <options> <input file> <output file>\n"
32 	"Compresses or decompresses (option -d) a file.\n"
33 	"\n"
34 	"Options:\n"
35 	"  -0 ... -9\n"
36 	"      Use compression level 0 ... 9. 0 means no, 9 best compression.\n"
37 	"      Defaults to 9.\n"
38 	"  -d, --decompress\n"
39 	"      Decompress the input file (default is compress).\n"
40 	"  -f <format>\n"
41 	"      Specify the compression format: \"zlib\" (default), \"gzip\"\n"
42 	"      or \"zstd\".\n"
43 	"  -h, --help\n"
44 	"      Print this usage info.\n"
45 	"  -i, --input-stream\n"
46 	"      Use the input stream API (default is output stream API).\n"
47 ;
48 
49 
50 static void
print_usage_and_exit(bool error)51 print_usage_and_exit(bool error)
52 {
53     fprintf(error ? stderr : stdout, kUsage, kCommandName);
54     exit(error ? 1 : 0);
55 }
56 
57 
58 int
main(int argc,const char * const * argv)59 main(int argc, const char* const* argv)
60 {
61 	int compressionLevel = -1;
62 	bool compress = true;
63 	bool useInputStream = false;
64 	CompressionType compressionType = ZlibCompression;
65 
66 	while (true) {
67 		static struct option sLongOptions[] = {
68 			{ "decompress", no_argument, 0, 'd' },
69 			{ "help", no_argument, 0, 'h' },
70 			{ "input-stream", no_argument, 0, 'i' },
71 			{ 0, 0, 0, 0 }
72 		};
73 
74 		opterr = 0; // don't print errors
75 		int c = getopt_long(argc, (char**)argv, "+0123456789df:hi",
76 			sLongOptions, NULL);
77 		if (c == -1)
78 			break;
79 
80 		switch (c) {
81 			case '0':
82 			case '1':
83 			case '2':
84 			case '3':
85 			case '4':
86 			case '5':
87 			case '6':
88 			case '7':
89 			case '8':
90 			case '9':
91 				compressionLevel = c - '0';
92 				break;
93 
94 			case 'h':
95 				print_usage_and_exit(false);
96 				break;
97 
98 			case 'd':
99 				compress = false;
100 				break;
101 
102 			case 'f':
103 				if (strcmp(optarg, "zlib") == 0) {
104 					compressionType = ZlibCompression;
105 				} else if (strcmp(optarg, "gzip") == 0) {
106 					compressionType = GzipCompression;
107 				} else if (strcmp(optarg, "zstd") == 0) {
108 					compressionType = ZstdCompression;
109 				} else {
110 					fprintf(stderr, "Error: Unsupported compression type "
111 						"\"%s\"\n", optarg);
112 					return 1;
113 				}
114 				break;
115 
116 			case 'i':
117 				useInputStream = true;
118 				break;
119 
120 			default:
121 				print_usage_and_exit(true);
122 				break;
123 		}
124 	}
125 
126 	// The remaining arguments are input and output file.
127 	if (optind + 2 != argc)
128 		print_usage_and_exit(true);
129 
130 	const char* inputFilePath = argv[optind++];
131 	const char* outputFilePath = argv[optind++];
132 
133 	// open input file
134 	BFile inputFile;
135 	status_t error = inputFile.SetTo(inputFilePath, B_READ_ONLY);
136 	if (error != B_OK) {
137 		fprintf(stderr, "Error: Failed to open \"%s\": %s\n", inputFilePath,
138 			strerror(errno));
139 		return 1;
140 	}
141 
142 	// open output file
143 	BFile outputFile;
144 	error = outputFile.SetTo(outputFilePath,
145 		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
146 	if (error != B_OK) {
147 		fprintf(stderr, "Error: Failed to open \"%s\": %s\n", outputFilePath,
148 			strerror(errno));
149 		return 1;
150 	}
151 
152 	// create compression algorithm and parameters
153 	BCompressionAlgorithm* compressionAlgorithm;
154 	BCompressionParameters* compressionParameters;
155 	BDecompressionParameters* decompressionParameters;
156 	switch (compressionType) {
157 		case ZlibCompression:
158 		case GzipCompression:
159 		{
160 			if (compressionLevel < 0)
161 				compressionLevel = B_ZLIB_COMPRESSION_DEFAULT;
162 			compressionAlgorithm = new BZlibCompressionAlgorithm;
163 			BZlibCompressionParameters* zlibCompressionParameters
164 				= new BZlibCompressionParameters(compressionLevel);
165 			zlibCompressionParameters->SetGzipFormat(
166 				compressionType == GzipCompression);
167 			compressionParameters = zlibCompressionParameters;
168 			decompressionParameters = new BZlibDecompressionParameters;
169 			break;
170 		}
171 		case ZstdCompression:
172 		{
173 			if (compressionLevel < 0)
174 				compressionLevel = B_ZSTD_COMPRESSION_DEFAULT;
175 			compressionAlgorithm = new BZstdCompressionAlgorithm;
176 			compressionParameters
177 				= new BZstdCompressionParameters(compressionLevel);
178 			decompressionParameters = new BZstdDecompressionParameters;
179 			break;
180 		}
181 	}
182 
183 	if (useInputStream) {
184 		// create input stream
185 		BDataIO* inputStream;
186 		if (compress) {
187 			error = compressionAlgorithm->CreateCompressingInputStream(
188 				&inputFile, compressionParameters, inputStream);
189 		} else {
190 			error = compressionAlgorithm->CreateDecompressingInputStream(
191 				&inputFile, decompressionParameters, inputStream);
192 		}
193 
194 		if (error != B_OK) {
195 			fprintf(stderr, "Error: Failed to create input stream: %s\n",
196 				strerror(error));
197 			return 1;
198 		}
199 
200 		// processing loop
201 		for (;;) {
202 			uint8 buffer[64 * 1024];
203 			ssize_t bytesRead = inputStream->Read(buffer, sizeof(buffer));
204 			if (bytesRead < 0) {
205 				fprintf(stderr, "Error: Failed to read from input stream: %s\n",
206 					strerror(bytesRead));
207 				return 1;
208 			}
209 			if (bytesRead == 0)
210 				break;
211 
212 			error = outputFile.WriteExactly(buffer, bytesRead);
213 			if (error != B_OK) {
214 				fprintf(stderr, "Error: Failed to write to output file: %s\n",
215 					strerror(error));
216 				return 1;
217 			}
218 		}
219 	} else {
220 		// create output stream
221 		BDataIO* outputStream;
222 		if (compress) {
223 			error = compressionAlgorithm->CreateCompressingOutputStream(
224 				&outputFile, compressionParameters, outputStream);
225 		} else {
226 			error = compressionAlgorithm->CreateDecompressingOutputStream(
227 				&outputFile, decompressionParameters, outputStream);
228 		}
229 
230 		if (error != B_OK) {
231 			fprintf(stderr, "Error: Failed to create output stream: %s\n",
232 				strerror(error));
233 			return 1;
234 		}
235 
236 		// processing loop
237 		for (;;) {
238 			uint8 buffer[64 * 1024];
239 			ssize_t bytesRead = inputFile.Read(buffer, sizeof(buffer));
240 			if (bytesRead < 0) {
241 				fprintf(stderr, "Error: Failed to read from input file: %s\n",
242 					strerror(bytesRead));
243 				return 1;
244 			}
245 			if (bytesRead == 0)
246 				break;
247 
248 			error = outputStream->WriteExactly(buffer, bytesRead);
249 			if (error != B_OK) {
250 				fprintf(stderr, "Error: Failed to write to output stream: %s\n",
251 					strerror(error));
252 				return 1;
253 			}
254 		}
255 
256 		// flush the output stream
257 		error = outputStream->Flush();
258 		if (error != B_OK) {
259 			fprintf(stderr, "Error: Failed to flush output stream: %s\n",
260 				strerror(error));
261 			return 1;
262 		}
263 	}
264 
265 	return 0;
266 }
267