Skip to content

Commit 0e1efc7

Browse files
committed
feat: dictionary support in functions
1 parent e29e59f commit 0e1efc7

File tree

4 files changed

+161
-22
lines changed

4 files changed

+161
-22
lines changed

brotli.c

Lines changed: 109 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,33 +37,43 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_brotli_compress, 0, 1, MAY_BE_ST
3737
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
3838
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, level, IS_LONG, 0, "BROTLI_COMPRESS_LEVEL_DEFAULT")
3939
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, "BROTLI_GENERIC")
40+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, dict, IS_STRING, 1, "null")
4041
#else
4142
ZEND_BEGIN_ARG_INFO_EX(arginfo_brotli_compress, 0, 0, 1)
4243
ZEND_ARG_INFO(0, data)
4344
ZEND_ARG_INFO(0, level)
4445
ZEND_ARG_INFO(0, mode)
46+
ZEND_ARG_INFO(0, dict)
4547
#endif
4648
ZEND_END_ARG_INFO()
4749

4850
#if PHP_VERSION_ID >= 80000
4951
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_brotli_uncompress, 0, 1, MAY_BE_STRING|MAY_BE_FALSE)
5052
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
5153
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, "0")
54+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, dict, IS_STRING, 1, "null")
5255
#else
5356
ZEND_BEGIN_ARG_INFO_EX(arginfo_brotli_uncompress, 0, 0, 1)
5457
ZEND_ARG_INFO(0, data)
5558
ZEND_ARG_INFO(0, length)
59+
#if defined(USE_BROTLI_DICTIONARY)
60+
ZEND_ARG_INFO(0, dict)
61+
#endif
5662
#endif
5763
ZEND_END_ARG_INFO()
5864

5965
#if PHP_VERSION_ID >= 80000
6066
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_brotli_compress_init, 0, 0, Brotli\\Compress\\Context, MAY_BE_FALSE)
6167
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, level, IS_LONG, 0, "BROTLI_COMPRESS_LEVEL_DEFAULT")
6268
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, "BROTLI_GENERIC")
69+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, dict, IS_STRING, 1, "null")
6370
#else
6471
ZEND_BEGIN_ARG_INFO_EX(arginfo_brotli_compress_init, 0, 0, 0)
6572
ZEND_ARG_INFO(0, level)
6673
ZEND_ARG_INFO(0, mode)
74+
#if defined(USE_BROTLI_DICTIONARY)
75+
ZEND_ARG_INFO(0, dict)
76+
#endif
6777
#endif
6878
ZEND_END_ARG_INFO()
6979

@@ -82,8 +92,10 @@ ZEND_END_ARG_INFO()
8292

8393
#if PHP_VERSION_ID >= 80000
8494
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_brotli_uncompress_init, 0, 0, Brotli\\UnCompress\\Context, MAY_BE_FALSE)
95+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, dict, IS_STRING, 1, "null")
8596
#else
8697
ZEND_BEGIN_ARG_INFO_EX(arginfo_brotli_uncompress_init, 0, 0, 0)
98+
ZEND_ARG_INFO(0, dict)
8799
#endif
88100
ZEND_END_ARG_INFO()
89101

@@ -100,6 +112,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_brotli_uncompress_add, 0, 0, 2)
100112
#endif
101113
ZEND_END_ARG_INFO()
102114

