2018-03-02 14:49:58 +00:00
|
|
|
/* Copyright 2018 Google Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
Distributed under MIT license.
|
|
|
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2021-08-31 12:07:17 +00:00
|
|
|
|
|
|
|
#if !defined(_WIN32)
|
2018-03-02 14:49:58 +00:00
|
|
|
#include <unistd.h>
|
2021-08-31 12:07:17 +00:00
|
|
|
#else
|
|
|
|
#include <io.h>
|
|
|
|
#define fdopen _fdopen
|
|
|
|
#if !defined(__MINGW32__)
|
|
|
|
#define STDIN_FILENO _fileno(stdin)
|
|
|
|
#define STDOUT_FILENO _fileno(stdout)
|
|
|
|
#endif
|
|
|
|
#endif
|
2018-03-02 14:49:58 +00:00
|
|
|
|
|
|
|
#include <brotli/decode.h>
|
|
|
|
|
|
|
|
#define BUFFER_SIZE (1u << 20)
|
|
|
|
|
|
|
|
typedef struct Context {
|
|
|
|
FILE* fin;
|
|
|
|
FILE* fout;
|
|
|
|
uint8_t* input_buffer;
|
|
|
|
uint8_t* output_buffer;
|
|
|
|
BrotliDecoderState* decoder;
|
|
|
|
} Context;
|
|
|
|
|
|
|
|
void init(Context* ctx) {
|
|
|
|
ctx->fin = 0;
|
|
|
|
ctx->fout = 0;
|
|
|
|
ctx->input_buffer = 0;
|
|
|
|
ctx->output_buffer = 0;
|
|
|
|
ctx->decoder = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cleanup(Context* ctx) {
|
|
|
|
if (ctx->decoder) BrotliDecoderDestroyInstance(ctx->decoder);
|
|
|
|
if (ctx->output_buffer) free(ctx->output_buffer);
|
|
|
|
if (ctx->input_buffer) free(ctx->input_buffer);
|
|
|
|
if (ctx->fout) fclose(ctx->fout);
|
|
|
|
if (ctx->fin) fclose(ctx->fin);
|
|
|
|
}
|
|
|
|
|
|
|
|
void fail(Context* ctx, const char* message) {
|
|
|
|
fprintf(stderr, "%s\n", message);
|
2020-08-26 10:32:27 +00:00
|
|
|
cleanup(ctx);
|
2018-03-02 14:49:58 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
Context ctx;
|
|
|
|
BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
|
|
|
|
size_t available_in;
|
|
|
|
const uint8_t* next_in;
|
|
|
|
size_t available_out = BUFFER_SIZE;
|
|
|
|
uint8_t* next_out;
|
|
|
|
init(&ctx);
|
|
|
|
|
|
|
|
ctx.fin = fdopen(STDIN_FILENO, "rb");
|
|
|
|
if (!ctx.fin) fail(&ctx, "can't open input file");
|
|
|
|
ctx.fout = fdopen(STDOUT_FILENO, "wb");
|
|
|
|
if (!ctx.fout) fail(&ctx, "can't open output file");
|
|
|
|
ctx.input_buffer = (uint8_t*)malloc(BUFFER_SIZE);
|
|
|
|
if (!ctx.input_buffer) fail(&ctx, "out of memory / input buffer");
|
|
|
|
ctx.output_buffer = (uint8_t*)malloc(BUFFER_SIZE);
|
|
|
|
if (!ctx.output_buffer) fail(&ctx, "out of memory / output buffer");
|
|
|
|
ctx.decoder = BrotliDecoderCreateInstance(0, 0, 0);
|
|
|
|
if (!ctx.decoder) fail(&ctx, "out of memory / decoder");
|
|
|
|
BrotliDecoderSetParameter(ctx.decoder, BROTLI_DECODER_PARAM_LARGE_WINDOW, 1);
|
|
|
|
|
|
|
|
next_out = ctx.output_buffer;
|
|
|
|
while (1) {
|
|
|
|
if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
|
|
|
|
if (feof(ctx.fin)) break;
|
|
|
|
available_in = fread(ctx.input_buffer, 1, BUFFER_SIZE, ctx.fin);
|
|
|
|
next_in = ctx.input_buffer;
|
|
|
|
if (ferror(ctx.fin)) break;
|
|
|
|
} else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
|
|
|
|
fwrite(ctx.output_buffer, 1, BUFFER_SIZE, ctx.fout);
|
|
|
|
if (ferror(ctx.fout)) break;
|
|
|
|
available_out = BUFFER_SIZE;
|
|
|
|
next_out = ctx.output_buffer;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
result = BrotliDecoderDecompressStream(
|
|
|
|
ctx.decoder, &available_in, &next_in, &available_out, &next_out, 0);
|
|
|
|
}
|
|
|
|
if (next_out != ctx.output_buffer) {
|
|
|
|
fwrite(ctx.output_buffer, 1, next_out - ctx.output_buffer, ctx.fout);
|
|
|
|
}
|
|
|
|
if ((result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) || ferror(ctx.fout)) {
|
|
|
|
fail(&ctx, "failed to write output");
|
|
|
|
} else if (result != BROTLI_DECODER_RESULT_SUCCESS) {
|
|
|
|
fail(&ctx, "corrupt input");
|
|
|
|
}
|
|
|
|
cleanup(&ctx);
|
|
|
|
return 0;
|
|
|
|
}
|