2 +--------------------------------------------------------------------+
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 +--------------------------------------------------------------------+
13 #include "php_http_api.h"
15 #if PHP_HTTP_HAVE_LIBBROTLI
17 #define PHP_HTTP_DEBROTLI_ROUNDS 100
18 #define PHP_HTTP_ENBROTLI_ROUNDS 100
20 #define PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(S) \
21 BrotliEncoderMaxCompressedSize(S)
22 #define PHP_HTTP_DEBROTLI_BUFFER_SIZE_ALIGN(S) \
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)
28 #define PHP_HTTP_ENBROTLI_BUFFER_SIZE 0x2000
29 #define PHP_HTTP_DEBROTLI_BUFFER_SIZE 0x1000
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)
39 static php_http_encoding_stream_t
*enbrotli_init(php_http_encoding_stream_t
*s
)
41 BrotliEncoderState
*br
;
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
);
48 br
= BrotliEncoderCreateInstance(NULL
, NULL
, NULL
);
50 BrotliEncoderSetParameter(br
, BROTLI_PARAM_QUALITY
, q
);
51 BrotliEncoderSetParameter(br
, BROTLI_PARAM_LGWIN
, win
);
52 BrotliEncoderSetParameter(br
, BROTLI_PARAM_MODE
, mode
);
58 php_error_docref(NULL
, E_WARNING
, "Failed to initialize brotli encoding stream");
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
)
64 php_http_buffer_t out
;
65 const unsigned char *in_ptr
;
69 php_http_buffer_init_ex(&out
, PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(data_len
), 0);
72 in_ptr
= (const unsigned char *) data
;
77 rc
= BrotliEncoderCompressStream(s
->ctx
, PHP_HTTP_ENBROTLI_FLUSH_FLAG(s
->flags
), &in_len
, &in_ptr
, &out_len
, NULL
, NULL
);
83 if (BrotliEncoderHasMoreOutput(s
->ctx
)) {
84 const char *out_str
= (const char *) BrotliEncoderTakeOutput(s
->ctx
, &out_len
);
86 php_http_buffer_append(&out
, out_str
, out_len
);
92 php_http_buffer_shrink(&out
);
93 php_http_buffer_fix(&out
);
95 *encoded_len
= out
.used
;
99 php_http_buffer_dtor(&out
);
104 php_http_buffer_dtor(&out
);
109 php_error_docref(NULL
, E_WARNING
, "Failed to update brotli encoding stream");
113 static inline ZEND_RESULT_CODE
enbrotli_flush_ex(php_http_encoding_stream_t
*s
, BrotliEncoderOperation op
, char **encoded
, size_t *encoded_len
)
115 php_http_buffer_t out
;
119 php_http_buffer_init_ex(&out
, PHP_HTTP_ENBROTLI_BUFFER_SIZE
, 0);
122 const unsigned char *empty
= NULL
;
123 size_t unused
= 0, out_len
= 0;
125 rc
= BrotliEncoderCompressStream(s
->ctx
, op
, &unused
, &empty
, &out_len
, NULL
, NULL
);
131 if (BrotliEncoderHasMoreOutput(s
->ctx
)) {
132 const char *out_str
= (const char *) BrotliEncoderTakeOutput(s
->ctx
, &out_len
);
134 php_http_buffer_append(&out
, out_str
, out_len
);
137 php_http_buffer_shrink(&out
);
138 php_http_buffer_fix(&out
);
141 *encoded_len
= out
.used
;
148 } while (++round
< PHP_HTTP_ENBROTLI_ROUNDS
);
150 php_http_buffer_dtor(&out
);
155 php_error_docref(NULL
, E_WARNING
, "Failed to flush brotli encoding stream");
159 static ZEND_RESULT_CODE
enbrotli_flush(php_http_encoding_stream_t
*s
, char **encoded
, size_t *encoded_len
)
161 return enbrotli_flush_ex(s
, BROTLI_OPERATION_FLUSH
, encoded
, encoded_len
);
164 static ZEND_RESULT_CODE
enbrotli_finish(php_http_encoding_stream_t
*s
, char **encoded
, size_t *encoded_len
)
169 rc
= enbrotli_flush_ex(s
, BROTLI_OPERATION_FINISH
, encoded
, encoded_len
);
170 } while (SUCCESS
== rc
&& !BrotliEncoderIsFinished(s
->ctx
));
175 static zend_bool
enbrotli_done(php_http_encoding_stream_t
*s
)
177 return !(s
->flags
& PHP_HTTP_ENCODING_STREAM_DIRTY
)
178 || BrotliEncoderIsFinished(s
->ctx
);
181 static void enbrotli_dtor(php_http_encoding_stream_t
*s
)
184 BrotliEncoderDestroyInstance(s
->ctx
);
189 static php_http_encoding_stream_t
*debrotli_init(php_http_encoding_stream_t
*s
)
191 BrotliDecoderState
*br
;
193 br
= BrotliDecoderCreateInstance(NULL
, NULL
, NULL
);
199 php_error_docref(NULL
, E_WARNING
, "Failed to initialize brotli decoding stream");
203 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
)
205 php_http_buffer_t out
;
206 BrotliDecoderResult rc
;
207 const unsigned char *in_ptr
;
210 in_ptr
= (const unsigned char *) encoded
;
211 in_len
= encoded_len
;
213 php_http_buffer_init_ex(&out
, encoded_len
, PHP_HTTP_BUFFER_INIT_PREALLOC
);
218 rc
= BrotliDecoderDecompressStream(s
->ctx
, &in_len
, &in_ptr
, &out_len
, NULL
, NULL
);
220 if (BROTLI_DECODER_RESULT_ERROR
== rc
) {
224 if (BrotliDecoderHasMoreOutput(s
->ctx
)) {
225 const char *out_str
= (const char *) BrotliDecoderTakeOutput(s
->ctx
, &out_len
);
227 php_http_buffer_append(&out
, out_str
, out_len
);
231 if (BROTLI_DECODER_RESULT_ERROR
!= rc
) {
233 php_http_buffer_shrink(&out
);
234 php_http_buffer_fix(&out
);
236 *decoded_len
= out
.used
;
238 php_http_buffer_dtor(&out
);
245 php_http_buffer_dtor(&out
);
247 php_error_docref(NULL
, E_WARNING
, "Could not brotli decode data: %s", BrotliDecoderErrorString(BrotliDecoderGetErrorCode(s
->ctx
)));
251 static zend_bool
debrotli_done(php_http_encoding_stream_t
*s
)
253 return !BrotliDecoderIsUsed(s
->ctx
) || BrotliDecoderIsFinished(s
->ctx
);
256 static void debrotli_dtor(php_http_encoding_stream_t
*s
)
259 BrotliDecoderDestroyInstance(s
->ctx
);
264 static php_http_encoding_stream_ops_t php_http_encoding_enbrotli_ops
= {
274 php_http_encoding_stream_ops_t
*php_http_encoding_stream_get_enbrotli_ops(void)
276 return &php_http_encoding_enbrotli_ops
;
279 static php_http_encoding_stream_ops_t php_http_encoding_debrotli_ops
= {
289 php_http_encoding_stream_ops_t
*php_http_encoding_stream_get_debrotli_ops(void)
291 return &php_http_encoding_debrotli_ops
;
294 ZEND_RESULT_CODE
php_http_encoding_enbrotli(int flags
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len
)
299 *encoded_len
= PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(data_len
);
300 *encoded
= emalloc(*encoded_len
+ 1);
302 PHP_HTTP_ENBROTLI_LEVEL_SET(flags
, q
);
303 PHP_HTTP_ENBROTLI_WBITS_SET(flags
, win
);
304 PHP_HTTP_ENBROTLI_MODE_SET(flags
, mode
);
306 rc
= BrotliEncoderCompress(q
, win
, mode
, data_len
, (const unsigned char *) data
, encoded_len
, (unsigned char *) *encoded
);
311 PTR_SET(*encoded
, NULL
);
314 php_error_docref(NULL
, E_WARNING
, "Could not brotli encode data");
318 ZEND_RESULT_CODE
php_http_encoding_debrotli(const char *encoded
, size_t encoded_len
, char **decoded
, size_t *decoded_len
)
320 php_http_encoding_stream_t s
= {0};
321 ZEND_RESULT_CODE rc
= FAILURE
;
323 if (debrotli_init(&s
)) {
324 rc
= debrotli_update(&s
, encoded
, encoded_len
, decoded
, decoded_len
);
331 static zend_class_entry
*php_http_enbrotli_stream_class_entry
;
332 zend_class_entry
*php_http_get_enbrotli_stream_class_entry(void)
334 return php_http_enbrotli_stream_class_entry
;
337 static zend_class_entry
*php_http_debrotli_stream_class_entry
;
338 zend_class_entry
*php_http_get_debrotli_stream_class_entry(void)
340 return php_http_debrotli_stream_class_entry
;
343 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnbrotliStream_encode
, 0, 0, 1)
344 ZEND_ARG_INFO(0, data
)
345 ZEND_ARG_INFO(0, flags
)
347 static PHP_METHOD(HttpEnbrotliStream
, encode
)
351 zend_long flags
= PHP_HTTP_ENBROTLI_MODE_GENERIC
| PHP_HTTP_ENBROTLI_WBITS_DEF
| PHP_HTTP_ENBROTLI_LEVEL_DEF
;
353 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &str
, &len
, &flags
)) {
354 char *enc_str
= NULL
;
357 if (SUCCESS
== php_http_encoding_enbrotli(flags
, str
, len
, &enc_str
, &enc_len
)) {
359 RETURN_STR(php_http_cs2zs(enc_str
, enc_len
));
361 RETURN_EMPTY_STRING();
367 static zend_function_entry php_http_enbrotli_stream_methods
[] = {
368 PHP_ME(HttpEnbrotliStream
, encode
, ai_HttpEnbrotliStream_encode
, ZEND_ACC_PUBLIC
|ZEND_ACC_STATIC
)
372 ZEND_BEGIN_ARG_INFO_EX(ai_HttpDebrotliStream_decode
, 0, 0, 1)
373 ZEND_ARG_INFO(0, data
)
375 static PHP_METHOD(HttpDebrotliStream
, decode
)
380 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str
, &len
)) {
381 char *enc_str
= NULL
;
384 if (SUCCESS
== php_http_encoding_debrotli(str
, len
, &enc_str
, &enc_len
)) {
386 RETURN_STR(php_http_cs2zs(enc_str
, enc_len
));
388 RETURN_EMPTY_STRING();
395 static zend_function_entry php_http_debrotli_stream_methods
[] = {
396 PHP_ME(HttpDebrotliStream
, decode
, ai_HttpDebrotliStream_decode
, ZEND_ACC_PUBLIC
|ZEND_ACC_STATIC
)
401 const void *BrotliGetDictionary();
402 const void *(*php_http_brotli_get_dictionary
)();
404 PHP_MINIT_FUNCTION(http_encoding_brotli
)
408 /* force link to libbrotlicommon, because their libraries don't */
409 php_http_brotli_get_dictionary
= BrotliGetDictionary();
411 memset(&ce
, 0, sizeof(ce
));
412 INIT_NS_CLASS_ENTRY(ce
, "http\\Encoding\\Stream", "Enbrotli", php_http_enbrotli_stream_methods
);
413 php_http_enbrotli_stream_class_entry
= zend_register_internal_class_ex(&ce
, php_http_get_encoding_stream_class_entry());
414 php_http_enbrotli_stream_class_entry
->create_object
= php_http_encoding_stream_object_new
;
416 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("LEVEL_MIN"), PHP_HTTP_ENBROTLI_LEVEL_MIN
);
417 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("LEVEL_DEF"), PHP_HTTP_ENBROTLI_LEVEL_DEF
);
418 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("LEVEL_MAX"), PHP_HTTP_ENBROTLI_LEVEL_MAX
);
419 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("WBITS_MIN"), PHP_HTTP_ENBROTLI_WBITS_MIN
);
420 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("WBITS_DEF"), PHP_HTTP_ENBROTLI_WBITS_DEF
);
421 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("WBITS_MAX"), PHP_HTTP_ENBROTLI_WBITS_MAX
);
422 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("MODE_GENERIC"), PHP_HTTP_ENBROTLI_MODE_GENERIC
);
423 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("MODE_TEXT"), PHP_HTTP_ENBROTLI_MODE_TEXT
);
424 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("MODE_FONT"), PHP_HTTP_ENBROTLI_MODE_FONT
);
426 memset(&ce
, 0, sizeof(ce
));
427 INIT_NS_CLASS_ENTRY(ce
, "http\\Encoding\\Stream", "Debrotli", php_http_debrotli_stream_methods
);
428 php_http_debrotli_stream_class_entry
= zend_register_internal_class_ex(&ce
, php_http_get_encoding_stream_class_entry());
429 php_http_debrotli_stream_class_entry
->create_object
= php_http_encoding_stream_object_new
;
441 * vim600: noet sw=4 ts=4 fdm=marker
442 * vim<600: noet sw=4 ts=4