From e9e6af0ff660086e256af6065f020ef60507b566 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sun, 20 Apr 2025 21:23:11 +0200 Subject: [PATCH 01/12] Add incremental zstd_(un)compress_init() and zstd_(un)compress_add() --- tests/inc_comp.phpt | 28 +++++ tests/inc_decomp.phpt | 36 ++++++ zstd.c | 257 +++++++++++++++++++++++++++++++++++++----- zstd.stub.php | 40 +++++++ 4 files changed, 331 insertions(+), 30 deletions(-) create mode 100755 tests/inc_comp.phpt create mode 100755 tests/inc_decomp.phpt diff --git a/tests/inc_comp.phpt b/tests/inc_comp.phpt new file mode 100755 index 0000000..31b834e --- /dev/null +++ b/tests/inc_comp.phpt @@ -0,0 +1,28 @@ +--TEST-- +Incremental compression +--FILE-- + +===Done=== +--EXPECTF-- +resource(%d) of type (zstd.state) +int(%d) +bool(true) +bool(true) +===Done=== diff --git a/tests/inc_decomp.phpt b/tests/inc_decomp.phpt new file mode 100755 index 0000000..3737838 --- /dev/null +++ b/tests/inc_decomp.phpt @@ -0,0 +1,36 @@ +--TEST-- +Incremental decompression +--FILE-- + +===Done=== +--EXPECTF-- +int(128) +resource(%d) of type (zstd.state) +bool(true) +int(512) +resource(%d) of type (zstd.state) +bool(true) +int(1024) +resource(%d) of type (zstd.state) +bool(true) +===Done=== diff --git a/zstd.c b/zstd.c index 4b1ba25..7d5f7c5 100644 --- a/zstd.c +++ b/zstd.c @@ -38,6 +38,8 @@ #endif #include "php_zstd.h" +int le_state; + /* zstd */ #ifdef HAVE_SYS_TYPES_H #include @@ -64,6 +66,7 @@ #define ZSTD_IS_ERROR(result) \ UNEXPECTED(ZSTD_isError(result)) +/* One-shot functions */ ZEND_BEGIN_ARG_INFO_EX(arginfo_zstd_compress, 0, 0, 1) ZEND_ARG_INFO(0, data) ZEND_ARG_INFO(0, level) @@ -84,6 +87,25 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_zstd_uncompress_dict, 0, 0, 2) ZEND_ARG_INFO(0, dictBuffer) ZEND_END_ARG_INFO() +/* Incremental functions */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_zstd_compress_init, 0, 0, 0) + ZEND_ARG_INFO(0, level) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zstd_compress_add, 0, 0, 2) + ZEND_ARG_INFO(0, context) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(0, end) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zstd_uncompress_init, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zstd_uncompress_add, 0, 0, 2) + ZEND_ARG_INFO(0, context) + ZEND_ARG_INFO(0, data) +ZEND_END_ARG_INFO() + #if PHP_VERSION_ID >= 80000 #if PHP_VERSION_ID >= 80100 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ob_zstd_handler, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) @@ -393,6 +415,193 @@ ZEND_FUNCTION(zstd_uncompress_dict) RETVAL_NEW_STR(output); } +struct _php_zstd_context { + ZSTD_CCtx* cctx; + ZSTD_DCtx* dctx; + ZSTD_CDict *cdict; + ZSTD_inBuffer input; + ZSTD_outBuffer output; +}; + +static php_zstd_context* php_zstd_output_handler_context_init(void) +{ + php_zstd_context *ctx + = (php_zstd_context *) ecalloc(1, sizeof(php_zstd_context)); + ctx->cctx = NULL; + ctx->dctx = NULL; + return ctx; +} + +static void php_zstd_output_handler_context_free(php_zstd_context *ctx) +{ + if (ctx->cctx) { + ZSTD_freeCCtx(ctx->cctx); + ctx->cctx = NULL; + } + if (ctx->dctx) { + ZSTD_freeDCtx(ctx->dctx); + ctx->dctx = NULL; + } + if (ctx->cdict) { + ZSTD_freeCDict(ctx->cdict); + ctx->cdict = NULL; + } + if (ctx->output.dst) { + efree(ctx->output.dst); + ctx->output.dst = NULL; + } +} + +static void php_zstd_state_rsrc_dtor(zend_resource *res) +{ + php_zstd_context *ctx = zend_fetch_resource(res, NULL, le_state); + php_zstd_output_handler_context_free(ctx); +} + +ZEND_FUNCTION(zstd_compress_init) +{ + zend_long level = DEFAULT_COMPRESS_LEVEL; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(level) + ZEND_PARSE_PARAMETERS_END(); + + if (!zstd_check_compress_level(level)) { + RETURN_FALSE; + } + + php_zstd_context *ctx = php_zstd_output_handler_context_init(); + + ctx->cctx = ZSTD_createCCtx(); + if (ctx->cctx == NULL) { + efree(ctx); + ZSTD_WARNING("ZSTD_createCCtx() error"); + RETURN_FALSE; + } + ctx->cdict = NULL; + + ZSTD_CCtx_reset(ctx->cctx, ZSTD_reset_session_only); + ZSTD_CCtx_setParameter(ctx->cctx, ZSTD_c_compressionLevel, level); + + ctx->output.size = ZSTD_CStreamOutSize(); + ctx->output.dst = emalloc(ctx->output.size); + ctx->output.pos = 0; + + RETURN_RES(zend_register_resource(ctx, le_state)); +} + +ZEND_FUNCTION(zstd_compress_add) +{ + zval *resource; + php_zstd_context *ctx; + char *in_buf; + size_t in_size; + zend_bool end = 0; + smart_string out = {0}; + + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_RESOURCE(resource) + Z_PARAM_STRING(in_buf, in_size) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(end) + ZEND_PARSE_PARAMETERS_END(); + + ctx = zend_fetch_resource(Z_RES_P(resource), NULL, le_state); + if (ctx == NULL) { + php_error_docref(NULL, E_WARNING, + "ZStandard incremental compress resource failed"); + RETURN_FALSE; + } + + ZSTD_inBuffer in = { in_buf, in_size, 0 }; + size_t res; + + do { + ctx->output.pos = 0; + res = ZSTD_compressStream2(ctx->cctx, &ctx->output, + &in, end ? ZSTD_e_end : ZSTD_e_flush); + if (ZSTD_isError(res)) { + php_error_docref(NULL, E_WARNING, + "libzstd error %s\n", ZSTD_getErrorName(res)); + smart_string_free(&out); + RETURN_FALSE; + } + smart_string_appendl(&out, ctx->output.dst, ctx->output.pos); + } while (res > 0); + + RETVAL_STRINGL(out.c, out.len); + smart_string_free(&out); +} + +ZEND_FUNCTION(zstd_uncompress_init) +{ + php_zstd_context *ctx = php_zstd_output_handler_context_init(); + size_t result; + + ctx->dctx = ZSTD_createDCtx(); + if (ctx->dctx == NULL) { + efree(ctx); + ZSTD_WARNING("ZSTD_createCCtx() error"); + RETURN_FALSE; + } + ctx->cdict = NULL; + + ZSTD_DCtx_reset(ctx->dctx, ZSTD_reset_session_only); + + ctx->output.size = ZSTD_CStreamOutSize(); + ctx->output.dst = emalloc(ctx->output.size); + ctx->output.pos = 0; + + RETURN_RES(zend_register_resource(ctx, le_state)); +} + +ZEND_FUNCTION(zstd_uncompress_add) +{ + zval *resource; + php_zstd_context *ctx; + char *in_buf; + size_t in_size; + zend_bool end = 0; + smart_string out = {0}; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_RESOURCE(resource) + Z_PARAM_STRING(in_buf, in_size) + ZEND_PARSE_PARAMETERS_END(); + + ctx = zend_fetch_resource(Z_RES_P(resource), NULL, le_state); + if (ctx == NULL) { + php_error_docref(NULL, E_WARNING, + "ZStandard incremental uncompress resource failed"); + RETURN_FALSE; + } + + ZSTD_inBuffer in = { in_buf, in_size, 0 }; + size_t res = 1; + uint64_t size; + + ctx->output.pos = 0; + while (in.pos < in.size && res > 0) { + if (ctx->output.pos == ctx->output.size) { + ctx->output.size += size; + ctx->output.dst = erealloc(ctx->output.dst, ctx->output.size); + } + + res = ZSTD_decompressStream(ctx->dctx, &ctx->output, &in); + if (ZSTD_isError(res)) { + php_error_docref(NULL, E_WARNING, + "libzstd error %s\n", ZSTD_getErrorName(res)); + smart_string_free(&out); + RETURN_FALSE; + } + + smart_string_appendl(&out, ctx->output.dst, ctx->output.pos); + } + + RETVAL_STRINGL(out.c, out.len); + smart_string_free(&out); +} typedef struct _php_zstd_stream_data { char *bufin, *bufout; @@ -835,13 +1044,6 @@ static int APC_UNSERIALIZER_NAME(zstd)(APC_UNSERIALIZER_ARGS) #if PHP_VERSION_ID >= 80000 #define PHP_ZSTD_OUTPUT_HANDLER_NAME "zstd output compression" -struct _php_zstd_context { - ZSTD_CCtx* cctx; - ZSTD_CDict *cdict; - ZSTD_inBuffer input; - ZSTD_outBuffer output; -}; - static int php_zstd_output_encoding(void) { zval *enc; @@ -866,13 +1068,6 @@ static int php_zstd_output_encoding(void) return PHP_ZSTD_G(compression_coding); } -static php_zstd_context* php_zstd_output_handler_context_init(void) -{ - php_zstd_context *ctx - = (php_zstd_context *) ecalloc(1, sizeof(php_zstd_context)); - return ctx; -} - static void php_zstd_output_handler_load_dict(php_zstd_context *ctx, int level) { @@ -944,22 +1139,6 @@ static zend_result php_zstd_output_handler_context_start(php_zstd_context *ctx) return SUCCESS; } -static void php_zstd_output_handler_context_free(php_zstd_context *ctx) -{ - if (ctx->cctx) { - ZSTD_freeCCtx(ctx->cctx); - ctx->cctx = NULL; - } - if (ctx->cdict) { - ZSTD_freeCDict(ctx->cdict); - ctx->cdict = NULL; - } - if (ctx->output.dst) { - efree(ctx->output.dst); - ctx->output.dst = NULL; - } -} - static void php_zstd_output_handler_context_dtor(void *opaq) { php_zstd_context *ctx = (php_zstd_context *) opaq; @@ -1298,6 +1477,10 @@ ZEND_MINIT_FUNCTION(zstd) ZSTD_VERSION_STRING, CONST_CS | CONST_PERSISTENT); + le_state = zend_register_list_destructors_ex(php_zstd_state_rsrc_dtor, + NULL, "zstd.state", + module_number); + php_register_url_stream_wrapper(STREAM_NAME, &php_stream_zstd_wrapper); #if defined(HAVE_APCU_SUPPORT) @@ -1397,6 +1580,11 @@ static zend_function_entry zstd_functions[] = { ZEND_FALIAS(zstd_decompress_usingcdict, zstd_uncompress_dict, arginfo_zstd_uncompress_dict) + ZEND_FE(zstd_compress_init, arginfo_zstd_compress_init) + ZEND_FE(zstd_compress_add, arginfo_zstd_compress_add) + ZEND_FE(zstd_uncompress_init, arginfo_zstd_uncompress_init) + ZEND_FE(zstd_uncompress_add, arginfo_zstd_uncompress_add) + ZEND_NS_FALIAS(PHP_ZSTD_NS, compress, zstd_compress, arginfo_zstd_compress) ZEND_NS_FALIAS(PHP_ZSTD_NS, uncompress, @@ -1416,6 +1604,15 @@ static zend_function_entry zstd_functions[] = { ZEND_NS_FALIAS(PHP_ZSTD_NS, decompress_usingcdict, zstd_uncompress_dict, arginfo_zstd_uncompress_dict) + ZEND_NS_FALIAS(PHP_ZSTD_NS, compress_init, + zstd_compress_init, arginfo_zstd_compress_init) + ZEND_NS_FALIAS(PHP_ZSTD_NS, compress_add, + zstd_compress_add, arginfo_zstd_compress_add) + ZEND_NS_FALIAS(PHP_ZSTD_NS, uncompress_init, + zstd_uncompress_init, arginfo_zstd_uncompress_init) + ZEND_NS_FALIAS(PHP_ZSTD_NS, uncompress_add, + zstd_uncompress_add, arginfo_zstd_uncompress_add) + #if PHP_VERSION_ID >= 80000 ZEND_FE(ob_zstd_handler, arginfo_ob_zstd_handler) #endif diff --git a/zstd.stub.php b/zstd.stub.php index 9169c6f..bbccf12 100644 --- a/zstd.stub.php +++ b/zstd.stub.php @@ -10,6 +10,26 @@ function zstd_compress_dict(string $data, string $dict, int $level = DEFAULT_COM function zstd_uncompress_dict(string $data, string $dict): string|false {} + /** + * @return resource|false + */ + function zstd_compress_init(int $level= 3) {} + + /** + * @param resource $context + */ + function zstd_compress_add($context, string $data, bool $end = false): string|false {} + + /** + * @return resource|false + */ + function zstd_uncompress_init() {} + + /** + * @param resource $context + */ + function zstd_uncompress_add($context, string $data): string|false {} + } namespace Zstd { @@ -22,4 +42,24 @@ function compress_dict(string $data, string $dict, int $level = 3): string|false function uncompress_dict(string $data, string $dict): string|false {} + /** + * @return resource|false + */ + function compress_init(int $level= 3) {} + + /** + * @param resource $context + */ + function compress_add($context, string $data, bool $end = false): string|false {} + + /** + * @return resource|false + */ + function uncompress_init() {} + + /** + * @param resource $context + */ + function uncompress_add($context, string $data): string|false {} + } From 683e59c994820ed5c4112cacf06874334c850068 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sun, 20 Apr 2025 21:29:52 +0200 Subject: [PATCH 02/12] Test compression with various chunk sizes --- tests/inc_comp.phpt | 38 ++++++++++++++++++++++++++------------ tests/inc_decomp.phpt | 2 +- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/tests/inc_comp.phpt b/tests/inc_comp.phpt index 31b834e..5840347 100755 --- a/tests/inc_comp.phpt +++ b/tests/inc_comp.phpt @@ -4,23 +4,37 @@ Incremental compression ===Done=== --EXPECTF-- +int(128) +resource(%d) of type (zstd.state) +int(%d) +bool(true) +bool(true) +int(512) +resource(%d) of type (zstd.state) +int(%d) +bool(true) +bool(true) +int(1024) resource(%d) of type (zstd.state) int(%d) bool(true) diff --git a/tests/inc_decomp.phpt b/tests/inc_decomp.phpt index 3737838..6cc5494 100755 --- a/tests/inc_decomp.phpt +++ b/tests/inc_decomp.phpt @@ -14,7 +14,7 @@ foreach ([128, 512, 1024] as $size) { $pos= 0; $decompressed= ''; while ($pos < strlen($compressed)) { - $chunk= substr($compressed, $pos, 1024); + $chunk= substr($compressed, $pos, $size); $decompressed.= zstd_uncompress_add($handle, $chunk); $pos+= strlen($chunk); } From 6fcceb71ed4fc5d3dbe151b347ad58a1393cffef Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sun, 20 Apr 2025 21:33:41 +0200 Subject: [PATCH 03/12] Use ZSTD_DStreamOutSize() for allocating the buffer during decompression See https://github.com/kjdev/php-ext-zstd/pull/77#discussion_r2051792081 --- zstd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zstd.c b/zstd.c index 7d5f7c5..7322735 100644 --- a/zstd.c +++ b/zstd.c @@ -549,7 +549,7 @@ ZEND_FUNCTION(zstd_uncompress_init) ZSTD_DCtx_reset(ctx->dctx, ZSTD_reset_session_only); - ctx->output.size = ZSTD_CStreamOutSize(); + ctx->output.size = ZSTD_DStreamOutSize(); ctx->output.dst = emalloc(ctx->output.size); ctx->output.pos = 0; From 4d2daf096f8d0d8b4c0508d8291d92d4c5215f38 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sun, 20 Apr 2025 21:36:53 +0200 Subject: [PATCH 04/12] Grow by ZSTD_DStreamOutSize() See https://github.com/kjdev/php-ext-zstd/pull/77#discussion_r2051792087 --- zstd.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/zstd.c b/zstd.c index 7322735..f637bed 100644 --- a/zstd.c +++ b/zstd.c @@ -455,7 +455,10 @@ static void php_zstd_output_handler_context_free(php_zstd_context *ctx) static void php_zstd_state_rsrc_dtor(zend_resource *res) { php_zstd_context *ctx = zend_fetch_resource(res, NULL, le_state); - php_zstd_output_handler_context_free(ctx); + if (ctx) { + php_zstd_output_handler_context_free(ctx); + efree(ctx); + } } ZEND_FUNCTION(zstd_compress_init) @@ -579,12 +582,12 @@ ZEND_FUNCTION(zstd_uncompress_add) ZSTD_inBuffer in = { in_buf, in_size, 0 }; size_t res = 1; - uint64_t size; + const size_t grow = ZSTD_DStreamOutSize(); ctx->output.pos = 0; while (in.pos < in.size && res > 0) { if (ctx->output.pos == ctx->output.size) { - ctx->output.size += size; + ctx->output.size += grow; ctx->output.dst = erealloc(ctx->output.dst, ctx->output.size); } From 2dce9a228013df9748546a6fbdf9d6df3cccc4ce Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sun, 20 Apr 2025 21:38:34 +0200 Subject: [PATCH 05/12] Set output.pos to 0 inside loop to prevent writing past the end of the buffer See https://github.com/kjdev/php-ext-zstd/pull/77#discussion_r2051792087 --- zstd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zstd.c b/zstd.c index f637bed..423d585 100644 --- a/zstd.c +++ b/zstd.c @@ -584,13 +584,13 @@ ZEND_FUNCTION(zstd_uncompress_add) size_t res = 1; const size_t grow = ZSTD_DStreamOutSize(); - ctx->output.pos = 0; while (in.pos < in.size && res > 0) { if (ctx->output.pos == ctx->output.size) { ctx->output.size += grow; ctx->output.dst = erealloc(ctx->output.dst, ctx->output.size); } + ctx->output.pos = 0; res = ZSTD_decompressStream(ctx->dctx, &ctx->output, &in); if (ZSTD_isError(res)) { php_error_docref(NULL, E_WARNING, From dd3dc9778e5173a1b03206c82c98c2e52aba42e2 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sun, 20 Apr 2025 21:43:48 +0200 Subject: [PATCH 06/12] Code style adjustments --- tests/inc_comp.phpt | 12 ++++++------ tests/inc_decomp.phpt | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/inc_comp.phpt b/tests/inc_comp.phpt index 5840347..da6813c 100755 --- a/tests/inc_comp.phpt +++ b/tests/inc_comp.phpt @@ -6,17 +6,17 @@ include(dirname(__FILE__) . '/data.inc'); foreach ([128, 512, 1024] as $size) { var_dump($size); - $handle= zstd_compress_init(); + $handle = zstd_compress_init(); var_dump($handle); $pos= 0; - $compressed= ''; + $compressed = ''; while ($pos < strlen($data)) { - $chunk= substr($data, $pos, $size); - $compressed.= zstd_compress_add($handle, $chunk, 0); - $pos+= strlen($chunk); + $chunk = substr($data, $pos, $size); + $compressed .= zstd_compress_add($handle, $chunk, false); + $pos += strlen($chunk); } - $compressed.= zstd_compress_add($handle, '', 1); + $compressed .= zstd_compress_add($handle, '', true); var_dump(strlen($compressed), strlen($compressed) < strlen($data)); var_dump($data === zstd_uncompress($compressed)); diff --git a/tests/inc_decomp.phpt b/tests/inc_decomp.phpt index 6cc5494..0ad2413 100755 --- a/tests/inc_decomp.phpt +++ b/tests/inc_decomp.phpt @@ -4,22 +4,22 @@ Incremental decompression ===Done=== From f791d1b603701dccd2a188b4d4d838a547fb4666 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sun, 20 Apr 2025 21:48:38 +0200 Subject: [PATCH 07/12] Remove unused variables --- zstd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/zstd.c b/zstd.c index 423d585..619082f 100644 --- a/zstd.c +++ b/zstd.c @@ -540,7 +540,6 @@ ZEND_FUNCTION(zstd_compress_add) ZEND_FUNCTION(zstd_uncompress_init) { php_zstd_context *ctx = php_zstd_output_handler_context_init(); - size_t result; ctx->dctx = ZSTD_createDCtx(); if (ctx->dctx == NULL) { @@ -565,7 +564,6 @@ ZEND_FUNCTION(zstd_uncompress_add) php_zstd_context *ctx; char *in_buf; size_t in_size; - zend_bool end = 0; smart_string out = {0}; ZEND_PARSE_PARAMETERS_START(2, 2) From 6de2c73aa101923b4e87cf8b71ea21ea0d51c7f0 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Mon, 21 Apr 2025 09:37:40 +0200 Subject: [PATCH 08/12] Add incremental (un)compression functions to README See https://github.com/kjdev/php-ext-zstd/pull/77#issuecomment-2817350683 --- README.md | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 92c91ae..6dc4df8 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,11 @@ LIBZSTD\_VERSION\_STRING | libzstd version string * zstd\_uncompress — Zstandard decompression * zstd\_compress\_dict — Zstandard compression using a digested dictionary * zstd\_uncompress\_dict — Zstandard decompression using a digested dictionary +* zstd\_compress_\init — Initialize an incremental compress context +* zstd\_compress\_add — Incrementally compress data +* zstd\_uncompress_\init — Initialize an incremental uncompress context +* zstd\_uncompress\_add — Incrementally uncompress data + ### zstd\_compress — Zstandard compression @@ -209,6 +214,88 @@ Zstandard decompression using a digested dictionary. Returns the decompressed data or FALSE if an error occurred. +### zstd\_compress\_init — Initialize an incremental compress context + +#### Description + +resource **zstd\_compress\_init** ( [ int $level = ZSTD_COMPRESS_LEVEL_DEFAULT ] ) + +Initialize an incremental compress context + +#### Parameters + +* _level_ + + The higher the level, the slower the compression. (Defaults to `ZSTD_COMPRESS_LEVEL_DEFAULT`) + +#### Return Values + +Returns a zstd context resource (zstd.state) on success, or FALSE on failure + + +### zstd\_compress\_add — Incrementally compress data + +#### Description + +string **zstd\_compress\_addt** ( resource *$context*, string *$data* [, bool *$end* = false ] ) + +Incrementally compress data + +#### Parameters + +* _context_ + + A context created with `zstd_compress_init()`. + +* _data_ + + A chunk of data to compress. + +* _end_ + + Set to true to terminate with the last chunk of data. + +#### Return Values + +Returns a chunk of compressed data, or FALSE on failure. + + +### zstd\_uncompress\_init — Initialize an incremental uncompress context + +#### Description + +resource **zstd\_uncompress\_init** ( void ) + +Initialize an incremental uncompress context + +#### Return Values + +Returns a zstd context resource (zstd.state) on success, or FALSE on failure + + +### zstd\_uncompress\_add — Incrementally uncompress data + +#### Description + +string **zstd\_uncompress\_addt** ( resource *$context*, string *$data* ) + +Incrementally uncompress data + +#### Parameters + +* _context_ + + A context created with `zstd_uncompress_init()`. + +* _data_ + + A chunk of compressed data. + +#### Return Values + +Returns a chunk of uncompressed data, or FALSE on failure. + + ## Namespace ``` @@ -218,10 +305,16 @@ function compress( $data [, $level = 3 ] ) function uncompress( $data ) function compress_dict ( $data, $dict ) function uncompress_dict ( $data, $dict ) +function compress_init ( [ $level = 3 ] ) +function compress_add ( $context, $data [, $end = false ] ) +function uncompress_init () +function uncompress_add ( $context, $data ) + ``` -`zstd_compress`, `zstd_uncompress`, `zstd_compress_dict` and -`zstd_uncompress_dict` function alias. +`zstd_compress`, `zstd_uncompress`, `zstd_compress_dict`, +`zstd_uncompress_dict`, `zstd_compress_init`, `zstd_compress_add`, +`zstd_uncompress_init` and `zstd_uncompress_add` function alias. ## Streams From 08b21023a34cf68c22cfcdf29ed2362c63bfaaca Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Mon, 21 Apr 2025 09:39:32 +0200 Subject: [PATCH 09/12] Add example from pull request with and without namespaces See https://github.com/kjdev/php-ext-zstd/pull/77#pullrequestreview-2780336077 --- tests/inc.phpt | 27 +++++++++++++++++++++++++++ tests/inc_ns.phpt | 27 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100755 tests/inc.phpt create mode 100755 tests/inc_ns.phpt diff --git a/tests/inc.phpt b/tests/inc.phpt new file mode 100755 index 0000000..ddd329f --- /dev/null +++ b/tests/inc.phpt @@ -0,0 +1,27 @@ +--TEST-- +Incremental compression and decompression +--FILE-- + +===Done=== +--EXPECTF-- +Hello, World! +Hello, World! +===Done=== diff --git a/tests/inc_ns.phpt b/tests/inc_ns.phpt new file mode 100755 index 0000000..746d520 --- /dev/null +++ b/tests/inc_ns.phpt @@ -0,0 +1,27 @@ +--TEST-- +Incremental compression and decompression (namespaces) +--FILE-- + +===Done=== +--EXPECTF-- +Hello, World! +Hello, World! +===Done=== From 1f026815f2350ac27c04d1ca4bd0eb9f81938fca Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Mon, 21 Apr 2025 09:43:27 +0200 Subject: [PATCH 10/12] Fix a minor inconsistency in zstd_uncompress_init() error message --- zstd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zstd.c b/zstd.c index 619082f..f7b783e 100644 --- a/zstd.c +++ b/zstd.c @@ -544,7 +544,7 @@ ZEND_FUNCTION(zstd_uncompress_init) ctx->dctx = ZSTD_createDCtx(); if (ctx->dctx == NULL) { efree(ctx); - ZSTD_WARNING("ZSTD_createCCtx() error"); + ZSTD_WARNING("ZSTD_createDCtx() error"); RETURN_FALSE; } ctx->cdict = NULL; From 67aa1f08cf903da65ea8655ef5e275ae7476e909 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Mon, 21 Apr 2025 09:44:20 +0200 Subject: [PATCH 11/12] Fix typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6dc4df8..b904a09 100644 --- a/README.md +++ b/README.md @@ -237,7 +237,7 @@ Returns a zstd context resource (zstd.state) on success, or FALSE on failure #### Description -string **zstd\_compress\_addt** ( resource *$context*, string *$data* [, bool *$end* = false ] ) +string **zstd\_compress\_add** ( resource *$context*, string *$data* [, bool *$end* = false ] ) Incrementally compress data @@ -277,7 +277,7 @@ Returns a zstd context resource (zstd.state) on success, or FALSE on failure #### Description -string **zstd\_uncompress\_addt** ( resource *$context*, string *$data* ) +string **zstd\_uncompress\_add** ( resource *$context*, string *$data* ) Incrementally uncompress data From 6020f3d863ba240f1f5291e8e1378f833177e3d8 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Mon, 21 Apr 2025 09:49:33 +0200 Subject: [PATCH 12/12] Consistently use underscores for parameter emphasis styling --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b904a09..2ffce4e 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,7 @@ Returns the decompressed data or FALSE if an error occurred. #### Description -resource **zstd\_compress\_init** ( [ int $level = ZSTD_COMPRESS_LEVEL_DEFAULT ] ) +resource **zstd\_compress\_init** ( [ int _$level_ = ZSTD_COMPRESS_LEVEL_DEFAULT ] ) Initialize an incremental compress context @@ -237,7 +237,7 @@ Returns a zstd context resource (zstd.state) on success, or FALSE on failure #### Description -string **zstd\_compress\_add** ( resource *$context*, string *$data* [, bool *$end* = false ] ) +string **zstd\_compress\_add** ( resource _$context_, string _$data_ [, bool _$end_ = false ] ) Incrementally compress data @@ -277,7 +277,7 @@ Returns a zstd context resource (zstd.state) on success, or FALSE on failure #### Description -string **zstd\_uncompress\_add** ( resource *$context*, string *$data* ) +string **zstd\_uncompress\_add** ( resource _$context_, string _$data_ ) Incrementally uncompress data