OLD | NEW |
| (Empty) |
1 /* Copyright 2014 Google Inc. All Rights Reserved. | |
2 | |
3 Distributed under MIT license. | |
4 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT | |
5 */ | |
6 | |
7 /* Example main() function for Brotli library. */ | |
8 | |
9 #include <fcntl.h> | |
10 #include <stdio.h> | |
11 #include <sys/stat.h> | |
12 #include <sys/types.h> | |
13 | |
14 #include <cstdlib> | |
15 #include <cstring> | |
16 #include <ctime> | |
17 #include <string> | |
18 | |
19 #include "../dec/decode.h" | |
20 #include "../enc/compressor.h" | |
21 | |
22 #if !defined(_WIN32) | |
23 #include <unistd.h> | |
24 #else | |
25 #include <io.h> | |
26 | |
27 #define STDIN_FILENO _fileno(stdin) | |
28 #define STDOUT_FILENO _fileno(stdout) | |
29 #define S_IRUSR S_IREAD | |
30 #define S_IWUSR S_IWRITE | |
31 #define fdopen _fdopen | |
32 #define unlink _unlink | |
33 | |
34 #define fopen ms_fopen | |
35 #define open ms_open | |
36 | |
37 #if defined(_MSC_VER) && (_MSC_VER >= 1400) | |
38 #define fseek _fseeki64 | |
39 #define ftell _ftelli64 | |
40 #endif | |
41 | |
42 static inline FILE* ms_fopen(const char *filename, const char *mode) { | |
43 FILE* result = 0; | |
44 fopen_s(&result, filename, mode); | |
45 return result; | |
46 } | |
47 | |
48 static inline int ms_open(const char *filename, int oflag, int pmode) { | |
49 int result = -1; | |
50 _sopen_s(&result, filename, oflag | O_BINARY, _SH_DENYNO, pmode); | |
51 return result; | |
52 } | |
53 #endif /* WIN32 */ | |
54 | |
55 | |
56 static bool ParseQuality(const char* s, int* quality) { | |
57 if (s[0] >= '0' && s[0] <= '9') { | |
58 *quality = s[0] - '0'; | |
59 if (s[1] >= '0' && s[1] <= '9') { | |
60 *quality = *quality * 10 + s[1] - '0'; | |
61 return s[2] == 0; | |
62 } | |
63 return s[1] == 0; | |
64 } | |
65 return false; | |
66 } | |
67 | |
68 static void ParseArgv(int argc, char **argv, | |
69 char **input_path, | |
70 char **output_path, | |
71 int *force, | |
72 int *quality, | |
73 int *decompress, | |
74 int *repeat, | |
75 int *verbose, | |
76 int *lgwin) { | |
77 *force = 0; | |
78 *input_path = 0; | |
79 *output_path = 0; | |
80 *repeat = 1; | |
81 *verbose = 0; | |
82 *lgwin = 22; | |
83 { | |
84 size_t argv0_len = strlen(argv[0]); | |
85 *decompress = | |
86 argv0_len >= 5 && strcmp(&argv[0][argv0_len - 5], "unbro") == 0; | |
87 } | |
88 for (int k = 1; k < argc; ++k) { | |
89 if (!strcmp("--force", argv[k]) || | |
90 !strcmp("-f", argv[k])) { | |
91 if (*force != 0) { | |
92 goto error; | |
93 } | |
94 *force = 1; | |
95 continue; | |
96 } else if (!strcmp("--decompress", argv[k]) || | |
97 !strcmp("--uncompress", argv[k]) || | |
98 !strcmp("-d", argv[k])) { | |
99 *decompress = 1; | |
100 continue; | |
101 } else if (!strcmp("--verbose", argv[k]) || | |
102 !strcmp("-v", argv[k])) { | |
103 if (*verbose != 0) { | |
104 goto error; | |
105 } | |
106 *verbose = 1; | |
107 continue; | |
108 } | |
109 if (k < argc - 1) { | |
110 if (!strcmp("--input", argv[k]) || | |
111 !strcmp("--in", argv[k]) || | |
112 !strcmp("-i", argv[k])) { | |
113 if (*input_path != 0) { | |
114 goto error; | |
115 } | |
116 *input_path = argv[k + 1]; | |
117 ++k; | |
118 continue; | |
119 } else if (!strcmp("--output", argv[k]) || | |
120 !strcmp("--out", argv[k]) || | |
121 !strcmp("-o", argv[k])) { | |
122 if (*output_path != 0) { | |
123 goto error; | |
124 } | |
125 *output_path = argv[k + 1]; | |
126 ++k; | |
127 continue; | |
128 } else if (!strcmp("--quality", argv[k]) || | |
129 !strcmp("-q", argv[k])) { | |
130 if (!ParseQuality(argv[k + 1], quality)) { | |
131 goto error; | |
132 } | |
133 ++k; | |
134 continue; | |
135 } else if (!strcmp("--repeat", argv[k]) || | |
136 !strcmp("-r", argv[k])) { | |
137 if (!ParseQuality(argv[k + 1], repeat)) { | |
138 goto error; | |
139 } | |
140 ++k; | |
141 continue; | |
142 } else if (!strcmp("--window", argv[k]) || | |
143 !strcmp("-w", argv[k])) { | |
144 if (!ParseQuality(argv[k + 1], lgwin)) { | |
145 goto error; | |
146 } | |
147 if (*lgwin < 10 || *lgwin >= 25) { | |
148 goto error; | |
149 } | |
150 ++k; | |
151 continue; | |
152 } | |
153 } | |
154 goto error; | |
155 } | |
156 return; | |
157 error: | |
158 fprintf(stderr, | |
159 "Usage: %s [--force] [--quality n] [--decompress]" | |
160 " [--input filename] [--output filename] [--repeat iters]" | |
161 " [--verbose] [--window n]\n", | |
162 argv[0]); | |
163 exit(1); | |
164 } | |
165 | |
166 static FILE* OpenInputFile(const char* input_path) { | |
167 if (input_path == 0) { | |
168 return fdopen(STDIN_FILENO, "rb"); | |
169 } | |
170 FILE* f = fopen(input_path, "rb"); | |
171 if (f == 0) { | |
172 perror("fopen"); | |
173 exit(1); | |
174 } | |
175 return f; | |
176 } | |
177 | |
178 static FILE *OpenOutputFile(const char *output_path, const int force) { | |
179 if (output_path == 0) { | |
180 return fdopen(STDOUT_FILENO, "wb"); | |
181 } | |
182 int excl = force ? 0 : O_EXCL; | |
183 int fd = open(output_path, O_CREAT | excl | O_WRONLY | O_TRUNC, | |
184 S_IRUSR | S_IWUSR); | |
185 if (fd < 0) { | |
186 if (!force) { | |
187 struct stat statbuf; | |
188 if (stat(output_path, &statbuf) == 0) { | |
189 fprintf(stderr, "output file exists\n"); | |
190 exit(1); | |
191 } | |
192 } | |
193 perror("open"); | |
194 exit(1); | |
195 } | |
196 return fdopen(fd, "wb"); | |
197 } | |
198 | |
199 static int64_t FileSize(char *path) { | |
200 FILE *f = fopen(path, "rb"); | |
201 if (f == NULL) { | |
202 return -1; | |
203 } | |
204 if (fseek(f, 0L, SEEK_END) != 0) { | |
205 fclose(f); | |
206 return -1; | |
207 } | |
208 int64_t retval = ftell(f); | |
209 if (fclose(f) != 0) { | |
210 return -1; | |
211 } | |
212 return retval; | |
213 } | |
214 | |
215 static const size_t kFileBufferSize = 65536; | |
216 | |
217 static void Decompresss(FILE* fin, FILE* fout) { | |
218 BrotliState* s = BrotliCreateState(NULL, NULL, NULL); | |
219 if (!s) { | |
220 fprintf(stderr, "out of memory\n"); | |
221 exit(1); | |
222 } | |
223 uint8_t* input = new uint8_t[kFileBufferSize]; | |
224 uint8_t* output = new uint8_t[kFileBufferSize]; | |
225 size_t total_out; | |
226 size_t available_in; | |
227 const uint8_t* next_in; | |
228 size_t available_out = kFileBufferSize; | |
229 uint8_t* next_out = output; | |
230 BrotliResult result = BROTLI_RESULT_NEEDS_MORE_INPUT; | |
231 while (1) { | |
232 if (result == BROTLI_RESULT_NEEDS_MORE_INPUT) { | |
233 if (feof(fin)) { | |
234 break; | |
235 } | |
236 available_in = fread(input, 1, kFileBufferSize, fin); | |
237 next_in = input; | |
238 if (ferror(fin)) { | |
239 break; | |
240 } | |
241 } else if (result == BROTLI_RESULT_NEEDS_MORE_OUTPUT) { | |
242 fwrite(output, 1, kFileBufferSize, fout); | |
243 if (ferror(fout)) { | |
244 break; | |
245 } | |
246 available_out = kFileBufferSize; | |
247 next_out = output; | |
248 } else { | |
249 break; /* Error or success. */ | |
250 } | |
251 result = BrotliDecompressStream(&available_in, &next_in, | |
252 &available_out, &next_out, &total_out, s); | |
253 } | |
254 if (next_out != output) { | |
255 fwrite(output, 1, static_cast<size_t>(next_out - output), fout); | |
256 } | |
257 delete[] input; | |
258 delete[] output; | |
259 BrotliDestroyState(s); | |
260 if ((result == BROTLI_RESULT_NEEDS_MORE_OUTPUT) || ferror(fout)) { | |
261 fprintf(stderr, "failed to write output\n"); | |
262 exit(1); | |
263 } else if (result != BROTLI_RESULT_SUCCESS) { /* Error or needs more input. */ | |
264 fprintf(stderr, "corrupt input\n"); | |
265 exit(1); | |
266 } | |
267 } | |
268 | |
269 int main(int argc, char** argv) { | |
270 char *input_path = 0; | |
271 char *output_path = 0; | |
272 int force = 0; | |
273 int quality = 11; | |
274 int decompress = 0; | |
275 int repeat = 1; | |
276 int verbose = 0; | |
277 int lgwin = 0; | |
278 ParseArgv(argc, argv, &input_path, &output_path, &force, | |
279 &quality, &decompress, &repeat, &verbose, &lgwin); | |
280 const clock_t clock_start = clock(); | |
281 for (int i = 0; i < repeat; ++i) { | |
282 FILE* fin = OpenInputFile(input_path); | |
283 FILE* fout = OpenOutputFile(output_path, force); | |
284 if (decompress) { | |
285 Decompresss(fin, fout); | |
286 } else { | |
287 brotli::BrotliParams params; | |
288 params.lgwin = lgwin; | |
289 params.quality = quality; | |
290 brotli::BrotliFileIn in(fin, 1 << 16); | |
291 brotli::BrotliFileOut out(fout); | |
292 if (!BrotliCompress(params, &in, &out)) { | |
293 fprintf(stderr, "compression failed\n"); | |
294 unlink(output_path); | |
295 exit(1); | |
296 } | |
297 } | |
298 if (fclose(fin) != 0) { | |
299 perror("fclose"); | |
300 exit(1); | |
301 } | |
302 if (fclose(fout) != 0) { | |
303 perror("fclose"); | |
304 exit(1); | |
305 } | |
306 } | |
307 if (verbose) { | |
308 const clock_t clock_end = clock(); | |
309 double duration = | |
310 static_cast<double>(clock_end - clock_start) / CLOCKS_PER_SEC; | |
311 if (duration < 1e-9) { | |
312 duration = 1e-9; | |
313 } | |
314 int64_t uncompressed_size = FileSize(decompress ? output_path : input_path); | |
315 if (uncompressed_size == -1) { | |
316 fprintf(stderr, "failed to determine uncompressed file size\n"); | |
317 exit(1); | |
318 } | |
319 double uncompressed_bytes_in_MB = | |
320 static_cast<double>(repeat * uncompressed_size) / (1024.0 * 1024.0); | |
321 if (decompress) { | |
322 printf("Brotli decompression speed: "); | |
323 } else { | |
324 printf("Brotli compression speed: "); | |
325 } | |
326 printf("%g MB/s\n", uncompressed_bytes_in_MB / duration); | |
327 } | |
328 return 0; | |
329 } | |
OLD | NEW |