diff options
Diffstat (limited to 'c/tools/bro.c')
-rw-r--r-- | c/tools/bro.c | 521 |
1 files changed, 521 insertions, 0 deletions
diff --git a/c/tools/bro.c b/c/tools/bro.c new file mode 100644 index 0000000..07cef95 --- /dev/null +++ b/c/tools/bro.c @@ -0,0 +1,521 @@ +/* Copyright 2014 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Example main() function for Brotli library. */ + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> + +#include <brotli/decode.h> +#include <brotli/encode.h> + +#if !defined(_WIN32) +#include <unistd.h> +#include <utime.h> +#else +#include <io.h> +#include <share.h> +#include <sys/utime.h> + +#define MAKE_BINARY(FILENO) (_setmode((FILENO), _O_BINARY), (FILENO)) + +#if !defined(__MINGW32__) +#define STDIN_FILENO MAKE_BINARY(_fileno(stdin)) +#define STDOUT_FILENO MAKE_BINARY(_fileno(stdout)) +#define S_IRUSR S_IREAD +#define S_IWUSR S_IWRITE +#endif + +#define fdopen _fdopen +#define unlink _unlink +#define utimbuf _utimbuf +#define utime _utime + +#define fopen ms_fopen +#define open ms_open + +#define chmod(F, P) (0) +#define chown(F, O, G) (0) + +#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#define fseek _fseeki64 +#define ftell _ftelli64 +#endif + +static FILE* ms_fopen(const char *filename, const char *mode) { + FILE* result = 0; + fopen_s(&result, filename, mode); + return result; +} + +static int ms_open(const char *filename, int oflag, int pmode) { + int result = -1; + _sopen_s(&result, filename, oflag | O_BINARY, _SH_DENYNO, pmode); + return result; +} +#endif /* WIN32 */ + +static int ParseQuality(const char* s, int* quality) { + if (s[0] >= '0' && s[0] <= '9') { + *quality = s[0] - '0'; + if (s[1] >= '0' && s[1] <= '9') { + *quality = *quality * 10 + s[1] - '0'; + return (s[2] == 0) ? 1 : 0; + } + return (s[1] == 0) ? 1 : 0; + } + return 0; +} + +static void ParseArgv(int argc, char **argv, + char **input_path, + char **output_path, + char **dictionary_path, + int *force, + int *quality, + int *decompress, + int *repeat, + int *verbose, + int *lgwin, + int *copy_stat) { + int k; + *force = 0; + *input_path = 0; + *output_path = 0; + *repeat = 1; + *verbose = 0; + *lgwin = 22; + *copy_stat = 1; + { + size_t argv0_len = strlen(argv[0]); + *decompress = + argv0_len >= 5 && strcmp(&argv[0][argv0_len - 5], "unbro") == 0; + } + for (k = 1; k < argc; ++k) { + if (!strcmp("--force", argv[k]) || + !strcmp("-f", argv[k])) { + if (*force != 0) { + goto error; + } + *force = 1; + continue; + } else if (!strcmp("--decompress", argv[k]) || + !strcmp("--uncompress", argv[k]) || + !strcmp("-d", argv[k])) { + *decompress = 1; + continue; + } else if (!strcmp("--verbose", argv[k]) || + !strcmp("-v", argv[k])) { + if (*verbose != 0) { + goto error; + } + *verbose = 1; + continue; + } else if (!strcmp("--no-copy-stat", argv[k])) { + if (*copy_stat == 0) { + goto error; + } + *copy_stat = 0; + continue; + } + if (k < argc - 1) { + if (!strcmp("--input", argv[k]) || + !strcmp("--in", argv[k]) || + !strcmp("-i", argv[k])) { + if (*input_path != 0) { + goto error; + } + *input_path = argv[k + 1]; + ++k; + continue; + } else if (!strcmp("--output", argv[k]) || + !strcmp("--out", argv[k]) || + !strcmp("-o", argv[k])) { + if (*output_path != 0) { + goto error; + } + *output_path = argv[k + 1]; + ++k; + continue; + } else if (!strcmp("--custom-dictionary", argv[k])) { + if (*dictionary_path != 0) { + goto error; + } + *dictionary_path = argv[k + 1]; + ++k; + continue; + } else if (!strcmp("--quality", argv[k]) || + !strcmp("-q", argv[k])) { + if (!ParseQuality(argv[k + 1], quality)) { + goto error; + } + ++k; + continue; + } else if (!strcmp("--repeat", argv[k]) || + !strcmp("-r", argv[k])) { + if (!ParseQuality(argv[k + 1], repeat)) { + goto error; + } + ++k; + continue; + } else if (!strcmp("--window", argv[k]) || + !strcmp("-w", argv[k])) { + if (!ParseQuality(argv[k + 1], lgwin)) { + goto error; + } + if (*lgwin < 10 || *lgwin >= 25) { + goto error; + } + ++k; + continue; + } + } + goto error; + } + return; +error: + fprintf(stderr, + "Usage: %s [--force] [--quality n] [--decompress]" + " [--input filename] [--output filename] [--repeat iters]" + " [--verbose] [--window n] [--custom-dictionary filename]" + " [--no-copy-stat]\n", + argv[0]); + exit(1); +} + +static FILE* OpenInputFile(const char* input_path) { + FILE* f; + if (input_path == 0) { + return fdopen(STDIN_FILENO, "rb"); + } + f = fopen(input_path, "rb"); + if (f == 0) { + perror("fopen"); + exit(1); + } + return f; +} + +static FILE *OpenOutputFile(const char *output_path, const int force) { + int fd; + if (output_path == 0) { + return fdopen(STDOUT_FILENO, "wb"); + } + fd = open(output_path, O_CREAT | (force ? 0 : O_EXCL) | O_WRONLY | O_TRUNC, + S_IRUSR | S_IWUSR); + if (fd < 0) { + if (!force) { + struct stat statbuf; + if (stat(output_path, &statbuf) == 0) { + fprintf(stderr, "output file exists\n"); + exit(1); + } + } + perror("open"); + exit(1); + } + return fdopen(fd, "wb"); +} + +static int64_t FileSize(const char *path) { + FILE *f = fopen(path, "rb"); + int64_t retval; + if (f == NULL) { + return -1; + } + if (fseek(f, 0L, SEEK_END) != 0) { + fclose(f); + return -1; + } + retval = ftell(f); + if (fclose(f) != 0) { + return -1; + } + return retval; +} + +/* Copy file times and permissions. + TODO: this is a "best effort" implementation; honest cross-platform + fully featured implementation is way too hacky; add more hacks by request. */ +static void CopyStat(const char* input_path, const char* output_path) { + struct stat statbuf; + struct utimbuf times; + int res; + if (input_path == 0 || output_path == 0) { + return; + } + if (stat(input_path, &statbuf) != 0) { + return; + } + times.actime = statbuf.st_atime; + times.modtime = statbuf.st_mtime; + utime(output_path, ×); + res = chmod(output_path, statbuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + if (res != 0) + perror("chmod failed"); + res = chown(output_path, (uid_t)-1, statbuf.st_gid); + if (res != 0) + perror("chown failed"); + res = chown(output_path, statbuf.st_uid, (gid_t)-1); + if (res != 0) + perror("chown failed"); +} + +/* Result ownersip is passed to caller. + |*dictionary_size| is set to resulting buffer size. */ +static uint8_t* ReadDictionary(const char* path, size_t* dictionary_size) { + static const int kMaxDictionarySize = (1 << 24) - 16; + FILE *f = fopen(path, "rb"); + int64_t file_size_64; + uint8_t* buffer; + size_t bytes_read; + + if (f == NULL) { + perror("fopen"); + exit(1); + } + + file_size_64 = FileSize(path); + if (file_size_64 == -1) { + fprintf(stderr, "could not get size of dictionary file"); + exit(1); + } + + if (file_size_64 > kMaxDictionarySize) { + fprintf(stderr, "dictionary is larger than maximum allowed: %d\n", + kMaxDictionarySize); + exit(1); + } + *dictionary_size = (size_t)file_size_64; + + buffer = (uint8_t*)malloc(*dictionary_size); + if (!buffer) { + fprintf(stderr, "could not read dictionary: out of memory\n"); + exit(1); + } + bytes_read = fread(buffer, sizeof(uint8_t), *dictionary_size, f); + if (bytes_read != *dictionary_size) { + fprintf(stderr, "could not read dictionary\n"); + exit(1); + } + fclose(f); + return buffer; +} + +static const size_t kFileBufferSize = 65536; + +static int Decompress(FILE* fin, FILE* fout, const char* dictionary_path) { + /* Dictionary should be kept during first rounds of decompression. */ + uint8_t* dictionary = NULL; + uint8_t* input; + uint8_t* output; + size_t available_in; + const uint8_t* next_in; + size_t available_out = kFileBufferSize; + uint8_t* next_out; + BrotliDecoderResult result = BROTLI_DECODER_RESULT_ERROR; + BrotliDecoderState* s = BrotliDecoderCreateInstance(NULL, NULL, NULL); + if (!s) { + fprintf(stderr, "out of memory\n"); + return 0; + } + if (dictionary_path != NULL) { + size_t dictionary_size = 0; + dictionary = ReadDictionary(dictionary_path, &dictionary_size); + BrotliDecoderSetCustomDictionary(s, dictionary_size, dictionary); + } + input = (uint8_t*)malloc(kFileBufferSize); + output = (uint8_t*)malloc(kFileBufferSize); + if (!input || !output) { + fprintf(stderr, "out of memory\n"); + goto end; + } + next_out = output; + result = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; + while (1) { + if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { + if (feof(fin)) { + break; + } + available_in = fread(input, 1, kFileBufferSize, fin); + next_in = input; + if (ferror(fin)) { + break; + } + } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { + fwrite(output, 1, kFileBufferSize, fout); + if (ferror(fout)) { + break; + } + available_out = kFileBufferSize; + next_out = output; + } else { + break; /* Error or success. */ + } + result = BrotliDecoderDecompressStream( + s, &available_in, &next_in, &available_out, &next_out, 0); + } + if (next_out != output) { + fwrite(output, 1, (size_t)(next_out - output), fout); + } + + if ((result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) || ferror(fout)) { + fprintf(stderr, "failed to write output\n"); + } else if (result != BROTLI_DECODER_RESULT_SUCCESS) { + /* Error or needs more input. */ + fprintf(stderr, "corrupt input\n"); + } + +end: + free(dictionary); + free(input); + free(output); + BrotliDecoderDestroyInstance(s); + return (result == BROTLI_DECODER_RESULT_SUCCESS) ? 1 : 0; +} + +static int Compress(int quality, int lgwin, FILE* fin, FILE* fout, + const char *dictionary_path) { + BrotliEncoderState* s = BrotliEncoderCreateInstance(0, 0, 0); + uint8_t* buffer = (uint8_t*)malloc(kFileBufferSize << 1); + uint8_t* input = buffer; + uint8_t* output = buffer + kFileBufferSize; + size_t available_in = 0; + const uint8_t* next_in = NULL; + size_t available_out = kFileBufferSize; + uint8_t* next_out = output; + int is_eof = 0; + int is_ok = 1; + + if (!s || !buffer) { + is_ok = 0; + goto finish; + } + + BrotliEncoderSetParameter(s, BROTLI_PARAM_QUALITY, (uint32_t)quality); + BrotliEncoderSetParameter(s, BROTLI_PARAM_LGWIN, (uint32_t)lgwin); + if (dictionary_path != NULL) { + size_t dictionary_size = 0; + uint8_t* dictionary = ReadDictionary(dictionary_path, &dictionary_size); + BrotliEncoderSetCustomDictionary(s, dictionary_size, dictionary); + free(dictionary); + } + + while (1) { + if (available_in == 0 && !is_eof) { + available_in = fread(input, 1, kFileBufferSize, fin); + next_in = input; + if (ferror(fin)) break; + is_eof = feof(fin); + } + + if (!BrotliEncoderCompressStream(s, + is_eof ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS, + &available_in, &next_in, &available_out, &next_out, NULL)) { + is_ok = 0; + break; + } + + if (available_out != kFileBufferSize) { + size_t out_size = kFileBufferSize - available_out; + fwrite(output, 1, out_size, fout); + if (ferror(fout)) break; + available_out = kFileBufferSize; + next_out = output; + } + + if (BrotliEncoderIsFinished(s)) break; + } + +finish: + free(buffer); + BrotliEncoderDestroyInstance(s); + + if (!is_ok) { + /* Should detect OOM? */ + fprintf(stderr, "failed to compress data\n"); + return 0; + } else if (ferror(fout)) { + fprintf(stderr, "failed to write output\n"); + return 0; + } else if (ferror(fin)) { + fprintf(stderr, "failed to read input\n"); + return 0; + } + return 1; +} + +int main(int argc, char** argv) { + char *input_path = 0; + char *output_path = 0; + char *dictionary_path = 0; + int force = 0; + int quality = 11; + int decompress = 0; + int repeat = 1; + int verbose = 0; + int lgwin = 0; + int copy_stat = 1; + clock_t clock_start; + int i; + ParseArgv(argc, argv, &input_path, &output_path, &dictionary_path, &force, + &quality, &decompress, &repeat, &verbose, &lgwin, ©_stat); + clock_start = clock(); + for (i = 0; i < repeat; ++i) { + FILE* fin = OpenInputFile(input_path); + FILE* fout = OpenOutputFile(output_path, force || (repeat > 1)); + int is_ok = 0; + if (decompress) { + is_ok = Decompress(fin, fout, dictionary_path); + } else { + is_ok = Compress(quality, lgwin, fin, fout, dictionary_path); + } + if (!is_ok) { + unlink(output_path); + exit(1); + } + if (fclose(fout) != 0) { + perror("fclose"); + exit(1); + } + /* TOCTOU violation, but otherwise it is impossible to set file times. */ + if (copy_stat && (i + 1 == repeat)) { + CopyStat(input_path, output_path); + } + if (fclose(fin) != 0) { + perror("fclose"); + exit(1); + } + } + if (verbose) { + clock_t clock_end = clock(); + double duration = (double)(clock_end - clock_start) / CLOCKS_PER_SEC; + int64_t uncompressed_size; + double uncompressed_bytes_in_MB; + if (duration < 1e-9) { + duration = 1e-9; + } + uncompressed_size = FileSize(decompress ? output_path : input_path); + if (uncompressed_size == -1) { + fprintf(stderr, "failed to determine uncompressed file size\n"); + exit(1); + } + uncompressed_bytes_in_MB = + (double)(repeat * uncompressed_size) / (1024.0 * 1024.0); + if (decompress) { + printf("Brotli decompression speed: "); + } else { + printf("Brotli compression speed: "); + } + printf("%g MB/s\n", uncompressed_bytes_in_MB / duration); + } + return 0; +} |