From: Sara Golemon Date: Sat, 17 Jul 2004 00:05:31 +0000 (+0000) Subject: Add zlib stream filter support X-Git-Tag: PRE_ZEND_VM_DISPATCH_PATCH~486 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5289adbe99f37847a54785f68748380e1a49050a;p=php Add zlib stream filter support --- diff --git a/NEWS b/NEWS index f343b7ee90..4df4ccf430 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? 2004, PHP 5.1.0 +- Added zlib stream filter suport. (Sara) - Changed the implementation of TRUE, FALSE, and NULL from constants to keywords. (Marcus) - Fixed ZTS destruction. (Marcus) diff --git a/ext/zlib/config0.m4 b/ext/zlib/config0.m4 index c174a3bcbc..495a952c2c 100644 --- a/ext/zlib/config0.m4 +++ b/ext/zlib/config0.m4 @@ -9,7 +9,7 @@ PHP_ARG_WITH(zlib-dir,if the location of ZLIB install directory is defined, [ --with-zlib-dir= Define the location of zlib install directory], no, no) if test "$PHP_ZLIB" != "no" -o "$PHP_ZLIB_DIR" != "no"; then - PHP_NEW_EXTENSION(zlib, zlib.c zlib_fopen_wrapper.c, $ext_shared) + PHP_NEW_EXTENSION(zlib, zlib.c zlib_fopen_wrapper.c zlib_filter.c, $ext_shared) PHP_SUBST(ZLIB_SHARED_LIBADD) if test "$PHP_ZLIB" != "yes" -a "$PHP_ZLIB" != "no"; then diff --git a/ext/zlib/php_zlib.h b/ext/zlib/php_zlib.h index 5e6795d909..92b1f874be 100644 --- a/ext/zlib/php_zlib.h +++ b/ext/zlib/php_zlib.h @@ -35,6 +35,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zlib) char *output_handler; ZEND_END_MODULE_GLOBALS(zlib) +extern php_stream_filter_factory php_zlib_filter_factory; extern zend_module_entry php_zlib_module_entry; #define zlib_module_ptr &php_zlib_module_entry diff --git a/ext/zlib/tests/zlib_filter_deflate.phpt b/ext/zlib/tests/zlib_filter_deflate.phpt new file mode 100644 index 0000000000..70c1bdfab7 --- /dev/null +++ b/ext/zlib/tests/zlib_filter_deflate.phpt @@ -0,0 +1,17 @@ +--TEST-- +zlib.deflate (with convert.base64-encode) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +eJwdy0EOgCAMRNGrzM6N8R4co4YBMbRNGkLi7VV2f/FfgijGRUzGA/XMDi+QlWFQuT1QaQzpO9I2iWbFQ2U0t49VDjk7d4g1/R+xDG0LHC8+diGh diff --git a/ext/zlib/tests/zlib_filter_inflate.phpt b/ext/zlib/tests/zlib_filter_inflate.phpt new file mode 100644 index 0000000000..b91745f39f --- /dev/null +++ b/ext/zlib/tests/zlib_filter_inflate.phpt @@ -0,0 +1,17 @@ +--TEST-- +zlib.inflate (with convert.base64-decode) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +I am the very model of a modern major general, I've information vegetable, animal, and mineral. diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index a3ae1e4381..80e7d0dffb 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -209,6 +209,7 @@ PHP_MINIT_FUNCTION(zlib) ts_allocate_id(&zlib_globals_id, sizeof(zend_zlib_globals), (ts_allocate_ctor) php_zlib_init_globals, NULL); #endif php_register_url_stream_wrapper("compress.zlib", &php_stream_gzip_wrapper TSRMLS_CC); + php_stream_filter_register_factory("zlib.*", &php_zlib_filter_factory TSRMLS_CC); REGISTER_LONG_CONSTANT("FORCE_GZIP", CODING_GZIP, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("FORCE_DEFLATE", CODING_DEFLATE, CONST_CS | CONST_PERSISTENT); @@ -243,6 +244,7 @@ PHP_RINIT_FUNCTION(zlib) PHP_MSHUTDOWN_FUNCTION(zlib) { php_unregister_url_stream_wrapper("zlib" TSRMLS_CC); + php_stream_filter_unregister_factory("zlib.*" TSRMLS_CC); UNREGISTER_INI_ENTRIES(); @@ -256,6 +258,8 @@ PHP_MINFO_FUNCTION(zlib) { php_info_print_table_start(); php_info_print_table_row(2, "ZLib Support", "enabled"); + php_info_print_table_row(2, "Stream Wrapper support", "compress.zlib://"); + php_info_print_table_row(2, "Stream Filter support", "zlib.inflate, zlib.deflate"); php_info_print_table_row(2, "Compiled Version", ZLIB_VERSION); php_info_print_table_row(2, "Linked Version", (char *) zlibVersion()); php_info_print_table_end(); diff --git a/ext/zlib/zlib_filter.c b/ext/zlib/zlib_filter.c new file mode 100644 index 0000000000..e0fa2d4555 --- /dev/null +++ b/ext/zlib/zlib_filter.c @@ -0,0 +1,403 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Sara Golemon (pollita@php.net) | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php.h" +#include "php_zlib.h" + +/* {{{ data structure */ + +typedef struct _php_zlib_filter_data { + z_stream strm; + char *inbuf; + size_t inbuf_len; + char *outbuf; + size_t outbuf_len; +} php_zlib_filter_data; + +/* }}} */ + +/* {{{ Memory management wrappers */ + +static voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size) +{ + return (voidpf)safe_emalloc(items, size, 0); +} + +static void php_zlib_free(voidpf opaque, voidpf address) +{ + efree((void*)address); +} +/* }}} */ + +/* {{{ zlib.inflate filter implementation */ + +static php_stream_filter_status_t php_zlib_inflate_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC) +{ + php_zlib_filter_data *data; + php_stream_bucket *bucket; + size_t consumed = 0, original_out, original_in; + int status; + php_stream_filter_status_t exit_status = PSFS_FEED_ME; + z_stream *streamp; + + if (!thisfilter || !thisfilter->abstract) { + /* Should never happen */ + return PSFS_ERR_FATAL; + } + + data = (php_zlib_filter_data *)(thisfilter->abstract); + streamp = &(data->strm); + original_in = data->strm.total_in; + original_out = data->strm.total_out; + + while (buckets_in->head) { + size_t bin = 0, desired; + + bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC); + while (bin < bucket->buflen) { + desired = bucket->buflen - bin; + if (desired > data->inbuf_len) { + desired = data->inbuf_len; + } + memcpy(data->strm.next_in, bucket->buf + bin, desired); + data->strm.avail_in = desired; + + status = inflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FINISH : Z_SYNC_FLUSH); + if (status != Z_OK && status != Z_STREAM_END) { + /* Something bad happened */ + php_stream_bucket_delref(bucket TSRMLS_CC); + return PSFS_ERR_FATAL; + } + desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */ + data->strm.next_in = data->inbuf; + data->strm.avail_in = 0; + consumed += desired; + bin += desired; + + if (data->strm.avail_out < data->outbuf_len) { + php_stream_bucket *out_bucket; + size_t bucketlen = data->outbuf_len - data->strm.avail_out; + out_bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC); + php_stream_bucket_append(buckets_out, out_bucket TSRMLS_CC); + data->strm.avail_out = data->outbuf_len; + data->strm.next_out = data->outbuf; + exit_status = PSFS_PASS_ON; + } + } + php_stream_bucket_delref(bucket TSRMLS_CC); + } + + if (flags & PSFS_FLAG_FLUSH_CLOSE) { + /* Spit it out! */ + status = Z_OK; + while (status == Z_OK) { + status = inflate(&(data->strm), Z_FINISH); + if (data->strm.avail_out < data->outbuf_len) { + size_t bucketlen = data->outbuf_len - data->strm.avail_out; + + bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC); + php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); + data->strm.avail_out = data->outbuf_len; + data->strm.next_out = data->outbuf; + exit_status = PSFS_PASS_ON; + } + } + } + + if (bytes_consumed) { + *bytes_consumed = consumed; + } + + return exit_status; +} + +static void php_zlib_inflate_dtor(php_stream_filter *thisfilter TSRMLS_DC) +{ + if (thisfilter && thisfilter->abstract) { + php_zlib_filter_data *data = thisfilter->abstract; + inflateEnd(&(data->strm)); + efree(data->inbuf); + efree(data->outbuf); + efree(data); + } +} + +static php_stream_filter_ops php_zlib_inflate_ops = { + php_zlib_inflate_filter, + php_zlib_inflate_dtor, + "zlib.inflate" +}; +/* }}} */ + +/* {{{ zlib.inflate filter implementation */ + +static php_stream_filter_status_t php_zlib_deflate_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC) +{ + php_zlib_filter_data *data; + php_stream_bucket *bucket; + size_t consumed = 0, original_out, original_in; + int status; + php_stream_filter_status_t exit_status = PSFS_FEED_ME; + z_stream *streamp; + + if (!thisfilter || !thisfilter->abstract) { + /* Should never happen */ + return PSFS_ERR_FATAL; + } + + data = (php_zlib_filter_data *)(thisfilter->abstract); + streamp = &(data->strm); + original_in = data->strm.total_in; + original_out = data->strm.total_out; + + while (buckets_in->head) { + size_t bin = 0, desired; + + bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC); + + while (bin < bucket->buflen) { + desired = bucket->buflen - bin; + if (desired > data->inbuf_len) { + desired = data->inbuf_len; + } + memcpy(data->strm.next_in, bucket->buf + bin, desired); + data->strm.avail_in = desired; + + status = deflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FULL_FLUSH : (flags & PSFS_FLAG_FLUSH_INC ? Z_SYNC_FLUSH : Z_NO_FLUSH)); + if (status != Z_OK) { + /* Something bad happened */ + php_stream_bucket_delref(bucket TSRMLS_CC); + return PSFS_ERR_FATAL; + } + desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */ + data->strm.next_in = data->inbuf; + data->strm.avail_in = 0; + consumed += desired; + bin += desired; + + if (data->strm.avail_out < data->outbuf_len) { + php_stream_bucket *out_bucket; + size_t bucketlen = data->outbuf_len - data->strm.avail_out; + + out_bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC); + php_stream_bucket_append(buckets_out, out_bucket TSRMLS_CC); + data->strm.avail_out = data->outbuf_len; + data->strm.next_out = data->outbuf; + exit_status = PSFS_PASS_ON; + } + } + php_stream_bucket_delref(bucket TSRMLS_CC); + } + + if (flags & PSFS_FLAG_FLUSH_CLOSE) { + /* Spit it out! */ + status = Z_OK; + while (status == Z_OK) { + status = deflate(&(data->strm), Z_FINISH); + if (data->strm.avail_out < data->outbuf_len) { + size_t bucketlen = data->outbuf_len - data->strm.avail_out; + + bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC); + php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); + data->strm.avail_out = data->outbuf_len; + data->strm.next_out = data->outbuf; + exit_status = PSFS_PASS_ON; + } + } + } + + if (bytes_consumed) { + *bytes_consumed = consumed; + } + return exit_status; +} + +static void php_zlib_deflate_dtor(php_stream_filter *thisfilter TSRMLS_DC) +{ + if (thisfilter && thisfilter->abstract) { + php_zlib_filter_data *data = thisfilter->abstract; + deflateEnd(&(data->strm)); + efree(data->inbuf); + efree(data->outbuf); + efree(data); + } +} + +static php_stream_filter_ops php_zlib_deflate_ops = { + php_zlib_deflate_filter, + php_zlib_deflate_dtor, + "zlib.deflate" +}; + +/* }}} */ + +/* {{{ zlib.* common factory */ + +static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC) +{ + php_stream_filter_ops *fops = NULL; + php_zlib_filter_data *data; + int status; + + /* Create this filter */ + data = ecalloc(1, sizeof(php_zlib_filter_data)); + + /* Circular reference */ + data->strm.opaque = (voidpf) data; + + data->strm.zalloc = (alloc_func) php_zlib_alloc; + data->strm.zfree = (free_func) php_zlib_free; + data->strm.avail_out = data->outbuf_len = data->inbuf_len = 2048; + data->strm.next_in = data->inbuf = (Bytef *) emalloc(data->inbuf_len); + data->strm.avail_in = 0; + data->strm.next_out = data->outbuf = (Bytef *) emalloc(data->outbuf_len); + data->strm.data_type = Z_ASCII; + + if (strcasecmp(filtername, "zlib.inflate") == 0) { + int windowBits = MAX_WBITS; + + if (filterparams) { + zval **tmpzval; + + if ((Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) && + zend_hash_find(HASH_OF(filterparams), "window", sizeof("window"), (void **) &tmpzval) == SUCCESS) { + /* log-2 base of history window (9 - 15) */ + SEPARATE_ZVAL(tmpzval); + convert_to_long_ex(tmpzval); + if (Z_LVAL_PP(tmpzval) < 9 || Z_LVAL_PP(tmpzval) > MAX_WBITS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter give for window size. (%ld)", Z_LVAL_PP(tmpzval)); + } else { + windowBits = Z_LVAL_PP(tmpzval); + } + zval_ptr_dtor(tmpzval); + } + } + + /* RFC 1951 Inflate */ + status = inflateInit2(&(data->strm), windowBits); + fops = &php_zlib_inflate_ops; + } else if (strcasecmp(filtername, "zlib.deflate") == 0) { + /* RFC 1951 Deflate */ + int level = Z_DEFAULT_COMPRESSION; + int windowBits = MAX_WBITS; + int memLevel = MAX_MEM_LEVEL; + + + if (filterparams) { + zval **tmpzval; + + /* filterparams can either be a scalar value to indicate compression level (shortcut method) + Or can be a hash containing one or more of 'window', 'memory', and/or 'level' members. */ + + switch (Z_TYPE_P(filterparams)) { + case IS_ARRAY: + case IS_OBJECT: + if (zend_hash_find(HASH_OF(filterparams), "memory", sizeof("memory"), (void**) &tmpzval) == SUCCESS) { + /* Memory Level (1 - 9) */ + SEPARATE_ZVAL(tmpzval); + convert_to_long_ex(tmpzval); + if (Z_LVAL_PP(tmpzval) < 1 || Z_LVAL_PP(tmpzval) > MAX_MEM_LEVEL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter give for memory level. (%ld)", Z_LVAL_PP(tmpzval)); + } else { + memLevel = Z_LVAL_PP(tmpzval); + } + zval_ptr_dtor(tmpzval); + } + + if (zend_hash_find(HASH_OF(filterparams), "window", sizeof("window"), (void**) &tmpzval) == SUCCESS) { + /* log-2 base of history window (9 - 15) */ + SEPARATE_ZVAL(tmpzval); + convert_to_long_ex(tmpzval); + if (Z_LVAL_PP(tmpzval) < 9 || Z_LVAL_PP(tmpzval) > MAX_WBITS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter give for window size. (%ld)", Z_LVAL_PP(tmpzval)); + } else { + windowBits = Z_LVAL_PP(tmpzval); + } + zval_ptr_dtor(tmpzval); + } + + if (zend_hash_find(HASH_OF(filterparams), "level", sizeof("level"), (void**) &tmpzval) == SUCCESS) { + /* Psuedo pass through to catch level validating code */ + goto factory_setlevel; + } + break; + case IS_STRING: + case IS_DOUBLE: + case IS_LONG: + tmpzval = &filterparams; + factory_setlevel: + /* Set compression level within reason (-1 == default, 0 == none, 1-9 == least to most compression */ + SEPARATE_ZVAL(tmpzval); + convert_to_long_ex(tmpzval); + if (Z_LVAL_PP(tmpzval) < -1 || Z_LVAL_PP(tmpzval) > 9) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid compression level specified. (%ld)", Z_LVAL_PP(tmpzval)); + } else { + level = Z_LVAL_PP(tmpzval); + } + zval_ptr_dtor(tmpzval); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid filter parameter, ignored."); + } + } + status = deflateInit2(&(data->strm), level, Z_DEFLATED, windowBits, memLevel, 0); + fops = &php_zlib_deflate_ops; + } else { + status = Z_DATA_ERROR; + } + + if (status != Z_OK) { + /* Unspecified (probably strm) error, let stream-filter error do its own whining */ + efree(data->strm.next_in); + efree(data->strm.next_out); + efree(data); + return NULL; + } + + return php_stream_filter_alloc(fops, data, persistent); +} + +php_stream_filter_factory php_zlib_filter_factory = { + php_zlib_filter_create +}; +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */