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