1 /* Copyright 2014 Google Inc. All Rights Reserved.
3 Distributed under MIT license.
4 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
7 /* Example main() function for Brotli library. */
14 #include <sys/types.h>
16 #include <Common/BuildVersion.h>
18 #include "../dec/decode.h"
19 #include "../enc/encode.h"
27 #define MAKE_BINARY(FILENO) (_setmode((FILENO), _O_BINARY), (FILENO))
29 #if !defined(__MINGW32__)
30 #define STDIN_FILENO MAKE_BINARY(_fileno(stdin))
31 #define STDOUT_FILENO MAKE_BINARY(_fileno(stdout))
32 #define S_IRUSR S_IREAD
33 #define S_IWUSR S_IWRITE
35 #define fdopen _fdopen
36 #define unlink _unlink
38 #define fopen ms_fopen
41 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
42 #define fseek _fseeki64
43 #define ftell _ftelli64
46 static FILE* ms_fopen(const char *filename
, const char *mode
) {
48 fopen_s(&result
, filename
, mode
);
52 static int ms_open(const char *filename
, int oflag
, int pmode
) {
54 _sopen_s(&result
, filename
, oflag
| O_BINARY
, _SH_DENYNO
, pmode
);
59 static int ParseQuality(const char* s
, int* quality
) {
60 if (s
[0] >= '0' && s
[0] <= '9') {
61 *quality
= s
[0] - '0';
62 if (s
[1] >= '0' && s
[1] <= '9') {
63 *quality
= *quality
* 10 + s
[1] - '0';
64 return (s
[2] == 0) ? 1 : 0;
66 return (s
[1] == 0) ? 1 : 0;
71 #define UTILITY_NAME "Brotli"
72 #define UTILITY_MAJOR_VERSION 0
73 #define UTILITY_MINOR_VERSION 5
74 #define UTILITY_REVERSION 2
76 static void ParseArgv(int argc
, char **argv
,
79 char **dictionary_path
,
95 size_t argv0_len
= strlen(argv
[0]);
97 argv0_len
>= 5 && strcmp(&argv
[0][argv0_len
- 5], "unbro") == 0;
99 for (k
= 1; k
< argc
; ++k
) {
100 if (!strcmp("--force", argv
[k
]) ||
101 !strcmp("-f", argv
[k
])) {
107 } else if (!strcmp("--decompress", argv
[k
]) ||
108 !strcmp("--uncompress", argv
[k
]) ||
109 !strcmp("-d", argv
[k
])) {
112 } else if (!strcmp("--verbose", argv
[k
]) ||
113 !strcmp("-v", argv
[k
])) {
119 } else if (!strcmp("--version", argv
[k
])) {
121 "%s Version %d.%d.%d %s\n",
123 UTILITY_MAJOR_VERSION
,
124 UTILITY_MINOR_VERSION
,
130 if (!strcmp("--input", argv
[k
]) ||
131 !strcmp("--in", argv
[k
]) ||
132 !strcmp("-i", argv
[k
])) {
133 if (*input_path
!= 0) {
136 *input_path
= argv
[k
+ 1];
139 } else if (!strcmp("--output", argv
[k
]) ||
140 !strcmp("--out", argv
[k
]) ||
141 !strcmp("-o", argv
[k
])) {
142 if (*output_path
!= 0) {
145 *output_path
= argv
[k
+ 1];
148 } else if (!strcmp("--custom-dictionary", argv
[k
])) {
149 if (*dictionary_path
!= 0) {
152 *dictionary_path
= argv
[k
+ 1];
155 } else if (!strcmp("--quality", argv
[k
]) ||
156 !strcmp("-q", argv
[k
])) {
157 if (!ParseQuality(argv
[k
+ 1], quality
)) {
162 } else if (!strcmp("--repeat", argv
[k
]) ||
163 !strcmp("-r", argv
[k
])) {
164 if (!ParseQuality(argv
[k
+ 1], repeat
)) {
169 } else if (!strcmp("--window", argv
[k
]) ||
170 !strcmp("-w", argv
[k
])) {
171 if (!ParseQuality(argv
[k
+ 1], lgwin
)) {
174 if (*lgwin
< 10 || *lgwin
>= 25) {
179 } else if (!strcmp("--gap", argv
[k
]) ||
180 !strcmp("-g", argv
[k
])) {
181 if (!ParseQuality(argv
[k
+ 1], gapmem
)) {
193 "Usage: %s [--force] [--quality n] [--gap n] [--decompress]"
194 " [--input filename] [--output filename] [--repeat iters]"
195 " [--verbose] [--window n] [--custom-dictionary filename]"
201 static FILE* OpenInputFile(const char* input_path
) {
203 if (input_path
== 0) {
204 return fdopen(STDIN_FILENO
, "rb");
206 f
= fopen(input_path
, "rb");
214 static FILE *OpenOutputFile(const char *output_path
, const int force
) {
216 if (output_path
== 0) {
217 return fdopen(STDOUT_FILENO
, "wb");
219 fd
= open(output_path
, O_CREAT
| (force
? 0 : O_EXCL
) | O_WRONLY
| O_TRUNC
,
224 if (stat(output_path
, &statbuf
) == 0) {
225 fprintf(stderr
, "output file exists\n");
232 return fdopen(fd
, "wb");
235 static int64_t FileSize(const char *path
) {
236 FILE *f
= fopen(path
, "rb");
241 if (fseek(f
, 0L, SEEK_END
) != 0) {
246 if (fclose(f
) != 0) {
252 /* Brotli specified memory allocate function */
253 static void *BrAlloc (void *memsize
, size_t size
) {
254 *(int64_t *)memsize
+= size
;
258 /* Brotli specified memory free function */
259 static void BrFree (void *memsize
, void *ptr
) {
263 /* Result ownersip is passed to caller.
264 |*dictionary_size| is set to resulting buffer size. */
265 static uint8_t* ReadDictionary(const char* path
, size_t* dictionary_size
, void *memsize
) {
266 static const int kMaxDictionarySize
= (1 << 24) - 16;
267 FILE *f
= fopen(path
, "rb");
268 int64_t file_size_64
;
277 file_size_64
= FileSize(path
);
278 if (file_size_64
== -1) {
279 fprintf(stderr
, "could not get size of dictionary file");
283 if (file_size_64
> kMaxDictionarySize
) {
284 fprintf(stderr
, "dictionary is larger than maximum allowed: %d\n",
288 *dictionary_size
= (size_t)file_size_64
;
290 buffer
= (uint8_t *)BrAlloc(memsize
, *dictionary_size
);
292 fprintf(stderr
, "could not read dictionary: out of memory\n");
295 bytes_read
= fread(buffer
, sizeof(uint8_t), *dictionary_size
, f
);
296 if (bytes_read
!= *dictionary_size
) {
297 fprintf(stderr
, "could not read dictionary\n");
304 static const size_t kFileBufferSize
= 65536;
306 static int Decompress(FILE* fin
, FILE* fout
, const char* dictionary_path
, void *memsize
) {
307 /* Dictionary should be kept during first rounds of decompression. */
308 uint8_t* dictionary
= NULL
;
313 const uint8_t* next_in
;
314 size_t available_out
= kFileBufferSize
;
316 BrotliResult result
= BROTLI_RESULT_ERROR
;
317 BrotliState
* s
= BrotliCreateState(BrAlloc
, BrFree
, memsize
);
319 fprintf(stderr
, "out of memory\n");
322 if (dictionary_path
!= NULL
) {
323 size_t dictionary_size
= 0;
324 dictionary
= ReadDictionary(dictionary_path
, &dictionary_size
, memsize
);
325 BrotliSetCustomDictionary(dictionary_size
, dictionary
, s
);
327 input
= (uint8_t*)BrAlloc(memsize
, kFileBufferSize
);
328 output
= (uint8_t*)BrAlloc(memsize
, kFileBufferSize
);
329 if (!input
|| !output
) {
330 fprintf(stderr
, "out of memory\n");
334 result
= BROTLI_RESULT_NEEDS_MORE_INPUT
;
336 if (result
== BROTLI_RESULT_NEEDS_MORE_INPUT
) {
340 available_in
= fread(input
, 1, kFileBufferSize
, fin
);
345 } else if (result
== BROTLI_RESULT_NEEDS_MORE_OUTPUT
) {
346 fwrite(output
, 1, kFileBufferSize
, fout
);
350 available_out
= kFileBufferSize
;
353 break; /* Error or success. */
355 result
= BrotliDecompressStream(&available_in
, &next_in
,
356 &available_out
, &next_out
, &total_out
, s
);
358 if (next_out
!= output
) {
359 fwrite(output
, 1, (size_t)(next_out
- output
), fout
);
362 if ((result
== BROTLI_RESULT_NEEDS_MORE_OUTPUT
) || ferror(fout
)) {
363 fprintf(stderr
, "failed to write output\n");
364 } else if (result
!= BROTLI_RESULT_SUCCESS
) { /* Error or needs more input. */
365 fprintf(stderr
, "corrupt input\n");
369 BrFree(memsize
, dictionary
);
370 BrFree(memsize
, input
);
371 BrFree(memsize
, output
);
372 BrotliDestroyState(s
);
373 return (result
== BROTLI_RESULT_SUCCESS
) ? 1 : 0;
376 static int Compress(int quality
, int lgwin
, FILE* fin
, FILE* fout
,
377 const char *dictionary_path
, void *memsize
) {
378 BrotliEncoderState
* s
= BrotliEncoderCreateInstance(BrAlloc
, BrFree
, memsize
);
379 uint8_t* buffer
= (uint8_t*)BrAlloc(memsize
, kFileBufferSize
<< 1);
380 uint8_t* input
= buffer
;
381 uint8_t* output
= buffer
+ kFileBufferSize
;
382 size_t available_in
= 0;
383 const uint8_t* next_in
= NULL
;
384 size_t available_out
= kFileBufferSize
;
385 uint8_t* next_out
= output
;
394 BrotliEncoderSetParameter(s
, BROTLI_PARAM_QUALITY
, (uint32_t)quality
);
395 BrotliEncoderSetParameter(s
, BROTLI_PARAM_LGWIN
, (uint32_t)lgwin
);
396 if (dictionary_path
!= NULL
) {
397 size_t dictionary_size
= 0;
398 uint8_t* dictionary
= ReadDictionary(dictionary_path
, &dictionary_size
, memsize
);
399 BrotliEncoderSetCustomDictionary(s
, dictionary_size
, dictionary
);
400 BrFree(memsize
, dictionary
);
404 if (available_in
== 0 && !is_eof
) {
405 available_in
= fread(input
, 1, kFileBufferSize
, fin
);
407 if (ferror(fin
)) break;
411 if (!BrotliEncoderCompressStream(s
,
412 is_eof
? BROTLI_OPERATION_FINISH
: BROTLI_OPERATION_PROCESS
,
413 &available_in
, &next_in
, &available_out
, &next_out
, NULL
)) {
418 if (available_out
!= kFileBufferSize
) {
419 size_t out_size
= kFileBufferSize
- available_out
;
420 fwrite(output
, 1, out_size
, fout
);
421 if (ferror(fout
)) break;
422 available_out
= kFileBufferSize
;
426 if (BrotliEncoderIsFinished(s
)) break;
430 BrFree(memsize
, buffer
);
431 BrotliEncoderDestroyInstance(s
);
434 /* Should detect OOM? */
435 fprintf(stderr
, "failed to compress data\n");
437 } else if (ferror(fout
)) {
438 fprintf(stderr
, "failed to write output\n");
440 } else if (ferror(fin
)) {
441 fprintf(stderr
, "failed to read input\n");
447 #define GAP_MEM_BLOCK 4096
449 int main(int argc
, char** argv
) {
450 char *input_path
= 0;
451 char *output_path
= 0;
452 char *dictionary_path
= 0;
462 int64_t originsize
= 0;
464 ParseArgv(argc
, argv
, &input_path
, &output_path
, &dictionary_path
, &force
,
465 &quality
, &gmem
, &decompress
, &repeat
, &verbose
, &lgwin
);
466 clock_start
= clock();
467 for (i
= 0; i
< repeat
; ++i
) {
468 FILE* fin
= OpenInputFile(input_path
);
469 FILE* fout
= OpenOutputFile(output_path
, force
|| repeat
);
472 if (fseek(fin
, 16, SEEK_SET
) != 0) {
476 is_ok
= Decompress(fin
, fout
, dictionary_path
, (void *)&msize
);
478 originsize
= FileSize(input_path
); /* get original file size */
479 fwrite(&originsize
, 1, sizeof(int64_t), fout
); /* add in original binary file size */
480 fwrite(&msize
, 1, sizeof(int64_t), fout
); /* add in dummy decompression required memory size */
481 is_ok
= Compress(quality
, lgwin
, fin
, fout
, dictionary_path
, (void *)&msize
);
487 if (fclose(fin
) != 0) {
491 if (fclose(fout
) != 0) {
495 /* after compression operation then execute decompression operation
496 to get decompression required memory size. */
497 if (decompress
== 0) {
498 fin
= OpenInputFile(output_path
);
501 if (fseek(fin
, 16, SEEK_SET
) != 0) {
505 is_ok
= Decompress(fin
, fout
, dictionary_path
, (void *)&msize
);
509 if (fclose(fin
) != 0) {
513 if (fclose(fout
) != 0) {
517 fout
= fopen(output_path
, "rb+"); /* open output_path file and add in head info */
518 /* seek to the offset of decompression required memory size */
519 if (fseek(fout
, 8, SEEK_SET
) != 0) {
523 msize
+= gmem
* GAP_MEM_BLOCK
; /* there is a memory gap between IA32 and X64 environment*/
524 fwrite(&msize
, 1, sizeof(int64_t), fout
); /* update final decompression required memory size */
525 if (fclose(fout
) != 0) {
532 clock_t clock_end
= clock();
533 double duration
= (double)(clock_end
- clock_start
) / CLOCKS_PER_SEC
;
534 int64_t uncompressed_size
;
535 double uncompressed_bytes_in_MB
;
536 if (duration
< 1e-9) {
539 uncompressed_size
= FileSize(decompress
? output_path
: input_path
);
540 if (uncompressed_size
== -1) {
541 fprintf(stderr
, "failed to determine uncompressed file size\n");
544 uncompressed_bytes_in_MB
=
545 (double)(repeat
* uncompressed_size
) / (1024.0 * 1024.0);
547 printf("Brotli decompression speed: ");
549 printf("Brotli compression speed: ");
551 printf("%g MB/s\n", uncompressed_bytes_in_MB
/ duration
);