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>
17 #include "../dec/decode.h"
18 #include "../enc/encode.h"
26 #define MAKE_BINARY(FILENO) (_setmode((FILENO), _O_BINARY), (FILENO))
28 #if !defined(__MINGW32__)
29 #define STDIN_FILENO MAKE_BINARY(_fileno(stdin))
30 #define STDOUT_FILENO MAKE_BINARY(_fileno(stdout))
31 #define S_IRUSR S_IREAD
32 #define S_IWUSR S_IWRITE
34 #define fdopen _fdopen
35 #define unlink _unlink
37 #define fopen ms_fopen
40 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
41 #define fseek _fseeki64
42 #define ftell _ftelli64
45 static FILE* ms_fopen(const char *filename
, const char *mode
) {
47 fopen_s(&result
, filename
, mode
);
51 static int ms_open(const char *filename
, int oflag
, int pmode
) {
53 _sopen_s(&result
, filename
, oflag
| O_BINARY
, _SH_DENYNO
, pmode
);
58 static int ParseQuality(const char* s
, int* quality
) {
59 if (s
[0] >= '0' && s
[0] <= '9') {
60 *quality
= s
[0] - '0';
61 if (s
[1] >= '0' && s
[1] <= '9') {
62 *quality
= *quality
* 10 + s
[1] - '0';
63 return (s
[2] == 0) ? 1 : 0;
65 return (s
[1] == 0) ? 1 : 0;
70 static void ParseArgv(int argc
, char **argv
,
73 char **dictionary_path
,
88 size_t argv0_len
= strlen(argv
[0]);
90 argv0_len
>= 5 && strcmp(&argv
[0][argv0_len
- 5], "unbro") == 0;
92 for (k
= 1; k
< argc
; ++k
) {
93 if (!strcmp("--force", argv
[k
]) ||
94 !strcmp("-f", argv
[k
])) {
100 } else if (!strcmp("--decompress", argv
[k
]) ||
101 !strcmp("--uncompress", argv
[k
]) ||
102 !strcmp("-d", argv
[k
])) {
105 } else if (!strcmp("--verbose", argv
[k
]) ||
106 !strcmp("-v", argv
[k
])) {
114 if (!strcmp("--input", argv
[k
]) ||
115 !strcmp("--in", argv
[k
]) ||
116 !strcmp("-i", argv
[k
])) {
117 if (*input_path
!= 0) {
120 *input_path
= argv
[k
+ 1];
123 } else if (!strcmp("--output", argv
[k
]) ||
124 !strcmp("--out", argv
[k
]) ||
125 !strcmp("-o", argv
[k
])) {
126 if (*output_path
!= 0) {
129 *output_path
= argv
[k
+ 1];
132 } else if (!strcmp("--custom-dictionary", argv
[k
])) {
133 if (*dictionary_path
!= 0) {
136 *dictionary_path
= argv
[k
+ 1];
139 } else if (!strcmp("--quality", argv
[k
]) ||
140 !strcmp("-q", argv
[k
])) {
141 if (!ParseQuality(argv
[k
+ 1], quality
)) {
146 } else if (!strcmp("--repeat", argv
[k
]) ||
147 !strcmp("-r", argv
[k
])) {
148 if (!ParseQuality(argv
[k
+ 1], repeat
)) {
153 } else if (!strcmp("--window", argv
[k
]) ||
154 !strcmp("-w", argv
[k
])) {
155 if (!ParseQuality(argv
[k
+ 1], lgwin
)) {
158 if (*lgwin
< 10 || *lgwin
>= 25) {
170 "Usage: %s [--force] [--quality n] [--decompress]"
171 " [--input filename] [--output filename] [--repeat iters]"
172 " [--verbose] [--window n] [--custom-dictionary filename]\n",
177 static FILE* OpenInputFile(const char* input_path
) {
179 if (input_path
== 0) {
180 return fdopen(STDIN_FILENO
, "rb");
182 f
= fopen(input_path
, "rb");
190 static FILE *OpenOutputFile(const char *output_path
, const int force
) {
192 if (output_path
== 0) {
193 return fdopen(STDOUT_FILENO
, "wb");
195 fd
= open(output_path
, O_CREAT
| (force
? 0 : O_EXCL
) | O_WRONLY
| O_TRUNC
,
200 if (stat(output_path
, &statbuf
) == 0) {
201 fprintf(stderr
, "output file exists\n");
208 return fdopen(fd
, "wb");
211 static int64_t FileSize(const char *path
) {
212 FILE *f
= fopen(path
, "rb");
217 if (fseek(f
, 0L, SEEK_END
) != 0) {
222 if (fclose(f
) != 0) {
228 /* Result ownersip is passed to caller.
229 |*dictionary_size| is set to resulting buffer size. */
230 static uint8_t* ReadDictionary(const char* path
, size_t* dictionary_size
) {
231 static const int kMaxDictionarySize
= (1 << 24) - 16;
232 FILE *f
= fopen(path
, "rb");
233 int64_t file_size_64
;
242 file_size_64
= FileSize(path
);
243 if (file_size_64
== -1) {
244 fprintf(stderr
, "could not get size of dictionary file");
248 if (file_size_64
> kMaxDictionarySize
) {
249 fprintf(stderr
, "dictionary is larger than maximum allowed: %d\n",
253 *dictionary_size
= (size_t)file_size_64
;
255 buffer
= (uint8_t*)malloc(*dictionary_size
);
257 fprintf(stderr
, "could not read dictionary: out of memory\n");
260 bytes_read
= fread(buffer
, sizeof(uint8_t), *dictionary_size
, f
);
261 if (bytes_read
!= *dictionary_size
) {
262 fprintf(stderr
, "could not read dictionary\n");
269 static const size_t kFileBufferSize
= 65536;
271 static int Decompress(FILE* fin
, FILE* fout
, const char* dictionary_path
) {
272 /* Dictionary should be kept during first rounds of decompression. */
273 uint8_t* dictionary
= NULL
;
278 const uint8_t* next_in
;
279 size_t available_out
= kFileBufferSize
;
281 BrotliResult result
= BROTLI_RESULT_ERROR
;
282 BrotliState
* s
= BrotliCreateState(NULL
, NULL
, NULL
);
284 fprintf(stderr
, "out of memory\n");
287 if (dictionary_path
!= NULL
) {
288 size_t dictionary_size
= 0;
289 dictionary
= ReadDictionary(dictionary_path
, &dictionary_size
);
290 BrotliSetCustomDictionary(dictionary_size
, dictionary
, s
);
292 input
= (uint8_t*)malloc(kFileBufferSize
);
293 output
= (uint8_t*)malloc(kFileBufferSize
);
294 if (!input
|| !output
) {
295 fprintf(stderr
, "out of memory\n");
299 result
= BROTLI_RESULT_NEEDS_MORE_INPUT
;
301 if (result
== BROTLI_RESULT_NEEDS_MORE_INPUT
) {
305 available_in
= fread(input
, 1, kFileBufferSize
, fin
);
310 } else if (result
== BROTLI_RESULT_NEEDS_MORE_OUTPUT
) {
311 fwrite(output
, 1, kFileBufferSize
, fout
);
315 available_out
= kFileBufferSize
;
318 break; /* Error or success. */
320 result
= BrotliDecompressStream(&available_in
, &next_in
,
321 &available_out
, &next_out
, &total_out
, s
);
323 if (next_out
!= output
) {
324 fwrite(output
, 1, (size_t)(next_out
- output
), fout
);
327 if ((result
== BROTLI_RESULT_NEEDS_MORE_OUTPUT
) || ferror(fout
)) {
328 fprintf(stderr
, "failed to write output\n");
329 } else if (result
!= BROTLI_RESULT_SUCCESS
) { /* Error or needs more input. */
330 fprintf(stderr
, "corrupt input\n");
337 BrotliDestroyState(s
);
338 return (result
== BROTLI_RESULT_SUCCESS
) ? 1 : 0;
341 static int Compress(int quality
, int lgwin
, FILE* fin
, FILE* fout
,
342 const char *dictionary_path
) {
343 BrotliEncoderState
* s
= BrotliEncoderCreateInstance(0, 0, 0);
344 uint8_t* buffer
= (uint8_t*)malloc(kFileBufferSize
<< 1);
345 uint8_t* input
= buffer
;
346 uint8_t* output
= buffer
+ kFileBufferSize
;
347 size_t available_in
= 0;
348 const uint8_t* next_in
= NULL
;
349 size_t available_out
= kFileBufferSize
;
350 uint8_t* next_out
= output
;
359 BrotliEncoderSetParameter(s
, BROTLI_PARAM_QUALITY
, (uint32_t)quality
);
360 BrotliEncoderSetParameter(s
, BROTLI_PARAM_LGWIN
, (uint32_t)lgwin
);
361 if (dictionary_path
!= NULL
) {
362 size_t dictionary_size
= 0;
363 uint8_t* dictionary
= ReadDictionary(dictionary_path
, &dictionary_size
);
364 BrotliEncoderSetCustomDictionary(s
, dictionary_size
, dictionary
);
369 if (available_in
== 0 && !is_eof
) {
370 available_in
= fread(input
, 1, kFileBufferSize
, fin
);
372 if (ferror(fin
)) break;
376 if (!BrotliEncoderCompressStream(s
,
377 is_eof
? BROTLI_OPERATION_FINISH
: BROTLI_OPERATION_PROCESS
,
378 &available_in
, &next_in
, &available_out
, &next_out
, NULL
)) {
383 if (available_out
!= kFileBufferSize
) {
384 size_t out_size
= kFileBufferSize
- available_out
;
385 fwrite(output
, 1, out_size
, fout
);
386 if (ferror(fout
)) break;
387 available_out
= kFileBufferSize
;
391 if (BrotliEncoderIsFinished(s
)) break;
396 BrotliEncoderDestroyInstance(s
);
399 /* Should detect OOM? */
400 fprintf(stderr
, "failed to compress data\n");
402 } else if (ferror(fout
)) {
403 fprintf(stderr
, "failed to write output\n");
405 } else if (ferror(fin
)) {
406 fprintf(stderr
, "failed to read input\n");
412 int main(int argc
, char** argv
) {
413 char *input_path
= 0;
414 char *output_path
= 0;
415 char *dictionary_path
= 0;
424 ParseArgv(argc
, argv
, &input_path
, &output_path
, &dictionary_path
, &force
,
425 &quality
, &decompress
, &repeat
, &verbose
, &lgwin
);
426 clock_start
= clock();
427 for (i
= 0; i
< repeat
; ++i
) {
428 FILE* fin
= OpenInputFile(input_path
);
429 FILE* fout
= OpenOutputFile(output_path
, force
|| repeat
);
432 is_ok
= Decompress(fin
, fout
, dictionary_path
);
434 is_ok
= Compress(quality
, lgwin
, fin
, fout
, dictionary_path
);
440 if (fclose(fin
) != 0) {
444 if (fclose(fout
) != 0) {
450 clock_t clock_end
= clock();
451 double duration
= (double)(clock_end
- clock_start
) / CLOCKS_PER_SEC
;
452 int64_t uncompressed_size
;
453 double uncompressed_bytes_in_MB
;
454 if (duration
< 1e-9) {
457 uncompressed_size
= FileSize(decompress
? output_path
: input_path
);
458 if (uncompressed_size
== -1) {
459 fprintf(stderr
, "failed to determine uncompressed file size\n");
462 uncompressed_bytes_in_MB
=
463 (double)(repeat
* uncompressed_size
) / (1024.0 * 1024.0);
465 printf("Brotli decompression speed: ");
467 printf("Brotli compression speed: ");
469 printf("%g MB/s\n", uncompressed_bytes_in_MB
/ duration
);