115+
#ifndef Z_PARAM_STR_OR_NULL
116+
#define Z_PARAM_STR_OR_NULL(dest) Z_PARAM_STR_EX(dest, 1, 0)
117+
#endif
118+
103119
#if defined(HAVE_APCU_SUPPORT)
104120
static int APC_SERIALIZER_NAME(brotli)(APC_SERIALIZER_ARGS);
105121
static int APC_UNSERIALIZER_NAME(brotli)(APC_UNSERIALIZER_ARGS);
@@ -132,6 +148,9 @@ static const size_t PHP_BROTLI_BUFFER_SIZE = 1 << 19;
132148
struct _php_brotli_context {
133149
BrotliEncoderState *encoder;
134150
BrotliDecoderState *decoder;
151+
#if defined(USE_BROTLI_DICTIONARY)
152+
BrotliEncoderPreparedDictionary *dictionary;
153+
#endif
135154
size_t available_in;
136155
const uint8_t *next_in;
137156
size_t available_out;
@@ -144,6 +163,9 @@ static void php_brotli_context_init(php_brotli_context *ctx)
144163
{
145164
ctx->encoder = NULL;
146165
ctx->decoder = NULL;
166+
#if defined(USE_BROTLI_DICTIONARY)
167+
ctx->dictionary = NULL;
168+
#endif
147169
ctx->available_in = 0;
148170
ctx->next_in = NULL;
149171
ctx->available_out = 0;
@@ -152,8 +174,11 @@ static void php_brotli_context_init(php_brotli_context *ctx)
152174
}
153175

154176
static int php_brotli_context_create_encoder_ex(php_brotli_context *ctx,
155-
long level, int lgwin,
156-
long mode, int fail)
177+
long level,
178+
int lgwin,
179+
long mode,
180+
zend_string *dict,
181+
int fail)
157182
{
158183
ctx->encoder = BrotliEncoderCreateInstance(NULL, NULL, NULL);
159184
if (!ctx->encoder) {
@@ -204,13 +229,37 @@ static int php_brotli_context_create_encoder_ex(php_brotli_context *ctx,
204229
return FAILURE;
205230
}
206231

232+
if (dict) {
233+
#if defined(USE_BROTLI_DICTIONARY)
234+
ctx->dictionary
235+
= BrotliEncoderPrepareDictionary(BROTLI_SHARED_DICTIONARY_RAW,
236+
ZSTR_LEN(dict), ZSTR_VAL(dict),
237+
BROTLI_MAX_QUALITY,
238+
NULL, NULL, NULL);
239+
if (ctx->dictionary == NULL
240+
|| !BrotliEncoderAttachPreparedDictionary(ctx->encoder,
241+
ctx->dictionary)) {
242+
php_error_docref(NULL, E_WARNING,
243+
"%sfailed to set compression dictionary",
244+
(fail ? "" : "brotli: "));
245+
return FAILURE;
246+
}
247+
#else
248+
php_error_docref(NULL, E_WARNING,
249+
"%sfailed to not supported compression dictionary",
250+
(fail ? "" : "brotli: "));
251+
return FAILURE;
252+
#endif
253+
}
254+
207255
return SUCCESS;
208256
}
209257

210258
#define php_brotli_context_create_encoder(ctx, level, lgwin, mode) \
211-
php_brotli_context_create_encoder_ex(ctx, level, lgwin, mode, 0)
259+
php_brotli_context_create_encoder_ex(ctx, level, lgwin, mode, NULL, 0)
212260

213261
static int php_brotli_context_create_decoder_ex(php_brotli_context *ctx,
262+
zend_string *dict,
214263
int fail)
215264
{
216265
ctx->decoder = BrotliDecoderCreateInstance(NULL, NULL, NULL);
@@ -229,11 +278,29 @@ static int php_brotli_context_create_decoder_ex(php_brotli_context *ctx,
229278
return FAILURE;
230279
}
231280

281+
if (dict) {
282+
#if defined(USE_BROTLI_DICTIONARY)
283+
if (!BrotliDecoderAttachDictionary(ctx->decoder,
284+
BROTLI_SHARED_DICTIONARY_RAW,
285+
ZSTR_LEN(dict), ZSTR_VAL(dict))) {
286+
php_error_docref(NULL, E_WARNING,
287+
"%sfailed to set uncompression dictionary",
288+
(fail ? "" : "brotli: "));
289+
return FAILURE;
290+
}
291+
#else
292+
php_error_docref(NULL, E_WARNING,
293+
"%sfailed to not supported uncompression dictionary",
294+
(fail ? "" : "brotli: "));
295+
return FAILURE;
296+
#endif
297+
}
298+
232299
return SUCCESS;
233300
}
234301

235302
#define php_brotli_context_create_decoder(ctx) \
236-
php_brotli_context_create_decoder_ex(ctx, 0)
303+
php_brotli_context_create_decoder_ex(ctx, NULL, 0)
237304

238305
static void php_brotli_context_close(php_brotli_context *ctx)
239306
{
@@ -245,6 +312,12 @@ static void php_brotli_context_close(php_brotli_context *ctx)
245312
BrotliDecoderDestroyInstance(ctx->decoder);
246313
ctx->decoder = NULL;
247314
}
315+
#if defined(USE_BROTLI_DICTIONARY)
316+
if (ctx->dictionary) {
317+
BrotliEncoderDestroyPreparedDictionary(ctx->dictionary);
318+
ctx->dictionary = NULL;
319+
}
320+
#endif
248321

249322
if (ctx->output) {
250323
efree(ctx->output);
@@ -1063,6 +1136,14 @@ ZEND_MINIT_FUNCTION(brotli)
10631136
REGISTER_LONG_CONSTANT("BROTLI_FINISH", BROTLI_OPERATION_FINISH,
10641137
CONST_CS | CONST_PERSISTENT);
10651138

1139+
#if defined(USE_BROTLI_DICTIONARY)
1140+
REGISTER_BOOL_CONSTANT("BROTLI_DICTIONARY_SUPPORT", 1,
1141+
CONST_CS | CONST_PERSISTENT);
1142+
#else
1143+
REGISTER_BOOL_CONSTANT("BROTLI_DICTIONARY_SUPPORT", 0,
1144+
CONST_CS | CONST_PERSISTENT);
1145+
#endif
1146+
10661147
php_output_handler_alias_register(ZEND_STRL(PHP_BROTLI_OUTPUT_HANDLER),
10671148
php_brotli_output_handler_init);
10681149
php_output_handler_conflict_register(ZEND_STRL(PHP_BROTLI_OUTPUT_HANDLER),
@@ -1176,6 +1257,11 @@ ZEND_MINFO_FUNCTION(brotli)
11761257
version >> 24, (version >> 12) & 0xfff, version & 0xfff);
11771258
php_info_print_table_row(2, "Library Version", buffer);
11781259
#endif
1260+
#if defined(USE_BROTLI_DICTIONARY)
1261+
php_info_print_table_row(2, "Dictionary support", "enabled");
1262+
#else
1263+
php_info_print_table_row(2, "Dictionary support", "disabled");
1264+
#endif
11791265
#if defined(HAVE_APCU_SUPPORT)
11801266
php_info_print_table_row(2, "APCu serializer ABI", APC_SERIALIZER_ABI);
11811267
#endif
@@ -1222,18 +1308,20 @@ static ZEND_FUNCTION(brotli_compress)
12221308

12231309
zend_long level = BROTLI_DEFAULT_QUALITY;
12241310
zend_long mode = BROTLI_MODE_GENERIC;
1311+
zend_string *dict = NULL;
12251312

1226-
ZEND_PARSE_PARAMETERS_START(1, 3)
1313+
ZEND_PARSE_PARAMETERS_START(1, 4)
12271314
Z_PARAM_STRING(in, in_size)
12281315
Z_PARAM_OPTIONAL
12291316
Z_PARAM_LONG(level)
12301317
Z_PARAM_LONG(mode)
1318+
Z_PARAM_STR_OR_NULL(dict)
12311319
ZEND_PARSE_PARAMETERS_END();
12321320

12331321
php_brotli_context ctx;
12341322
php_brotli_context_init(&ctx);
12351323
if (php_brotli_context_create_encoder_ex(&ctx, level, 0, mode,
1236-
1) != SUCCESS) {
1324+
dict, 1) != SUCCESS) {
12371325
php_brotli_context_close(&ctx);
12381326
RETURN_FALSE;
12391327
}
@@ -1278,17 +1366,19 @@ static ZEND_FUNCTION(brotli_compress_init)
12781366
{
12791367
zend_long level = BROTLI_DEFAULT_QUALITY;
12801368
zend_long mode = BROTLI_MODE_GENERIC;
1369+
zend_string *dict = NULL;
12811370

1282-
ZEND_PARSE_PARAMETERS_START(0, 2)
1371+
ZEND_PARSE_PARAMETERS_START(0, 3)
12831372
Z_PARAM_OPTIONAL
12841373
Z_PARAM_LONG(level)
12851374
Z_PARAM_LONG(mode)
1375+
Z_PARAM_STR_OR_NULL(dict)
12861376
ZEND_PARSE_PARAMETERS_END();
12871377

12881378
PHP_BROTLI_CONTEXT_OBJ_INIT_OF_CLASS(php_brotli_compress_context_ce);
12891379

12901380
if (php_brotli_context_create_encoder_ex(ctx, level, 0, mode,
1291-
1) != SUCCESS) {
1381+
dict, 1) != SUCCESS) {
12921382
zval_ptr_dtor(return_value);
12931383
RETURN_FALSE;
12941384
}
@@ -1410,11 +1500,13 @@ static ZEND_FUNCTION(brotli_uncompress)
14101500
char *in;
14111501
size_t in_size;
14121502
smart_string out = {0};
1503+
zend_string *dict = NULL;
14131504

1414-
ZEND_PARSE_PARAMETERS_START(1, 2)
1505+
ZEND_PARSE_PARAMETERS_START(1, 3)
14151506
Z_PARAM_STRING(in, in_size)
14161507
Z_PARAM_OPTIONAL
14171508
Z_PARAM_LONG(max_size)
1509+
Z_PARAM_STR_OR_NULL(dict)
14181510
ZEND_PARSE_PARAMETERS_END();
14191511

14201512
if (max_size && max_size < in_size) {
@@ -1423,7 +1515,7 @@ static ZEND_FUNCTION(brotli_uncompress)
14231515

14241516
php_brotli_context ctx;
14251517
php_brotli_context_init(&ctx);
1426-
if (php_brotli_context_create_decoder_ex(&ctx, 1) != SUCCESS) {
1518+
if (php_brotli_context_create_decoder_ex(&ctx, dict, 1) != SUCCESS) {
14271519
php_brotli_context_close(&ctx);
14281520
RETURN_FALSE;
14291521
}
@@ -1463,13 +1555,16 @@ static ZEND_FUNCTION(brotli_uncompress)
14631555

14641556
static ZEND_FUNCTION(brotli_uncompress_init)
14651557
{
1466-
if (zend_parse_parameters_none() == FAILURE) {
1467-
RETURN_FALSE;
1468-
}
1558+
zend_string *dict = NULL;
1559+
1560+
ZEND_PARSE_PARAMETERS_START(0, 1)
1561+
Z_PARAM_OPTIONAL
1562+
Z_PARAM_STR_OR_NULL(dict)
1563+
ZEND_PARSE_PARAMETERS_END();
14691564

14701565
PHP_BROTLI_CONTEXT_OBJ_INIT_OF_CLASS(php_brotli_uncompress_context_ce);
14711566

1472-
if (php_brotli_context_create_decoder_ex(ctx, 1) != SUCCESS) {
1567+
if (php_brotli_context_create_decoder_ex(ctx, dict, 1) != SUCCESS) {
14731568
zval_ptr_dtor(return_value);
14741569
RETURN_FALSE;
14751570
}

brotli.stub.php

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,31 +56,61 @@
5656
*/
5757
const BROTLI_FINISH = UNKNOWN;
5858

59+
/**
60+
* @var bool
61+
* @cvalue USE_BROTLI_DICTIONARY
62+
* @note enabled when brotli library version is 1.1.0 or higher
63+
*/
64+
const BROTLI_DICTIONARY_SUPPORT = UNKNOWN;
5965

60-
function brotli_compress(string $data, int $level = BROTLI_COMPRESS_LEVEL_DEFAULT, int $mode = BROTLI_GENERIC): string|false {}
66+
/**
67+
* @note dict parameter can be used when BROTLI_DICTIONARY_SUPPORT is enabled
68+
*/
69+
function brotli_compress(string $data, int $level = BROTLI_COMPRESS_LEVEL_DEFAULT, int $mode = BROTLI_GENERIC, ?string $dict = null): string|false {}
6170

62-
function brotli_uncompress(string $data, int $length = 0): string|false {}
71+
/**
72+
* @note dict parameter can be used when BROTLI_DICTIONARY_SUPPORT is enabled
73+
*/
74+
function brotli_uncompress(string $data, int $length = 0, ?string $dict = null): string|false {}
6375

64-
function brotli_compress_init(int $level = BROTLI_COMPRESS_LEVEL_DEFAULT, int $mode = BROTLI_GENERIC): Brotli\Compress\Context|false {}
76+
/**
77+
* @note dict parameter can be used when BROTLI_DICTIONARY_SUPPORT is enabled
78+
*/
79+
function brotli_compress_init(int $level = BROTLI_COMPRESS_LEVEL_DEFAULT, int $mode = BROTLI_GENERIC, ?string $dict = null): Brotli\Compress\Context|false {}
6580

6681
function brotli_compress_add(Brotli\Compress\Context $context, string $data, int $mode = BROTLI_FLUSH): string|false {}
6782

68-
function brotli_uncompress_init(): Brotli\UnCompress\Context|false {}
83+
/**
84+
* @note dict parameter can be used when BROTLI_DICTIONARY_SUPPORT is enabled
85+
*/
86+
function brotli_uncompress_init(?string $dict = null): Brotli\UnCompress\Context|false {}
6987

7088
function brotli_uncompress_add(Brotli\UnCompress\Context $context, string $data, int $mode = BROTLI_FLUSH): string|false {}
7189
}
7290

7391
namespace Brotli {
7492

75-
function compress(string $data, int $level = \BROTLI_COMPRESS_LEVEL_DEFAULT, int $mode = \BROTLI_GENERIC): string|false {}
93+
/**
94+
* @note dict parameter can be used when BROTLI_DICTIONARY_SUPPORT is enabled
95+
*/
96+
function compress(string $data, int $level = \BROTLI_COMPRESS_LEVEL_DEFAULT, int $mode = \BROTLI_GENERIC, ?string $dict = null): string|false {}
7697

77-
function uncompress(string $data, int $length = 0): string|false {}
98+
/**
99+
* @note dict parameter can be used when BROTLI_DICTIONARY_SUPPORT is enabled
100+
*/
101+
function uncompress(string $data, int $length = 0, ?string $dict = null): string|false {}
78102

79-
function compress_init(int $level = \BROTLI_COMPRESS_LEVEL_DEFAULT, int $mode = \BROTLI_GENERIC): Compress\Context|false {}
103+
/**
104+
* @note dict parameter can be used when BROTLI_DICTIONARY_SUPPORT is enabled
105+
*/
106+
function compress_init(int $level = \BROTLI_COMPRESS_LEVEL_DEFAULT, int $mode = \BROTLI_GENERIC, ?string $dict = null): Compress\Context|false {}
80107

81108
function compress_add(Compress\Context $context, string $data, int $mode = \BROTLI_FLUSH): string|false {}
82109

83-
function uncompress_init(): UnCompress\Context|false {}
110+
/**
111+
* @note dict parameter can be used when BROTLI_DICTIONARY_SUPPORT is enabled
112+
*/
113+
function uncompress_init(?string $dict = null): UnCompress\Context|false {}
84114

85115
function uncompress_add(UnCompress\Context $context, string $data, int $mode = \BROTLI_FLUSH): string|false {}
86116

config.m4

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ if test "$PHP_BROTLI" != "no"; then
6363
PHP_EVAL_INCLINE($LIBBROTLIDEC_CFLAGS)
6464
PHP_EVAL_LIBLINE($LIBBROTLIDEC_LIBS, BROTLI_SHARED_LIBADD)
6565
AC_DEFINE_UNQUOTED(BROTLI_LIB_VERSION, "$LIBBROTLIDEC_VERSION", [system library version])
66+
67+
AC_MSG_CHECKING(for brotli dictionary)
68+
if $PKG_CONFIG libbrotlidec --atleast-version 1.1.0; then
69+
AC_MSG_RESULT([enable])
70+
AC_DEFINE(USE_BROTLI_DICTIONARY, 1, [use dictionary support])
71+
else
72+
AC_MSG_RESULT([disable])
73+
fi
6674
else
6775
AC_MSG_CHECKING(for brotli)
6876
AC_MSG_RESULT(use bundled copy)
@@ -106,6 +114,7 @@ if test "$PHP_BROTLI" != "no"; then
106114
"
107115

108116
AC_DEFINE(USE_BROTLI_BUNDLED, 1, [use bundled])
117+
AC_DEFINE(USE_BROTLI_DICTIONARY, 1, [use dictionary support])
109118
fi
110119

111120
PHP_SUBST(BROTLI_SHARED_LIBADD)

0 commit comments

Comments
 (0)