Add dictionary option to {in,de}flate_init()

This commit is contained in:
Bob Weinand 2015-05-15 02:18:53 +02:00
parent a129ded3c1
commit dd17e18f41
3 changed files with 126 additions and 4 deletions

View file

@ -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);

View 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)

View file

@ -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);