ignore new headers
[m6w6/ext-http] / src / php_http_encoding_brotli.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #include "php_http_api.h"
14
15 #if PHP_HTTP_HAVE_LIBBROTLI
16
17 #define PHP_HTTP_DEBROTLI_ROUNDS 100
18 #define PHP_HTTP_ENBROTLI_ROUNDS 100
19
20 #define PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(S) \
21 BrotliEncoderMaxCompressedSize(S)
22 #define PHP_HTTP_DEBROTLI_BUFFER_SIZE_ALIGN(S) \
23 ((S) += (S) >> 3)
24
25 #define PHP_HTTP_ENBROTLI_FLUSH_FLAG(flags) \
26 PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG((flags), BROTLI_OPERATION_FLUSH, BROTLI_OPERATION_FLUSH, BROTLI_OPERATION_PROCESS)
27
28 #define PHP_HTTP_ENBROTLI_BUFFER_SIZE 0x2000
29 #define PHP_HTTP_DEBROTLI_BUFFER_SIZE 0x1000
30
31 #define PHP_HTTP_ENBROTLI_LEVEL_SET(flags, level) \
32 level = (((flags) & 0xf) ?: PHP_HTTP_ENBROTLI_LEVEL_DEF)
33 #define PHP_HTTP_ENBROTLI_WBITS_SET(flags, wbits) \
34 wbits = ((((flags) >> 4) & 0xff) ?: (PHP_HTTP_ENBROTLI_WBITS_DEF >> 4))
35 #define PHP_HTTP_ENBROTLI_MODE_SET(flags, mode) \
36 mode = (((flags) >> 12) & 0xf)
37
38
39 static php_http_encoding_stream_t *enbrotli_init(php_http_encoding_stream_t *s)
40 {
41 BrotliEncoderState *br;
42 int q, win, mode;
43
44 PHP_HTTP_ENBROTLI_LEVEL_SET(s->flags, q);
45 PHP_HTTP_ENBROTLI_WBITS_SET(s->flags, win);
46 PHP_HTTP_ENBROTLI_MODE_SET(s->flags, mode);
47
48 br = BrotliEncoderCreateInstance(NULL, NULL, NULL);
49 if (EXPECTED(br)) {
50 BrotliEncoderSetParameter(br, BROTLI_PARAM_QUALITY, q);
51 BrotliEncoderSetParameter(br, BROTLI_PARAM_LGWIN, win);
52 BrotliEncoderSetParameter(br, BROTLI_PARAM_MODE, mode);
53
54 s->ctx = br;
55 return s;
56 }
57
58 php_error_docref(NULL, E_WARNING, "Failed to initialize brotli encoding stream");
59 return NULL;
60 }
61
62 static ZEND_RESULT_CODE enbrotli_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **encoded, size_t *encoded_len)
63 {
64 php_http_buffer_t out;
65 const unsigned char *in_ptr;
66 size_t in_len;
67 BROTLI_BOOL rc;
68
69 php_http_buffer_init_ex(&out, PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(data_len), 0);
70
71 in_len = data_len;
72 in_ptr = (const unsigned char *) data;
73
74 while (in_len) {
75 size_t out_len = 0;
76
77 rc = BrotliEncoderCompressStream(s->ctx, PHP_HTTP_ENBROTLI_FLUSH_FLAG(s->flags), &in_len, &in_ptr, &out_len, NULL, NULL);
78
79 if (!rc) {
80 break;
81 }
82
83 if (BrotliEncoderHasMoreOutput(s->ctx)) {
84 const char *out_str = (const char *) BrotliEncoderTakeOutput(s->ctx, &out_len);
85
86 php_http_buffer_append(&out, out_str, out_len);
87 }
88 }
89
90 if (rc) {
91 if (out.used) {
92 php_http_buffer_shrink(&out);
93 php_http_buffer_fix(&out);
94 *encoded = out.data;
95 *encoded_len = out.used;
96 } else {
97 *encoded = NULL;
98 *encoded_len = 0;
99 }
100 return SUCCESS;
101 }
102
103 php_http_buffer_dtor(&out);
104
105 *encoded = NULL;
106 *encoded_len = 0;
107
108 php_error_docref(NULL, E_WARNING, "Failed to update brotli encoding stream");
109 return FAILURE;
110 }
111
112 static inline ZEND_RESULT_CODE enbrotli_flush_ex(php_http_encoding_stream_t *s, BrotliEncoderOperation op, char **encoded, size_t *encoded_len)
113 {
114 php_http_buffer_t out;
115 BROTLI_BOOL rc;
116 int round = 0;
117
118 php_http_buffer_init_ex(&out, PHP_HTTP_ENBROTLI_BUFFER_SIZE, 0);
119
120 do {
121 const unsigned char *empty = NULL;
122 size_t unused = 0, out_len = 0;
123
124 rc = BrotliEncoderCompressStream(s->ctx, op, &unused, &empty, &out_len, NULL, NULL);
125
126 if (!rc) {
127 break;
128 }
129
130 if (BrotliEncoderHasMoreOutput(s->ctx)) {
131 const char *out_str = (const char *) BrotliEncoderTakeOutput(s->ctx, &out_len);
132
133 php_http_buffer_append(&out, out_str, out_len);
134 } else {
135 if (out.used) {
136 php_http_buffer_shrink(&out);
137 php_http_buffer_fix(&out);
138
139 *encoded = out.data;
140 *encoded_len = out.used;
141 } else {
142 *encoded = NULL;
143 *encoded_len = 0;
144 }
145 return SUCCESS;
146 }
147 } while (++round < PHP_HTTP_ENBROTLI_ROUNDS);
148
149 php_http_buffer_dtor(&out);
150
151 *encoded = NULL;
152 *encoded_len = 0;
153
154 php_error_docref(NULL, E_WARNING, "Failed to flush brotli encoding stream");
155 return FAILURE;
156 }
157
158 static ZEND_RESULT_CODE enbrotli_flush(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len)
159 {
160 return enbrotli_flush_ex(s, BROTLI_OPERATION_FLUSH, encoded, encoded_len);
161 }
162
163 static ZEND_RESULT_CODE enbrotli_finish(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len)
164 {
165 return enbrotli_flush_ex(s, BROTLI_OPERATION_FINISH, encoded, encoded_len);
166 }
167
168 static zend_bool enbrotli_done(php_http_encoding_stream_t *s)
169 {
170 return BrotliEncoderIsFinished(s->ctx);
171 }
172
173 static void enbrotli_dtor(php_http_encoding_stream_t *s)
174 {
175 if (s->ctx) {
176 BrotliEncoderDestroyInstance(s->ctx);
177 s->ctx = NULL;
178 }
179 }
180
181 static php_http_encoding_stream_t *debrotli_init(php_http_encoding_stream_t *s)
182 {
183 BrotliDecoderState *br;
184
185 br = BrotliDecoderCreateInstance(NULL, NULL, NULL);
186 if (br) {
187 s->ctx = br;
188 return s;
189 }
190
191 php_error_docref(NULL, E_WARNING, "Failed to initialize brotli decoding stream");
192 return NULL;
193 }
194
195 static ZEND_RESULT_CODE debrotli_update(php_http_encoding_stream_t *s, const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len)
196 {
197 php_http_buffer_t out;
198 BrotliDecoderResult rc;
199 const unsigned char *in_ptr;
200 size_t in_len;
201
202 in_ptr = (const unsigned char *) encoded;
203 in_len = encoded_len;
204
205 php_http_buffer_init_ex(&out, encoded_len, PHP_HTTP_BUFFER_INIT_PREALLOC);
206
207 while (in_len) {
208 size_t out_len = 0;
209
210 rc = BrotliDecoderDecompressStream(s->ctx, &in_len, &in_ptr, &out_len, NULL, NULL);
211
212 if (BROTLI_DECODER_RESULT_ERROR == rc) {
213 break;
214 }
215
216 if (BrotliDecoderHasMoreOutput(s->ctx)) {
217 const char *out_str = (const char *) BrotliDecoderTakeOutput(s->ctx, &out_len);
218
219 php_http_buffer_append(&out, out_str, out_len);
220 }
221 }
222
223 if (BROTLI_DECODER_RESULT_ERROR != rc) {
224 if (out.used) {
225 php_http_buffer_shrink(&out);
226 php_http_buffer_fix(&out);
227 *decoded = out.data;
228 *decoded_len = out.used;
229 } else {
230 *decoded = NULL;
231 *decoded_len = 0;
232 }
233 return SUCCESS;
234 }
235
236 php_http_buffer_dtor(&out);
237
238 php_error_docref(NULL, E_WARNING, "Could not brotli decode data: %s", BrotliDecoderErrorString(rc));
239 return FAILURE;
240 }
241
242 static zend_bool debrotli_done(php_http_encoding_stream_t *s)
243 {
244 return BrotliDecoderIsFinished(s->ctx);
245 }
246
247 static void debrotli_dtor(php_http_encoding_stream_t *s)
248 {
249 if (s->ctx) {
250 BrotliDecoderDestroyInstance(s->ctx);
251 s->ctx = NULL;
252 }
253 }
254
255 static php_http_encoding_stream_ops_t php_http_encoding_enbrotli_ops = {
256 enbrotli_init,
257 NULL,
258 enbrotli_update,
259 enbrotli_flush,
260 enbrotli_done,
261 enbrotli_finish,
262 enbrotli_dtor
263 };
264
265 php_http_encoding_stream_ops_t *php_http_encoding_stream_get_enbrotli_ops(void)
266 {
267 return &php_http_encoding_enbrotli_ops;
268 }
269
270 static php_http_encoding_stream_ops_t php_http_encoding_debrotli_ops = {
271 debrotli_init,
272 NULL,
273 debrotli_update,
274 NULL,
275 debrotli_done,
276 NULL,
277 debrotli_dtor
278 };
279
280 php_http_encoding_stream_ops_t *php_http_encoding_stream_get_debrotli_ops(void)
281 {
282 return &php_http_encoding_debrotli_ops;
283 }
284
285 ZEND_RESULT_CODE php_http_encoding_enbrotli(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len)
286 {
287 BROTLI_BOOL rc;
288 int q, win, mode;
289
290 *encoded_len = PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(data_len);
291 *encoded = emalloc(*encoded_len + 1);
292
293 PHP_HTTP_ENBROTLI_LEVEL_SET(flags, q);
294 PHP_HTTP_ENBROTLI_WBITS_SET(flags, win);
295 PHP_HTTP_ENBROTLI_MODE_SET(flags, mode);
296
297 rc = BrotliEncoderCompress(q, win, mode, data_len, (const unsigned char *) data, encoded_len, (unsigned char *) *encoded);
298 if (rc) {
299 return SUCCESS;
300 }
301
302 PTR_SET(*encoded, NULL);
303 *encoded_len = 0;
304
305 php_error_docref(NULL, E_WARNING, "Could not brotli encode data");
306 return FAILURE;
307 }
308
309 ZEND_RESULT_CODE php_http_encoding_debrotli(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len)
310 {
311 php_http_encoding_stream_t s = {0};
312 ZEND_RESULT_CODE rc = FAILURE;
313
314 if (debrotli_init(&s)) {
315 rc = debrotli_update(&s, encoded, encoded_len, decoded, decoded_len);
316 debrotli_dtor(&s);
317 }
318
319 return rc;
320 }
321
322 static zend_class_entry *php_http_enbrotli_stream_class_entry;
323 zend_class_entry *php_http_get_enbrotli_stream_class_entry(void)
324 {
325 return php_http_enbrotli_stream_class_entry;
326 }
327
328 static zend_class_entry *php_http_debrotli_stream_class_entry;
329 zend_class_entry *php_http_get_debrotli_stream_class_entry(void)
330 {
331 return php_http_debrotli_stream_class_entry;
332 }
333
334 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnbrotliStream_encode, 0, 0, 1)
335 ZEND_ARG_INFO(0, data)
336 ZEND_ARG_INFO(0, flags)
337 ZEND_END_ARG_INFO();
338 static PHP_METHOD(HttpEnbrotliStream, encode)
339 {
340 char *str;
341 size_t len;
342 zend_long flags = PHP_HTTP_ENBROTLI_MODE_GENERIC | PHP_HTTP_ENBROTLI_WBITS_DEF | PHP_HTTP_ENBROTLI_LEVEL_DEF;
343
344 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len, &flags)) {
345 char *enc_str = NULL;
346 size_t enc_len;
347
348 if (SUCCESS == php_http_encoding_enbrotli(flags, str, len, &enc_str, &enc_len)) {
349 if (enc_str) {
350 RETURN_STR(php_http_cs2zs(enc_str, enc_len));
351 } else {
352 RETURN_EMPTY_STRING();
353 }
354 }
355 }
356 RETURN_FALSE;
357 }
358 static zend_function_entry php_http_enbrotli_stream_methods[] = {
359 PHP_ME(HttpEnbrotliStream, encode, ai_HttpEnbrotliStream_encode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
360 EMPTY_FUNCTION_ENTRY
361 };
362
363 ZEND_BEGIN_ARG_INFO_EX(ai_HttpDebrotliStream_decode, 0, 0, 1)
364 ZEND_ARG_INFO(0, data)
365 ZEND_END_ARG_INFO();
366 static PHP_METHOD(HttpDebrotliStream, decode)
367 {
368 char *str;
369 size_t len;
370
371 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len)) {
372 char *enc_str = NULL;
373 size_t enc_len;
374
375 if (SUCCESS == php_http_encoding_debrotli(str, len, &enc_str, &enc_len)) {
376 if (enc_str) {
377 RETURN_STR(php_http_cs2zs(enc_str, enc_len));
378 } else {
379 RETURN_EMPTY_STRING();
380 }
381 }
382 }
383 RETURN_FALSE;
384 }
385
386 static zend_function_entry php_http_debrotli_stream_methods[] = {
387 PHP_ME(HttpDebrotliStream, decode, ai_HttpDebrotliStream_decode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
388 EMPTY_FUNCTION_ENTRY
389 };
390
391 PHP_MINIT_FUNCTION(http_encoding_brotli)
392 {
393 zend_class_entry ce;
394
395 memset(&ce, 0, sizeof(ce));
396 INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Enbrotli", php_http_enbrotli_stream_methods);
397 php_http_enbrotli_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_get_encoding_stream_class_entry());
398 php_http_enbrotli_stream_class_entry->create_object = php_http_encoding_stream_object_new;
399
400 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("LEVEL_MIN"), PHP_HTTP_ENBROTLI_LEVEL_MIN);
401 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("LEVEL_DEF"), PHP_HTTP_ENBROTLI_LEVEL_DEF);
402 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("LEVEL_MAX"), PHP_HTTP_ENBROTLI_LEVEL_MAX);
403 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("WBITS_MIN"), PHP_HTTP_ENBROTLI_WBITS_MIN);
404 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("WBITS_DEF"), PHP_HTTP_ENBROTLI_WBITS_DEF);
405 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("WBITS_MAX"), PHP_HTTP_ENBROTLI_WBITS_MAX);
406 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("MODE_GENERIC"), PHP_HTTP_ENBROTLI_MODE_GENERIC);
407 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("MODE_TEXT"), PHP_HTTP_ENBROTLI_MODE_TEXT);
408 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("MODE_FONT"), PHP_HTTP_ENBROTLI_MODE_FONT);
409
410 memset(&ce, 0, sizeof(ce));
411 INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Debrotli", php_http_debrotli_stream_methods);
412 php_http_debrotli_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_get_encoding_stream_class_entry());
413 php_http_debrotli_stream_class_entry->create_object = php_http_encoding_stream_object_new;
414
415 return SUCCESS;
416 }
417
418 #endif
419
420 /*
421 * Local variables:
422 * tab-width: 4
423 * c-basic-offset: 4
424 * End:
425 * vim600: noet sw=4 ts=4 fdm=marker
426 * vim<600: noet sw=4 ts=4
427 */
428