Experimental brotli encoding support.
[elinks.git] / src / encoding / brotli.c
blob9dc1fe69cc5cb64c10c6c5f101a80a478949df2c
1 /* Brotli encoding (ENCODING_BROTLI) backend */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdio.h>
8 #include <string.h>
9 #ifdef HAVE_UNISTD_H
10 #include <unistd.h>
11 #endif
13 #ifdef HAVE_BROTLI_DEC_DECODE_H
14 #include <brotli/dec/decode.h>
15 #endif
17 #include <errno.h>
19 #include "elinks.h"
21 #include "encoding/brotli.h"
22 #include "encoding/encoding.h"
23 #include "util/math.h"
24 #include "util/memory.h"
26 struct br_enc_data {
27 BrotliState br_stream;
29 uint8_t *input;
30 uint8_t *output;
32 size_t input_length;
33 size_t output_length;
34 size_t output_pos;
35 size_t input_pos;
37 /* The file descriptor from which we read. */
38 int fdread;
39 int after_end:1;
40 int need_free:1;
43 static int
44 brotli_open(struct stream_encoded *stream, int fd)
46 struct br_enc_data *data = mem_calloc(1, sizeof(*data));
48 stream->data = NULL;
49 if (!data) {
50 return -1;
53 data->fdread = fd;
54 BrotliStateInit(&data->br_stream);
55 stream->data = data;
57 return 0;
60 static int
61 brotli_read_function_fd(void *data, uint8_t *buf, size_t len)
63 struct br_enc_data *enc_data = (struct br_enc_data *)data;
65 return safe_read(enc_data->fdread, buf, len);
68 static int
69 brotli_read_function(void *data, uint8_t *buf, size_t len)
71 struct br_enc_data *enc_data = (struct br_enc_data *)data;
72 size_t l = MIN(len, enc_data->input_length - enc_data->input_pos);
74 memcpy(buf, enc_data->input + enc_data->input_pos, l);
75 enc_data->input_pos += l;
76 return l;
79 static int
80 brotli_write_function(void *data, const uint8_t *buf, size_t len)
82 struct br_enc_data *enc_data = (struct br_enc_data *)data;
84 enc_data->output = mem_alloc(len);
85 if (!enc_data->output) {
86 return -1;
88 memcpy(enc_data->output, buf, len);
89 enc_data->output_length = len;
90 return len;
93 static int
94 brotli_read(struct stream_encoded *stream, unsigned char *buf, int len)
96 struct br_enc_data *enc_data = (struct br_enc_data *) stream->data;
97 BrotliState *s;
98 BrotliInput inp;
99 BrotliOutput outp;
100 size_t l;
101 int error;
103 if (!enc_data) return -1;
104 s = &enc_data->br_stream;
106 assert(len > 0);
108 if (enc_data->after_end) {
109 l = MIN(len, enc_data->output_length - enc_data->output_pos);
110 memcpy(buf, enc_data->output + enc_data->output_pos, l);
111 enc_data->output_pos += l;
112 return l;
115 enc_data->input = NULL;
116 enc_data->input_length = 0;
117 enc_data->output = NULL;
118 enc_data->output_length = 0;
119 enc_data->output_pos = 0;
120 inp.data_ = enc_data;
121 outp.data_ = enc_data;
122 inp.cb_ = brotli_read_function_fd;
123 outp.cb_ = brotli_write_function;
125 error = BrotliDecompressStreaming(inp, outp, 1, s);
126 switch (error) {
127 case BROTLI_RESULT_ERROR:
128 return -1;
129 case BROTLI_RESULT_SUCCESS:
130 enc_data->after_end = 1;
131 case BROTLI_RESULT_NEEDS_MORE_INPUT:
132 default:
133 enc_data->need_free = 1;
134 l = MIN(len, enc_data->output_length - enc_data->output_pos);
135 memcpy(buf, enc_data->output + enc_data->output_pos, l);
136 enc_data->output_pos += l;
137 return l;
141 static unsigned char *
142 brotli_decode_buffer(struct stream_encoded *st, unsigned char *data, int len, int *new_len)
144 struct br_enc_data *enc_data = (struct br_enc_data *)st->data;
145 BrotliInput inp;
146 BrotliOutput outp;
147 BrotliState *stream = &enc_data->br_stream;
148 int error;
149 int finish = (len == 0);
151 *new_len = 0; /* default, left there if an error occurs */
152 enc_data->input = data;
153 enc_data->input_length = len;
154 enc_data->input_pos = 0;
155 enc_data->output = NULL;
156 enc_data->output_length = 0;
157 enc_data->output_pos = 0;
158 inp.data_ = enc_data;
159 outp.data_ = enc_data;
160 inp.cb_ = brotli_read_function;
161 outp.cb_ = brotli_write_function;
162 error = BrotliDecompressStreaming(inp, outp, finish, stream);
164 switch (error) {
165 case BROTLI_RESULT_ERROR:
166 return NULL;
167 case BROTLI_RESULT_SUCCESS:
168 enc_data->after_end = 1;
169 case BROTLI_RESULT_NEEDS_MORE_INPUT:
170 default:
171 *new_len = enc_data->output_length;
172 return enc_data->output;
176 static void
177 brotli_close(struct stream_encoded *stream)
179 struct br_enc_data *data = (struct br_enc_data *) stream->data;
181 if (data) {
182 BrotliStateCleanup(&data->br_stream);
183 if (data->fdread != -1) {
184 close(data->fdread);
186 if (data->need_free) {
187 mem_free_if(data->output);
189 mem_free(data);
190 stream->data = 0;
194 static const unsigned char *const brotli_extensions[] = { ".br", NULL };
196 const struct decoding_backend brotli_decoding_backend = {
197 "brotli",
198 brotli_extensions,
199 brotli_open,
200 brotli_read,
201 brotli_decode_buffer,
202 brotli_close,