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
init(Context * ctx)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
cleanup(Context * ctx)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
fail(Context * ctx,const char * message)39 void fail(Context* ctx, const char* message) {
40 fprintf(stderr, "%s\n", message);
41 exit(1);
42 }
43
main(int argc,char ** argv)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 }
94