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