mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Add dictionary option to {in,de}flate_init()
This commit is contained in:
parent
a129ded3c1
commit
dd17e18f41
3 changed files with 126 additions and 4 deletions
|
@ -47,6 +47,8 @@ typedef struct _php_zlib_buffer {
|
|||
|
||||
typedef struct _php_zlib_context {
|
||||
z_stream Z;
|
||||
char *inflateDict;
|
||||
size_t inflateDictlen;
|
||||
php_zlib_buffer buffer;
|
||||
} php_zlib_context;
|
||||
|
||||
|
@ -57,7 +59,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zlib)
|
|||
char *output_handler;
|
||||
php_zlib_context *ob_gzhandler;
|
||||
zend_long output_compression_default;
|
||||
zend_bool handler_registered;
|
||||
zend_bool handler_registered;
|
||||
int compression_coding;
|
||||
ZEND_END_MODULE_GLOBALS(zlib);
|
||||
|
||||
|
|
25
ext/zlib/tests/dictionary_usage.phpt
Normal file
25
ext/zlib/tests/dictionary_usage.phpt
Normal file
|
@ -0,0 +1,25 @@
|
|||
--TEST--
|
||||
Test dictionary usage on zlib methods
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dict = range("a", "z");
|
||||
|
||||
$r = deflate_init(ZLIB_ENCODING_DEFLATE, ["dictionary" => $dict]);
|
||||
$a = deflate_add($r, "abdcde", ZLIB_FINISH);
|
||||
var_dump($a);
|
||||
|
||||
$r = inflate_init(ZLIB_ENCODING_DEFLATE, ["dictionary" => $dict]);
|
||||
var_dump(inflate_add($r, $a, ZLIB_FINISH));
|
||||
|
||||
|
||||
$r = inflate_init(ZLIB_ENCODING_DEFLATE, ["dictionary" => ["8"] + range("a", "z")]);
|
||||
var_dump(inflate_add($r, $a, ZLIB_FINISH));
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
string(%d) "%s"
|
||||
string(6) "abdcde"
|
||||
|
||||
Warning: inflate_add(): dictionary does match expected dictionary (incorrect adler32 hash) in %s on line %d
|
||||
bool(false)
|
101
ext/zlib/zlib.c
101
ext/zlib/zlib.c
|
@ -76,6 +76,9 @@ void deflate_rsrc_dtor(zend_resource *res)
|
|||
void inflate_rsrc_dtor(zend_resource *res)
|
||||
{
|
||||
z_stream *ctx = zend_fetch_resource(res, NULL, le_inflate);
|
||||
if (((php_zlib_context *) ctx)->inflateDict) {
|
||||
efree(((php_zlib_context *) ctx)->inflateDict);
|
||||
}
|
||||
inflateEnd(ctx);
|
||||
efree(ctx);
|
||||
}
|
||||
|
@ -752,12 +755,66 @@ PHP_ZLIB_DECODE_FUNC(gzdecode, PHP_ZLIB_ENCODING_GZIP);
|
|||
PHP_ZLIB_DECODE_FUNC(gzuncompress, PHP_ZLIB_ENCODING_DEFLATE);
|
||||
/* }}} */
|
||||
|
||||
static zend_bool zlib_create_dictionary_string(HashTable *options, char **dict, size_t *dictlen) {
|
||||
zval *option_buffer;
|
||||
|
||||
if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("dictionary"))) != NULL) {
|
||||
HashTable *dictionary;
|
||||
|
||||
ZVAL_DEREF(option_buffer);
|
||||
if (Z_TYPE_P(option_buffer) != IS_ARRAY) {
|
||||
php_error_docref(NULL, E_WARNING, "dictionary must be of type array, got %s", zend_get_type_by_const(Z_TYPE_P(option_buffer)));
|
||||
return 0;
|
||||
}
|
||||
dictionary = Z_ARR_P(option_buffer);
|
||||
|
||||
if (zend_hash_num_elements(dictionary) > 0) {
|
||||
char *dictptr;
|
||||
zval *cur;
|
||||
zend_string **strings = emalloc(sizeof(zend_string *) * zend_hash_num_elements(dictionary));
|
||||
zend_string **end, **ptr = strings - 1;
|
||||
|
||||
ZEND_HASH_FOREACH_VAL(dictionary, cur) {
|
||||
*++ptr = zval_get_string(cur);
|
||||
if (!*ptr || (*ptr)->len == 0) {
|
||||
if (*ptr) {
|
||||
efree(*ptr);
|
||||
}
|
||||
while (--ptr >= strings) {
|
||||
efree(ptr);
|
||||
}
|
||||
efree(strings);
|
||||
php_error_docref(NULL, E_WARNING, "dictionary entries must be non-empty strings");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*dictlen += (*ptr)->len + 1;
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
dictptr = *dict = emalloc(*dictlen);
|
||||
ptr = strings;
|
||||
end = strings + zend_hash_num_elements(dictionary);
|
||||
do {
|
||||
memcpy(dictptr, *ptr, (*ptr)->len);
|
||||
dictptr += (*ptr)->len;
|
||||
*dictptr++ = 0;
|
||||
zend_string_release(*ptr);
|
||||
} while (++ptr != end);
|
||||
efree(strings);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* {{{ proto resource inflate_init(int encoding)
|
||||
Initialize an incremental inflate context with the specified encoding */
|
||||
PHP_FUNCTION(inflate_init)
|
||||
{
|
||||
z_stream *ctx;
|
||||
zend_long encoding, window = 15;
|
||||
char *dict = NULL;
|
||||
size_t dictlen = 0;
|
||||
HashTable *options;
|
||||
zval *option_buffer;
|
||||
|
||||
|
@ -773,20 +830,25 @@ PHP_FUNCTION(inflate_init)
|
|||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
if (!zlib_create_dictionary_string(options, &dict, &dictlen)) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
switch (encoding) {
|
||||
case PHP_ZLIB_ENCODING_RAW:
|
||||
case PHP_ZLIB_ENCODING_GZIP:
|
||||
case PHP_ZLIB_ENCODING_DEFLATE:
|
||||
break;
|
||||
default:
|
||||
php_error_docref(NULL, E_WARNING,
|
||||
"encoding mode must be ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP or ZLIB_ENCODING_DEFLATE");
|
||||
php_error_docref(NULL, E_WARNING, "encoding mode must be ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP or ZLIB_ENCODING_DEFLATE");
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
ctx = ecalloc(1, sizeof(php_zlib_context));
|
||||
ctx->zalloc = php_zlib_alloc;
|
||||
ctx->zfree = php_zlib_free;
|
||||
((php_zlib_context *) ctx)->inflateDict = dict;
|
||||
((php_zlib_context *) ctx)->inflateDictlen = dictlen;
|
||||
|
||||
if (encoding < 0) {
|
||||
encoding += 15 - window;
|
||||
|
@ -879,6 +941,27 @@ PHP_FUNCTION(inflate_add)
|
|||
/* No more input data; we're finished */
|
||||
goto complete;
|
||||
}
|
||||
case Z_NEED_DICT:
|
||||
if (((php_zlib_context *) ctx)->inflateDict) {
|
||||
php_zlib_context *php_ctx = (php_zlib_context *) ctx;
|
||||
switch (inflateSetDictionary(ctx, (Bytef *) php_ctx->inflateDict, php_ctx->inflateDictlen)) {
|
||||
case Z_OK:
|
||||
efree(php_ctx->inflateDict);
|
||||
php_ctx->inflateDict = NULL;
|
||||
break;
|
||||
case Z_DATA_ERROR:
|
||||
php_error_docref(NULL, E_WARNING, "dictionary does match expected dictionary (incorrect adler32 hash)");
|
||||
efree(php_ctx->inflateDict);
|
||||
zend_string_release(out);
|
||||
php_ctx->inflateDict = NULL;
|
||||
RETURN_FALSE;
|
||||
EMPTY_SWITCH_DEFAULT_CASE()
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
php_error_docref(NULL, E_WARNING, "inflating this data requires a preset dictionary, please specify it in the options array of inflate_init()");
|
||||
RETURN_FALSE;
|
||||
}
|
||||
default:
|
||||
zend_string_release(out);
|
||||
php_error_docref(NULL, E_WARNING, "%s", zError(status));
|
||||
|
@ -900,6 +983,8 @@ PHP_FUNCTION(deflate_init)
|
|||
{
|
||||
z_stream *ctx;
|
||||
zend_long encoding, level = -1, memory = 8, window = 15;
|
||||
char *dict = NULL;
|
||||
size_t dictlen = 0;
|
||||
HashTable *options = 0;
|
||||
zval *option_buffer;
|
||||
|
||||
|
@ -931,7 +1016,11 @@ PHP_FUNCTION(deflate_init)
|
|||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
/* @TODO: in the future we may add "strategy" and "dictionary" options */
|
||||
/* @TODO: in the future we may add "strategy" option */
|
||||
|
||||
if (!zlib_create_dictionary_string(options, &dict, &dictlen)) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
switch (encoding) {
|
||||
case PHP_ZLIB_ENCODING_RAW:
|
||||
|
@ -955,6 +1044,12 @@ PHP_FUNCTION(deflate_init)
|
|||
}
|
||||
|
||||
if (Z_OK == deflateInit2(ctx, level, Z_DEFLATED, encoding, memory, Z_DEFAULT_STRATEGY)) {
|
||||
if (dict) {
|
||||
int success = deflateSetDictionary(ctx, (Bytef *) dict, dictlen);
|
||||
ZEND_ASSERT(success == Z_OK);
|
||||
efree(dict);
|
||||
}
|
||||
|
||||
RETURN_RES(zend_register_resource(ctx, le_deflate));
|
||||
} else {
|
||||
efree(ctx);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue