]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/C/BrotliCompress/tools/bro.c
BaseTools: Copy Brotli algorithm 3rd party source code for tool
[mirror_edk2.git] / BaseTools / Source / C / BrotliCompress / tools / bro.c
CommitLineData
11b7501a
SB
1/* Copyright 2014 Google Inc. All Rights Reserved.\r
2\r
3 Distributed under MIT license.\r
4 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT\r
5*/\r
6\r
7/* Example main() function for Brotli library. */\r
8\r
9#include <fcntl.h>\r
10#include <stdio.h>\r
11#include <stdlib.h>\r
12#include <string.h>\r
13#include <sys/stat.h>\r
14#include <sys/types.h>\r
15#include <time.h>\r
16\r
17#include "../dec/decode.h"\r
18#include "../enc/encode.h"\r
19\r
20#if !defined(_WIN32)\r
21#include <unistd.h>\r
22#else\r
23#include <io.h>\r
24#include <share.h>\r
25\r
26#define MAKE_BINARY(FILENO) (_setmode((FILENO), _O_BINARY), (FILENO))\r
27\r
28#if !defined(__MINGW32__)\r
29#define STDIN_FILENO MAKE_BINARY(_fileno(stdin))\r
30#define STDOUT_FILENO MAKE_BINARY(_fileno(stdout))\r
31#define S_IRUSR S_IREAD\r
32#define S_IWUSR S_IWRITE\r
33#endif\r
34#define fdopen _fdopen\r
35#define unlink _unlink\r
36\r
37#define fopen ms_fopen\r
38#define open ms_open\r
39\r
40#if defined(_MSC_VER) && (_MSC_VER >= 1400)\r
41#define fseek _fseeki64\r
42#define ftell _ftelli64\r
43#endif\r
44\r
45static FILE* ms_fopen(const char *filename, const char *mode) {\r
46 FILE* result = 0;\r
47 fopen_s(&result, filename, mode);\r
48 return result;\r
49}\r
50\r
51static int ms_open(const char *filename, int oflag, int pmode) {\r
52 int result = -1;\r
53 _sopen_s(&result, filename, oflag | O_BINARY, _SH_DENYNO, pmode);\r
54 return result;\r
55}\r
56#endif /* WIN32 */\r
57\r
58static int ParseQuality(const char* s, int* quality) {\r
59 if (s[0] >= '0' && s[0] <= '9') {\r
60 *quality = s[0] - '0';\r
61 if (s[1] >= '0' && s[1] <= '9') {\r
62 *quality = *quality * 10 + s[1] - '0';\r
63 return (s[2] == 0) ? 1 : 0;\r
64 }\r
65 return (s[1] == 0) ? 1 : 0;\r
66 }\r
67 return 0;\r
68}\r
69\r
70static void ParseArgv(int argc, char **argv,\r
71 char **input_path,\r
72 char **output_path,\r
73 char **dictionary_path,\r
74 int *force,\r
75 int *quality,\r
76 int *decompress,\r
77 int *repeat,\r
78 int *verbose,\r
79 int *lgwin) {\r
80 int k;\r
81 *force = 0;\r
82 *input_path = 0;\r
83 *output_path = 0;\r
84 *repeat = 1;\r
85 *verbose = 0;\r
86 *lgwin = 22;\r
87 {\r
88 size_t argv0_len = strlen(argv[0]);\r
89 *decompress =\r
90 argv0_len >= 5 && strcmp(&argv[0][argv0_len - 5], "unbro") == 0;\r
91 }\r
92 for (k = 1; k < argc; ++k) {\r
93 if (!strcmp("--force", argv[k]) ||\r
94 !strcmp("-f", argv[k])) {\r
95 if (*force != 0) {\r
96 goto error;\r
97 }\r
98 *force = 1;\r
99 continue;\r
100 } else if (!strcmp("--decompress", argv[k]) ||\r
101 !strcmp("--uncompress", argv[k]) ||\r
102 !strcmp("-d", argv[k])) {\r
103 *decompress = 1;\r
104 continue;\r
105 } else if (!strcmp("--verbose", argv[k]) ||\r
106 !strcmp("-v", argv[k])) {\r
107 if (*verbose != 0) {\r
108 goto error;\r
109 }\r
110 *verbose = 1;\r
111 continue;\r
112 }\r
113 if (k < argc - 1) {\r
114 if (!strcmp("--input", argv[k]) ||\r
115 !strcmp("--in", argv[k]) ||\r
116 !strcmp("-i", argv[k])) {\r
117 if (*input_path != 0) {\r
118 goto error;\r
119 }\r
120 *input_path = argv[k + 1];\r
121 ++k;\r
122 continue;\r
123 } else if (!strcmp("--output", argv[k]) ||\r
124 !strcmp("--out", argv[k]) ||\r
125 !strcmp("-o", argv[k])) {\r
126 if (*output_path != 0) {\r
127 goto error;\r
128 }\r
129 *output_path = argv[k + 1];\r
130 ++k;\r
131 continue;\r
132 } else if (!strcmp("--custom-dictionary", argv[k])) {\r
133 if (*dictionary_path != 0) {\r
134 goto error;\r
135 }\r
136 *dictionary_path = argv[k + 1];\r
137 ++k;\r
138 continue;\r
139 } else if (!strcmp("--quality", argv[k]) ||\r
140 !strcmp("-q", argv[k])) {\r
141 if (!ParseQuality(argv[k + 1], quality)) {\r
142 goto error;\r
143 }\r
144 ++k;\r
145 continue;\r
146 } else if (!strcmp("--repeat", argv[k]) ||\r
147 !strcmp("-r", argv[k])) {\r
148 if (!ParseQuality(argv[k + 1], repeat)) {\r
149 goto error;\r
150 }\r
151 ++k;\r
152 continue;\r
153 } else if (!strcmp("--window", argv[k]) ||\r
154 !strcmp("-w", argv[k])) {\r
155 if (!ParseQuality(argv[k + 1], lgwin)) {\r
156 goto error;\r
157 }\r
158 if (*lgwin < 10 || *lgwin >= 25) {\r
159 goto error;\r
160 }\r
161 ++k;\r
162 continue;\r
163 }\r
164 }\r
165 goto error;\r
166 }\r
167 return;\r
168error:\r
169 fprintf(stderr,\r
170 "Usage: %s [--force] [--quality n] [--decompress]"\r
171 " [--input filename] [--output filename] [--repeat iters]"\r
172 " [--verbose] [--window n] [--custom-dictionary filename]\n",\r
173 argv[0]);\r
174 exit(1);\r
175}\r
176\r
177static FILE* OpenInputFile(const char* input_path) {\r
178 FILE* f;\r
179 if (input_path == 0) {\r
180 return fdopen(STDIN_FILENO, "rb");\r
181 }\r
182 f = fopen(input_path, "rb");\r
183 if (f == 0) {\r
184 perror("fopen");\r
185 exit(1);\r
186 }\r
187 return f;\r
188}\r
189\r
190static FILE *OpenOutputFile(const char *output_path, const int force) {\r
191 int fd;\r
192 if (output_path == 0) {\r
193 return fdopen(STDOUT_FILENO, "wb");\r
194 }\r
195 fd = open(output_path, O_CREAT | (force ? 0 : O_EXCL) | O_WRONLY | O_TRUNC,\r
196 S_IRUSR | S_IWUSR);\r
197 if (fd < 0) {\r
198 if (!force) {\r
199 struct stat statbuf;\r
200 if (stat(output_path, &statbuf) == 0) {\r
201 fprintf(stderr, "output file exists\n");\r
202 exit(1);\r
203 }\r
204 }\r
205 perror("open");\r
206 exit(1);\r
207 }\r
208 return fdopen(fd, "wb");\r
209}\r
210\r
211static int64_t FileSize(const char *path) {\r
212 FILE *f = fopen(path, "rb");\r
213 int64_t retval;\r
214 if (f == NULL) {\r
215 return -1;\r
216 }\r
217 if (fseek(f, 0L, SEEK_END) != 0) {\r
218 fclose(f);\r
219 return -1;\r
220 }\r
221 retval = ftell(f);\r
222 if (fclose(f) != 0) {\r
223 return -1;\r
224 }\r
225 return retval;\r
226}\r
227\r
228/* Result ownersip is passed to caller.\r
229 |*dictionary_size| is set to resulting buffer size. */\r
230static uint8_t* ReadDictionary(const char* path, size_t* dictionary_size) {\r
231 static const int kMaxDictionarySize = (1 << 24) - 16;\r
232 FILE *f = fopen(path, "rb");\r
233 int64_t file_size_64;\r
234 uint8_t* buffer;\r
235 size_t bytes_read;\r
236\r
237 if (f == NULL) {\r
238 perror("fopen");\r
239 exit(1);\r
240 }\r
241\r
242 file_size_64 = FileSize(path);\r
243 if (file_size_64 == -1) {\r
244 fprintf(stderr, "could not get size of dictionary file");\r
245 exit(1);\r
246 }\r
247\r
248 if (file_size_64 > kMaxDictionarySize) {\r
249 fprintf(stderr, "dictionary is larger than maximum allowed: %d\n",\r
250 kMaxDictionarySize);\r
251 exit(1);\r
252 }\r
253 *dictionary_size = (size_t)file_size_64;\r
254\r
255 buffer = (uint8_t*)malloc(*dictionary_size);\r
256 if (!buffer) {\r
257 fprintf(stderr, "could not read dictionary: out of memory\n");\r
258 exit(1);\r
259 }\r
260 bytes_read = fread(buffer, sizeof(uint8_t), *dictionary_size, f);\r
261 if (bytes_read != *dictionary_size) {\r
262 fprintf(stderr, "could not read dictionary\n");\r
263 exit(1);\r
264 }\r
265 fclose(f);\r
266 return buffer;\r
267}\r
268\r
269static const size_t kFileBufferSize = 65536;\r
270\r
271static int Decompress(FILE* fin, FILE* fout, const char* dictionary_path) {\r
272 /* Dictionary should be kept during first rounds of decompression. */\r
273 uint8_t* dictionary = NULL;\r
274 uint8_t* input;\r
275 uint8_t* output;\r
276 size_t total_out;\r
277 size_t available_in;\r
278 const uint8_t* next_in;\r
279 size_t available_out = kFileBufferSize;\r
280 uint8_t* next_out;\r
281 BrotliResult result = BROTLI_RESULT_ERROR;\r
282 BrotliState* s = BrotliCreateState(NULL, NULL, NULL);\r
283 if (!s) {\r
284 fprintf(stderr, "out of memory\n");\r
285 return 0;\r
286 }\r
287 if (dictionary_path != NULL) {\r
288 size_t dictionary_size = 0;\r
289 dictionary = ReadDictionary(dictionary_path, &dictionary_size);\r
290 BrotliSetCustomDictionary(dictionary_size, dictionary, s);\r
291 }\r
292 input = (uint8_t*)malloc(kFileBufferSize);\r
293 output = (uint8_t*)malloc(kFileBufferSize);\r
294 if (!input || !output) {\r
295 fprintf(stderr, "out of memory\n");\r
296 goto end;\r
297 }\r
298 next_out = output;\r
299 result = BROTLI_RESULT_NEEDS_MORE_INPUT;\r
300 while (1) {\r
301 if (result == BROTLI_RESULT_NEEDS_MORE_INPUT) {\r
302 if (feof(fin)) {\r
303 break;\r
304 }\r
305 available_in = fread(input, 1, kFileBufferSize, fin);\r
306 next_in = input;\r
307 if (ferror(fin)) {\r
308 break;\r
309 }\r
310 } else if (result == BROTLI_RESULT_NEEDS_MORE_OUTPUT) {\r
311 fwrite(output, 1, kFileBufferSize, fout);\r
312 if (ferror(fout)) {\r
313 break;\r
314 }\r
315 available_out = kFileBufferSize;\r
316 next_out = output;\r
317 } else {\r
318 break; /* Error or success. */\r
319 }\r
320 result = BrotliDecompressStream(&available_in, &next_in,\r
321 &available_out, &next_out, &total_out, s);\r
322 }\r
323 if (next_out != output) {\r
324 fwrite(output, 1, (size_t)(next_out - output), fout);\r
325 }\r
326\r
327 if ((result == BROTLI_RESULT_NEEDS_MORE_OUTPUT) || ferror(fout)) {\r
328 fprintf(stderr, "failed to write output\n");\r
329 } else if (result != BROTLI_RESULT_SUCCESS) { /* Error or needs more input. */\r
330 fprintf(stderr, "corrupt input\n");\r
331 }\r
332\r
333end:\r
334 free(dictionary);\r
335 free(input);\r
336 free(output);\r
337 BrotliDestroyState(s);\r
338 return (result == BROTLI_RESULT_SUCCESS) ? 1 : 0;\r
339}\r
340\r
341static int Compress(int quality, int lgwin, FILE* fin, FILE* fout,\r
342 const char *dictionary_path) {\r
343 BrotliEncoderState* s = BrotliEncoderCreateInstance(0, 0, 0);\r
344 uint8_t* buffer = (uint8_t*)malloc(kFileBufferSize << 1);\r
345 uint8_t* input = buffer;\r
346 uint8_t* output = buffer + kFileBufferSize;\r
347 size_t available_in = 0;\r
348 const uint8_t* next_in = NULL;\r
349 size_t available_out = kFileBufferSize;\r
350 uint8_t* next_out = output;\r
351 int is_eof = 0;\r
352 int is_ok = 1;\r
353\r
354 if (!s || !buffer) {\r
355 is_ok = 0;\r
356 goto finish;\r
357 }\r
358\r
359 BrotliEncoderSetParameter(s, BROTLI_PARAM_QUALITY, (uint32_t)quality);\r
360 BrotliEncoderSetParameter(s, BROTLI_PARAM_LGWIN, (uint32_t)lgwin);\r
361 if (dictionary_path != NULL) {\r
362 size_t dictionary_size = 0;\r
363 uint8_t* dictionary = ReadDictionary(dictionary_path, &dictionary_size);\r
364 BrotliEncoderSetCustomDictionary(s, dictionary_size, dictionary);\r
365 free(dictionary);\r
366 }\r
367\r
368 while (1) {\r
369 if (available_in == 0 && !is_eof) {\r
370 available_in = fread(input, 1, kFileBufferSize, fin);\r
371 next_in = input;\r
372 if (ferror(fin)) break;\r
373 is_eof = feof(fin);\r
374 }\r
375\r
376 if (!BrotliEncoderCompressStream(s,\r
377 is_eof ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS,\r
378 &available_in, &next_in, &available_out, &next_out, NULL)) {\r
379 is_ok = 0;\r
380 break;\r
381 }\r
382\r
383 if (available_out != kFileBufferSize) {\r
384 size_t out_size = kFileBufferSize - available_out;\r
385 fwrite(output, 1, out_size, fout);\r
386 if (ferror(fout)) break;\r
387 available_out = kFileBufferSize;\r
388 next_out = output;\r
389 }\r
390\r
391 if (BrotliEncoderIsFinished(s)) break;\r
392 }\r
393\r
394finish:\r
395 free(buffer);\r
396 BrotliEncoderDestroyInstance(s);\r
397\r
398 if (!is_ok) {\r
399 /* Should detect OOM? */\r
400 fprintf(stderr, "failed to compress data\n");\r
401 return 0;\r
402 } else if (ferror(fout)) {\r
403 fprintf(stderr, "failed to write output\n");\r
404 return 0;\r
405 } else if (ferror(fin)) {\r
406 fprintf(stderr, "failed to read input\n");\r
407 return 0;\r
408 }\r
409 return 1;\r
410}\r
411\r
412int main(int argc, char** argv) {\r
413 char *input_path = 0;\r
414 char *output_path = 0;\r
415 char *dictionary_path = 0;\r
416 int force = 0;\r
417 int quality = 11;\r
418 int decompress = 0;\r
419 int repeat = 1;\r
420 int verbose = 0;\r
421 int lgwin = 0;\r
422 clock_t clock_start;\r
423 int i;\r
424 ParseArgv(argc, argv, &input_path, &output_path, &dictionary_path, &force,\r
425 &quality, &decompress, &repeat, &verbose, &lgwin);\r
426 clock_start = clock();\r
427 for (i = 0; i < repeat; ++i) {\r
428 FILE* fin = OpenInputFile(input_path);\r
429 FILE* fout = OpenOutputFile(output_path, force || repeat);\r
430 int is_ok = 0;\r
431 if (decompress) {\r
432 is_ok = Decompress(fin, fout, dictionary_path);\r
433 } else {\r
434 is_ok = Compress(quality, lgwin, fin, fout, dictionary_path);\r
435 }\r
436 if (!is_ok) {\r
437 unlink(output_path);\r
438 exit(1);\r
439 }\r
440 if (fclose(fin) != 0) {\r
441 perror("fclose");\r
442 exit(1);\r
443 }\r
444 if (fclose(fout) != 0) {\r
445 perror("fclose");\r
446 exit(1);\r
447 }\r
448 }\r
449 if (verbose) {\r
450 clock_t clock_end = clock();\r
451 double duration = (double)(clock_end - clock_start) / CLOCKS_PER_SEC;\r
452 int64_t uncompressed_size;\r
453 double uncompressed_bytes_in_MB;\r
454 if (duration < 1e-9) {\r
455 duration = 1e-9;\r
456 }\r
457 uncompressed_size = FileSize(decompress ? output_path : input_path);\r
458 if (uncompressed_size == -1) {\r
459 fprintf(stderr, "failed to determine uncompressed file size\n");\r
460 exit(1);\r
461 }\r
462 uncompressed_bytes_in_MB =\r
463 (double)(repeat * uncompressed_size) / (1024.0 * 1024.0);\r
464 if (decompress) {\r
465 printf("Brotli decompression speed: ");\r
466 } else {\r
467 printf("Brotli compression speed: ");\r
468 }\r
469 printf("%g MB/s\n", uncompressed_bytes_in_MB / duration);\r
470 }\r
471 return 0;\r
472}\r