]> granicus.if.org Git - php/commitdiff
Add dictionary option to {in,de}flate_init()
authorBob Weinand <bobwei9@hotmail.com>
Fri, 15 May 2015 00:18:53 +0000 (02:18 +0200)
committerBob Weinand <bobwei9@hotmail.com>
Fri, 15 May 2015 00:28:38 +0000 (02:28 +0200)
ext/zlib/php_zlib.h
ext/zlib/tests/dictionary_usage.phpt [new file with mode: 0644]
ext/zlib/zlib.c

index 6da5e2e5430444d5befe00cc9adc7446d8593a21..b69c948523c253c6340fa35ff13da047cd3fbaba 100644 (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);
 
diff --git a/ext/zlib/tests/dictionary_usage.phpt b/ext/zlib/tests/dictionary_usage.phpt
new file mode 100644 (file)
index 0000000..b73cd52
--- /dev/null
@@ -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)
index e99e3760e238192bdb834d33f208596b138a7bd5..2b9b34e1cb7585b738ae5a7eb0b960ea9bae6b62 100644 (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);