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
;
103 php_http_buffer_dtor(&out
);
108 php_error_docref(NULL
, E_WARNING
, "Failed to update brotli encoding stream");
112 static inline ZEND_RESULT_CODE
enbrotli_flush_ex(php_http_encoding_stream_t
*s
, BrotliEncoderOperation op
, char **encoded
, size_t *encoded_len
)
114 php_http_buffer_t out
;
118 php_http_buffer_init_ex(&out
, PHP_HTTP_ENBROTLI_BUFFER_SIZE
, 0);
121 const unsigned char *empty
= NULL
;
122 size_t unused
= 0, out_len
= 0;
124 rc
= BrotliEncoderCompressStream(s
->ctx
, op
, &unused
, &empty
, &out_len
, NULL
, NULL
);
130 if (BrotliEncoderHasMoreOutput(s
->ctx
)) {
131 const char *out_str
= (const char *) BrotliEncoderTakeOutput(s
->ctx
, &out_len
);
133 php_http_buffer_append(&out
, out_str
, out_len
);
136 php_http_buffer_shrink(&out
);
137 php_http_buffer_fix(&out
);
140 *encoded_len
= out
.used
;
147 } while (++round
< PHP_HTTP_ENBROTLI_ROUNDS
);
149 php_http_buffer_dtor(&out
);
154 php_error_docref(NULL
, E_WARNING
, "Failed to flush brotli encoding stream");
158 static ZEND_RESULT_CODE
enbrotli_flush(php_http_encoding_stream_t
*s
, char **encoded
, size_t *encoded_len
)
160 return enbrotli_flush_ex(s
, BROTLI_OPERATION_FLUSH
, encoded
, encoded_len
);
163 static ZEND_RESULT_CODE
enbrotli_finish(php_http_encoding_stream_t
*s
, char **encoded
, size_t *encoded_len
)
165 return enbrotli_flush_ex(s
, BROTLI_OPERATION_FINISH
, encoded
, encoded_len
);
168 static zend_bool
enbrotli_done(php_http_encoding_stream_t
*s
)
170 return BrotliEncoderIsFinished(s
->ctx
);
173 static void enbrotli_dtor(php_http_encoding_stream_t
*s
)
176 BrotliEncoderDestroyInstance(s
->ctx
);
181 static php_http_encoding_stream_t
*debrotli_init(php_http_encoding_stream_t
*s
)
183 BrotliDecoderState
*br
;
185 br
= BrotliDecoderCreateInstance(NULL
, NULL
, NULL
);
191 php_error_docref(NULL
, E_WARNING
, "Failed to initialize brotli decoding stream");
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
)
197 php_http_buffer_t out
;
198 BrotliDecoderResult rc
;
199 const unsigned char *in_ptr
;
202 in_ptr
= (const unsigned char *) encoded
;
203 in_len
= encoded_len
;
205 php_http_buffer_init_ex(&out
, encoded_len
, PHP_HTTP_BUFFER_INIT_PREALLOC
);
210 rc
= BrotliDecoderDecompressStream(s
->ctx
, &in_len
, &in_ptr
, &out_len
, NULL
, NULL
);
212 if (BROTLI_DECODER_RESULT_ERROR
== rc
) {
216 if (BrotliDecoderHasMoreOutput(s
->ctx
)) {
217 const char *out_str
= (const char *) BrotliDecoderTakeOutput(s
->ctx
, &out_len
);
219 php_http_buffer_append(&out
, out_str
, out_len
);
223 if (BROTLI_DECODER_RESULT_ERROR
!= rc
) {
225 php_http_buffer_shrink(&out
);
226 php_http_buffer_fix(&out
);
228 *decoded_len
= out
.used
;
236 php_http_buffer_dtor(&out
);
238 php_error_docref(NULL
, E_WARNING
, "Could not brotli decode data: %s", BrotliDecoderErrorString(rc
));
242 static zend_bool
debrotli_done(php_http_encoding_stream_t
*s
)
244 return BrotliDecoderIsFinished(s
->ctx
);
247 static void debrotli_dtor(php_http_encoding_stream_t
*s
)
250 BrotliDecoderDestroyInstance(s
->ctx
);
255 static php_http_encoding_stream_ops_t php_http_encoding_enbrotli_ops
= {
265 php_http_encoding_stream_ops_t
*php_http_encoding_stream_get_enbrotli_ops(void)
267 return &php_http_encoding_enbrotli_ops
;
270 static php_http_encoding_stream_ops_t php_http_encoding_debrotli_ops
= {
280 php_http_encoding_stream_ops_t
*php_http_encoding_stream_get_debrotli_ops(void)
282 return &php_http_encoding_debrotli_ops
;
285 ZEND_RESULT_CODE
php_http_encoding_enbrotli(int flags
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len
)
290 *encoded_len
= PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(data_len
);
291 *encoded
= emalloc(*encoded_len
+ 1);
293 PHP_HTTP_ENBROTLI_LEVEL_SET(flags
, q
);
294 PHP_HTTP_ENBROTLI_WBITS_SET(flags
, win
);
295 PHP_HTTP_ENBROTLI_MODE_SET(flags
, mode
);
297 rc
= BrotliEncoderCompress(q
, win
, mode
, data_len
, (const unsigned char *) data
, encoded_len
, (unsigned char *) *encoded
);
302 PTR_SET(*encoded
, NULL
);
305 php_error_docref(NULL
, E_WARNING
, "Could not brotli encode data");
309 ZEND_RESULT_CODE
php_http_encoding_debrotli(const char *encoded
, size_t encoded_len
, char **decoded
, size_t *decoded_len
)
311 php_http_encoding_stream_t s
= {0};
312 ZEND_RESULT_CODE rc
= FAILURE
;
314 if (debrotli_init(&s
)) {
315 rc
= debrotli_update(&s
, encoded
, encoded_len
, decoded
, decoded_len
);
322 static zend_class_entry
*php_http_enbrotli_stream_class_entry
;
323 zend_class_entry
*php_http_get_enbrotli_stream_class_entry(void)
325 return php_http_enbrotli_stream_class_entry
;
328 static zend_class_entry
*php_http_debrotli_stream_class_entry
;
329 zend_class_entry
*php_http_get_debrotli_stream_class_entry(void)
331 return php_http_debrotli_stream_class_entry
;
334 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnbrotliStream_encode
, 0, 0, 1)
335 ZEND_ARG_INFO(0, data
)
336 ZEND_ARG_INFO(0, flags
)
338 static PHP_METHOD(HttpEnbrotliStream
, encode
)
342 zend_long flags
= PHP_HTTP_ENBROTLI_MODE_GENERIC
| PHP_HTTP_ENBROTLI_WBITS_DEF
| PHP_HTTP_ENBROTLI_LEVEL_DEF
;
344 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str
, &len
, &flags
)) {
345 char *enc_str
= NULL
;
348 if (SUCCESS
== php_http_encoding_enbrotli(flags
, str
, len
, &enc_str
, &enc_len
)) {
350 RETURN_STR(php_http_cs2zs(enc_str
, enc_len
));
352 RETURN_EMPTY_STRING();
358 static zend_function_entry php_http_enbrotli_stream_methods
[] = {
359 PHP_ME(HttpEnbrotliStream
, encode
, ai_HttpEnbrotliStream_encode
, ZEND_ACC_PUBLIC
|ZEND_ACC_STATIC
)
363 ZEND_BEGIN_ARG_INFO_EX(ai_HttpDebrotliStream_decode
, 0, 0, 1)
364 ZEND_ARG_INFO(0, data
)
366 static PHP_METHOD(HttpDebrotliStream
, decode
)
371 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str
, &len
)) {
372 char *enc_str
= NULL
;
375 if (SUCCESS
== php_http_encoding_debrotli(str
, len
, &enc_str
, &enc_len
)) {
377 RETURN_STR(php_http_cs2zs(enc_str
, enc_len
));
379 RETURN_EMPTY_STRING();
386 static zend_function_entry php_http_debrotli_stream_methods
[] = {
387 PHP_ME(HttpDebrotliStream
, decode
, ai_HttpDebrotliStream_decode
, ZEND_ACC_PUBLIC
|ZEND_ACC_STATIC
)
391 PHP_MINIT_FUNCTION(http_encoding_brotli
)
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
;
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
);
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
;
425 * vim600: noet sw=4 ts=4 fdm=marker
426 * vim<600: noet sw=4 ts=4