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 cleanup(ctx);
42 exit(1);
43 }
44
main(int argc,char ** argv)45 int main(int argc, char** argv) {
46 Context ctx;
47 BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
48 size_t available_in;
49 const uint8_t* next_in;
50 size_t available_out = BUFFER_SIZE;
51 uint8_t* next_out;
52 init(&ctx);
53
54 ctx.fin = fdopen(STDIN_FILENO, "rb");
55 if (!ctx.fin) fail(&ctx, "can't open input file");
56 ctx.fout = fdopen(STDOUT_FILENO, "wb");
57 if (!ctx.fout) fail(&ctx, "can't open output file");
58 ctx.input_buffer = (uint8_t*)malloc(BUFFER_SIZE);
59 if (!ctx.input_buffer) fail(&ctx, "out of memory / input buffer");
60 ctx.output_buffer = (uint8_t*)malloc(BUFFER_SIZE);
61 if (!ctx.output_buffer) fail(&ctx, "out of memory / output buffer");
62 ctx.decoder = BrotliDecoderCreateInstance(0, 0, 0);
63 if (!ctx.decoder) fail(&ctx, "out of memory / decoder");
64 BrotliDecoderSetParameter(ctx.decoder, BROTLI_DECODER_PARAM_LARGE_WINDOW, 1);
65
66 next_out = ctx.output_buffer;
67 while (1) {
68 if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
69 if (feof(ctx.fin)) break;
70 available_in = fread(ctx.input_buffer, 1, BUFFER_SIZE, ctx.fin);
71 next_in = ctx.input_buffer;
72 if (ferror(ctx.fin)) break;
73 } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
74 fwrite(ctx.output_buffer, 1, BUFFER_SIZE, ctx.fout);
75 if (ferror(ctx.fout)) break;
76 available_out = BUFFER_SIZE;
77 next_out = ctx.output_buffer;
78 } else {
79 break;
80 }
81 result = BrotliDecoderDecompressStream(
82 ctx.decoder, &available_in, &next_in, &available_out, &next_out, 0);
83 }
84 if (next_out != ctx.output_buffer) {
85 fwrite(ctx.output_buffer, 1, next_out - ctx.output_buffer, ctx.fout);
86 }
87 if ((result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) || ferror(ctx.fout)) {
88 fail(&ctx, "failed to write output");
89 } else if (result != BROTLI_DECODER_RESULT_SUCCESS) {
90 fail(&ctx, "corrupt input");
91 }
92 cleanup(&ctx);
93 return 0;
94 }
95