Tianjie Xu | a5dcb7c | 2018-09-25 12:25:15 -0700 | [diff] [blame] | 1 | /* Copyright 2018 Google Inc. All Rights Reserved. |
| 2 | |
| 3 | Distributed under MIT license. |
| 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT |
| 5 | */ |
| 6 | |
| 7 | #include <stdio.h> |
| 8 | #include <stdlib.h> |
| 9 | #include <unistd.h> |
| 10 | |
| 11 | #include <brotli/decode.h> |
| 12 | |
| 13 | #define BUFFER_SIZE (1u << 20) |
| 14 | |
| 15 | typedef struct Context { |
| 16 | FILE* fin; |
| 17 | FILE* fout; |
| 18 | uint8_t* input_buffer; |
| 19 | uint8_t* output_buffer; |
| 20 | BrotliDecoderState* decoder; |
| 21 | } Context; |
| 22 | |
| 23 | void init(Context* ctx) { |
| 24 | ctx->fin = 0; |
| 25 | ctx->fout = 0; |
| 26 | ctx->input_buffer = 0; |
| 27 | ctx->output_buffer = 0; |
| 28 | ctx->decoder = 0; |
| 29 | } |
| 30 | |
| 31 | void cleanup(Context* ctx) { |
| 32 | if (ctx->decoder) BrotliDecoderDestroyInstance(ctx->decoder); |
| 33 | if (ctx->output_buffer) free(ctx->output_buffer); |
| 34 | if (ctx->input_buffer) free(ctx->input_buffer); |
| 35 | if (ctx->fout) fclose(ctx->fout); |
| 36 | if (ctx->fin) fclose(ctx->fin); |
| 37 | } |
| 38 | |
| 39 | void fail(Context* ctx, const char* message) { |
| 40 | fprintf(stderr, "%s\n", message); |
| 41 | exit(1); |
| 42 | } |
| 43 | |
| 44 | int main(int argc, char** argv) { |
| 45 | Context ctx; |
| 46 | BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; |
| 47 | size_t available_in; |
| 48 | const uint8_t* next_in; |
| 49 | size_t available_out = BUFFER_SIZE; |
| 50 | uint8_t* next_out; |
| 51 | init(&ctx); |
| 52 | |
| 53 | ctx.fin = fdopen(STDIN_FILENO, "rb"); |
| 54 | if (!ctx.fin) fail(&ctx, "can't open input file"); |
| 55 | ctx.fout = fdopen(STDOUT_FILENO, "wb"); |
| 56 | if (!ctx.fout) fail(&ctx, "can't open output file"); |
| 57 | ctx.input_buffer = (uint8_t*)malloc(BUFFER_SIZE); |
| 58 | if (!ctx.input_buffer) fail(&ctx, "out of memory / input buffer"); |
| 59 | ctx.output_buffer = (uint8_t*)malloc(BUFFER_SIZE); |
| 60 | if (!ctx.output_buffer) fail(&ctx, "out of memory / output buffer"); |
| 61 | ctx.decoder = BrotliDecoderCreateInstance(0, 0, 0); |
| 62 | if (!ctx.decoder) fail(&ctx, "out of memory / decoder"); |
| 63 | BrotliDecoderSetParameter(ctx.decoder, BROTLI_DECODER_PARAM_LARGE_WINDOW, 1); |
| 64 | |
| 65 | next_out = ctx.output_buffer; |
| 66 | while (1) { |
| 67 | if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { |
| 68 | if (feof(ctx.fin)) break; |
| 69 | available_in = fread(ctx.input_buffer, 1, BUFFER_SIZE, ctx.fin); |
| 70 | next_in = ctx.input_buffer; |
| 71 | if (ferror(ctx.fin)) break; |
| 72 | } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { |
| 73 | fwrite(ctx.output_buffer, 1, BUFFER_SIZE, ctx.fout); |
| 74 | if (ferror(ctx.fout)) break; |
| 75 | available_out = BUFFER_SIZE; |
| 76 | next_out = ctx.output_buffer; |
| 77 | } else { |
| 78 | break; |
| 79 | } |
| 80 | result = BrotliDecoderDecompressStream( |
| 81 | ctx.decoder, &available_in, &next_in, &available_out, &next_out, 0); |
| 82 | } |
| 83 | if (next_out != ctx.output_buffer) { |
| 84 | fwrite(ctx.output_buffer, 1, next_out - ctx.output_buffer, ctx.fout); |
| 85 | } |
| 86 | if ((result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) || ferror(ctx.fout)) { |
| 87 | fail(&ctx, "failed to write output"); |
| 88 | } else if (result != BROTLI_DECODER_RESULT_SUCCESS) { |
| 89 | fail(&ctx, "corrupt input"); |
| 90 | } |
| 91 | cleanup(&ctx); |
| 92 | return 0; |
| 93 | } |