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 0x8000
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 ZEND_RESULT_CODE
php_http_encoding_enbrotli(int flags
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len
)
44 *encoded_len
= PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(data_len
);
45 *encoded
= emalloc(*encoded_len
+ 1);
47 PHP_HTTP_ENBROTLI_LEVEL_SET(flags
, q
);
48 PHP_HTTP_ENBROTLI_WBITS_SET(flags
, win
);
49 PHP_HTTP_ENBROTLI_MODE_SET(flags
, mode
);
51 rc
= BrotliEncoderCompress(q
, win
, mode
, data_len
, (const unsigned char *) data
, encoded_len
, (unsigned char *) *encoded
);
56 PTR_SET(*encoded
, NULL
);
59 php_error_docref(NULL
, E_WARNING
, "Could not brotli encode data");
63 ZEND_RESULT_CODE
php_http_encoding_debrotli(const char *encoded
, size_t encoded_len
, char **decoded
, size_t *decoded_len
)
65 BrotliDecoderState
*br
;
66 BrotliDecoderResult rc
;
67 php_http_buffer_t buffer
;
75 br
= BrotliDecoderCreateInstance(NULL
, NULL
, NULL
);
80 php_http_buffer_init_ex(&buffer
, encoded_len
, PHP_HTTP_BUFFER_INIT_PREALLOC
);
83 if (PHP_HTTP_BUFFER_NOMEM
== php_http_buffer_resize_ex(&buffer
, buffer
.size
, 0, 1)) {
87 ptr
= (unsigned char *) &buffer
.data
[buffer
.used
];
89 rc
= BrotliDecoderDecompressStream(br
, &encoded_len
, (const unsigned char **) &encoded
, &len
, &ptr
, NULL
);
91 php_http_buffer_account(&buffer
, buffer
.free
- len
);
92 PHP_HTTP_DEBROTLI_BUFFER_SIZE_ALIGN(buffer
.size
);
94 } while ((BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT
== rc
) && ++round
< PHP_HTTP_DEBROTLI_ROUNDS
);
96 BrotliDecoderDestroyInstance(br
);
98 if (rc
== BROTLI_DECODER_RESULT_SUCCESS
) {
99 php_http_buffer_shrink(&buffer
);
100 php_http_buffer_fix(&buffer
);
102 *decoded
= buffer
.data
;
103 *decoded_len
= buffer
.used
;
108 php_http_buffer_dtor(&buffer
);
109 php_error_docref(NULL
, E_WARNING
, "Could not brotli decode data: %s", BrotliDecoderErrorString(rc
));
114 struct enbrotli_ctx
{
115 BrotliEncoderState
*br
;
116 php_http_buffer_t buffer
;
119 static php_http_encoding_stream_t
*enbrotli_init(php_http_encoding_stream_t
*s
)
121 BrotliEncoderState
*br
;
122 int q
, win
, mode
, p
= (s
->flags
& PHP_HTTP_ENCODING_STREAM_PERSISTENT
);
123 struct enbrotli_ctx
*ctx
= pemalloc(sizeof(*ctx
), p
);
125 PHP_HTTP_ENBROTLI_LEVEL_SET(s
->flags
, q
);
126 PHP_HTTP_ENBROTLI_WBITS_SET(s
->flags
, win
);
127 PHP_HTTP_ENBROTLI_MODE_SET(s
->flags
, mode
);
129 br
= BrotliEncoderCreateInstance(NULL
, NULL
, NULL
);
131 BrotliEncoderSetParameter(br
, BROTLI_PARAM_QUALITY
, q
);
132 BrotliEncoderSetParameter(br
, BROTLI_PARAM_LGWIN
, win
);
133 BrotliEncoderSetParameter(br
, BROTLI_PARAM_MODE
, mode
);
136 php_http_buffer_init_ex(&ctx
->buffer
, PHP_HTTP_ENBROTLI_BUFFER_SIZE
, p
? PHP_HTTP_BUFFER_INIT_PERSISTENT
: 0);
142 php_error_docref(NULL
, E_WARNING
, "Failed to initialize brotli encoding stream");
146 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
)
148 struct enbrotli_ctx
*ctx
= s
->ctx
;
149 const unsigned char *in_ptr
;
150 unsigned char *out_ptr
;
151 size_t in_len
, out_len
;
154 php_http_buffer_append(&ctx
->buffer
, data
, data_len
);
156 in_len
= ctx
->buffer
.used
;
157 in_ptr
= (unsigned char *) ctx
->buffer
.data
;
158 out_len
= PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(in_len
);
159 out_ptr
= emalloc(out_len
+ 1);
161 *encoded_len
= out_len
;
162 *encoded
= (char *) out_ptr
;
164 rc
= BrotliEncoderCompressStream(ctx
->br
, PHP_HTTP_ENBROTLI_FLUSH_FLAG(s
->flags
), &in_len
, &in_ptr
, &out_len
, &out_ptr
, NULL
);
167 php_http_buffer_cut(&ctx
->buffer
, 0, ctx
->buffer
.used
- in_len
);
169 php_http_buffer_reset(&ctx
->buffer
);
172 *encoded_len
-= out_len
;
173 *encoded
= erealloc(*encoded
, *encoded_len
+ 1);
174 (*encoded
)[*encoded_len
] = '\0';
178 PTR_SET(*encoded
, NULL
);
181 php_error_docref(NULL
, E_WARNING
, "Failed to update brotli encoding stream");
185 static inline ZEND_RESULT_CODE
enbrotli_flush_ex(php_http_encoding_stream_t
*s
, BrotliEncoderOperation op
, char **encoded
, size_t *encoded_len
)
187 struct enbrotli_ctx
*ctx
= s
->ctx
;
190 unsigned char *out_ptr
, *ptr
;
195 out_ptr
= ptr
= emalloc(len
);
198 const unsigned char *empty
= NULL
;
201 rc
= BrotliEncoderCompressStream(ctx
->br
, op
, &unused
, &empty
, &out_len
, &out_ptr
, NULL
);
205 if (!BrotliEncoderHasMoreOutput(ctx
->br
)) {
206 *encoded_len
= len
- out_len
;
207 *encoded
= erealloc(ptr
, *encoded_len
+ 1);
208 (*encoded
)[*encoded_len
] = '\0';
215 ptr
= erealloc(ptr
, len
);
218 } while (++round
< PHP_HTTP_ENBROTLI_ROUNDS
);
224 php_error_docref(NULL
, E_WARNING
, "Failed to flush brotli encoding stream");
228 static ZEND_RESULT_CODE
enbrotli_flush(php_http_encoding_stream_t
*s
, char **encoded
, size_t *encoded_len
)
230 return enbrotli_flush_ex(s
, BROTLI_OPERATION_FLUSH
, encoded
, encoded_len
);
233 static ZEND_RESULT_CODE
enbrotli_finish(php_http_encoding_stream_t
*s
, char **encoded
, size_t *encoded_len
)
235 return enbrotli_flush_ex(s
, BROTLI_OPERATION_FINISH
, encoded
, encoded_len
);
238 static zend_bool
enbrotli_done(php_http_encoding_stream_t
*s
)
240 struct enbrotli_ctx
*ctx
= s
->ctx
;
242 return !ctx
->buffer
.used
&& BrotliEncoderIsFinished(ctx
->br
);
245 static void enbrotli_dtor(php_http_encoding_stream_t
*s
)
248 struct enbrotli_ctx
*ctx
= s
->ctx
;
250 php_http_buffer_dtor(&ctx
->buffer
);
251 BrotliEncoderDestroyInstance(ctx
->br
);
252 pefree(ctx
, (s
->flags
& PHP_HTTP_ENCODING_STREAM_PERSISTENT
));
257 static php_http_encoding_stream_t
*debrotli_init(php_http_encoding_stream_t
*s
)
259 BrotliDecoderState
*br
;
261 br
= BrotliDecoderCreateInstance(NULL
, NULL
, NULL
);
267 php_error_docref(NULL
, E_WARNING
, "Failed to initialize brotli decoding stream");
271 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
)
273 BrotliDecoderState
*br
= s
->ctx
;
274 php_http_buffer_t buffer
;
275 BrotliDecoderResult rc
;
280 php_http_buffer_init_ex(&buffer
, encoded_len
, PHP_HTTP_BUFFER_INIT_PREALLOC
);
283 if (PHP_HTTP_BUFFER_NOMEM
== php_http_buffer_resize_ex(&buffer
, buffer
.size
, 0, 1)) {
287 ptr
= (unsigned char *) &buffer
.data
[buffer
.used
];
289 rc
= BrotliDecoderDecompressStream(br
, &encoded_len
, (const unsigned char **) &encoded
, &len
, &ptr
, NULL
);
291 php_http_buffer_account(&buffer
, buffer
.free
- len
);
292 PHP_HTTP_DEBROTLI_BUFFER_SIZE_ALIGN(buffer
.size
);
294 } while ((BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT
== rc
) && ++round
< PHP_HTTP_DEBROTLI_ROUNDS
);
296 if (rc
!= BROTLI_DECODER_RESULT_ERROR
) {
297 php_http_buffer_shrink(&buffer
);
298 php_http_buffer_fix(&buffer
);
300 *decoded
= buffer
.data
;
301 *decoded_len
= buffer
.used
;
306 php_http_buffer_dtor(&buffer
);
307 php_error_docref(NULL
, E_WARNING
, "Could not brotli decode data: %s", BrotliDecoderErrorString(rc
));
312 static zend_bool
debrotli_done(php_http_encoding_stream_t
*s
)
314 return BrotliDecoderIsFinished(s
->ctx
);
317 static void debrotli_dtor(php_http_encoding_stream_t
*s
)
320 BrotliDecoderDestroyInstance(s
->ctx
);
325 static php_http_encoding_stream_ops_t php_http_encoding_enbrotli_ops
= {
335 php_http_encoding_stream_ops_t
*php_http_encoding_stream_get_enbrotli_ops(void)
337 return &php_http_encoding_enbrotli_ops
;
340 static php_http_encoding_stream_ops_t php_http_encoding_debrotli_ops
= {
350 php_http_encoding_stream_ops_t
*php_http_encoding_stream_get_debrotli_ops(void)
352 return &php_http_encoding_debrotli_ops
;
355 static zend_class_entry
*php_http_enbrotli_stream_class_entry
;
356 zend_class_entry
*php_http_get_enbrotli_stream_class_entry(void)
358 return php_http_enbrotli_stream_class_entry
;
361 static zend_class_entry
*php_http_debrotli_stream_class_entry
;
362 zend_class_entry
*php_http_get_debrotli_stream_class_entry(void)
364 return php_http_debrotli_stream_class_entry
;
367 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnbrotliStream_encode
, 0, 0, 1)
368 ZEND_ARG_INFO(0, data
)
369 ZEND_ARG_INFO(0, flags
)
371 static PHP_METHOD(HttpEnbrotliStream
, encode
)
375 zend_long flags
= PHP_HTTP_ENBROTLI_MODE_GENERIC
| PHP_HTTP_ENBROTLI_WBITS_DEF
| PHP_HTTP_ENBROTLI_LEVEL_DEF
;
377 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str
, &len
, &flags
)) {
378 char *enc_str
= NULL
;
381 if (SUCCESS
== php_http_encoding_enbrotli(flags
, str
, len
, &enc_str
, &enc_len
)) {
383 RETURN_STR(php_http_cs2zs(enc_str
, enc_len
));
385 RETURN_EMPTY_STRING();
391 static zend_function_entry php_http_enbrotli_stream_methods
[] = {
392 PHP_ME(HttpEnbrotliStream
, encode
, ai_HttpEnbrotliStream_encode
, ZEND_ACC_PUBLIC
|ZEND_ACC_STATIC
)
396 ZEND_BEGIN_ARG_INFO_EX(ai_HttpDebrotliStream_decode
, 0, 0, 1)
397 ZEND_ARG_INFO(0, data
)
399 static PHP_METHOD(HttpDebrotliStream
, decode
)
404 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str
, &len
)) {
405 char *enc_str
= NULL
;
408 if (SUCCESS
== php_http_encoding_debrotli(str
, len
, &enc_str
, &enc_len
)) {
410 RETURN_STR(php_http_cs2zs(enc_str
, enc_len
));
412 RETURN_EMPTY_STRING();
419 static zend_function_entry php_http_debrotli_stream_methods
[] = {
420 PHP_ME(HttpDebrotliStream
, decode
, ai_HttpDebrotliStream_decode
, ZEND_ACC_PUBLIC
|ZEND_ACC_STATIC
)
424 PHP_MINIT_FUNCTION(http_encoding_brotli
)
428 memset(&ce
, 0, sizeof(ce
));
429 INIT_NS_CLASS_ENTRY(ce
, "http\\Encoding\\Stream", "Enbrotli", php_http_enbrotli_stream_methods
);
430 php_http_enbrotli_stream_class_entry
= zend_register_internal_class_ex(&ce
, php_http_get_encoding_stream_class_entry());
431 php_http_enbrotli_stream_class_entry
->create_object
= php_http_encoding_stream_object_new
;
433 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("LEVEL_MIN"), PHP_HTTP_ENBROTLI_LEVEL_MIN
);
434 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("LEVEL_DEF"), PHP_HTTP_ENBROTLI_LEVEL_DEF
);
435 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("LEVEL_MAX"), PHP_HTTP_ENBROTLI_LEVEL_MAX
);
436 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("WBITS_MIN"), PHP_HTTP_ENBROTLI_WBITS_MIN
);
437 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("WBITS_DEF"), PHP_HTTP_ENBROTLI_WBITS_DEF
);
438 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("WBITS_MAX"), PHP_HTTP_ENBROTLI_WBITS_MAX
);
439 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("MODE_GENERIC"), PHP_HTTP_ENBROTLI_MODE_GENERIC
);
440 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("MODE_TEXT"), PHP_HTTP_ENBROTLI_MODE_TEXT
);
441 zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry
, ZEND_STRL("MODE_FONT"), PHP_HTTP_ENBROTLI_MODE_FONT
);
443 memset(&ce
, 0, sizeof(ce
));
444 INIT_NS_CLASS_ENTRY(ce
, "http\\Encoding\\Stream", "Debrotli", php_http_debrotli_stream_methods
);
445 php_http_debrotli_stream_class_entry
= zend_register_internal_class_ex(&ce
, php_http_get_encoding_stream_class_entry());
446 php_http_debrotli_stream_class_entry
->create_object
= php_http_encoding_stream_object_new
;
458 * vim600: noet sw=4 ts=4 fdm=marker
459 * vim<600: noet sw=4 ts=4