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