diff --git a/zstd.c b/zstd.c index d55bbd3..5479ba5 100644 --- a/zstd.c +++ b/zstd.c @@ -60,6 +60,12 @@ #define zend_string_efree(string) zend_string_free(string) #endif +#define ZSTD_WARNING(...) \ + php_error_docref(NULL, E_WARNING, __VA_ARGS__) + +#define ZSTD_IS_ERROR(result) \ + UNEXPECTED(ZSTD_isError(result)) + zend_class_entry *zstd_context_ptr; static const zend_function_entry zstd_context_methods[] = { ZEND_FE_END @@ -69,6 +75,7 @@ struct _php_zstd_context { ZSTD_CCtx* cctx; ZSTD_DCtx* dctx; ZSTD_CDict *cdict; + ZSTD_DDict *ddict; ZSTD_inBuffer input; ZSTD_outBuffer output; zend_object std; @@ -84,15 +91,21 @@ static php_zstd_context *php_zstd_context_from_obj(zend_object *obj) #define PHP_ZSTD_CONTEXT_OBJ_INIT_OF_CLASS(ce) \ object_init_ex(return_value, ce); \ php_zstd_context *ctx = php_zstd_context_from_obj(Z_OBJ_P(return_value)); \ - ctx->cctx = NULL; \ - ctx->dctx = NULL; \ - ctx->cdict = 0; \ - ctx->input.src = NULL; \ - ctx->input.size = 0; \ - ctx->input.pos = 0; \ - ctx->output.dst = NULL; \ - ctx->output.size = 0; \ - ctx->output.pos = 0; + php_zstd_context_init(ctx); + +static void php_zstd_context_init(php_zstd_context *ctx) +{ + ctx->cctx = NULL; + ctx->dctx = NULL; + ctx->cdict = NULL; + ctx->ddict = NULL; + ctx->input.src = NULL; + ctx->input.size = 0; + ctx->input.pos = 0; + ctx->output.dst = NULL; + ctx->output.size = 0; + ctx->output.pos = 0; +} static void php_zstd_context_free(php_zstd_context *ctx) { @@ -108,6 +121,10 @@ static void php_zstd_context_free(php_zstd_context *ctx) ZSTD_freeCDict(ctx->cdict); ctx->cdict = NULL; } + if (ctx->ddict) { + ZSTD_freeDDict(ctx->ddict); + ctx->ddict = NULL; + } if (ctx->output.dst) { efree(ctx->output.dst); ctx->output.dst = NULL; @@ -140,6 +157,65 @@ php_zstd_context_create_object(zend_class_entry *class_type, return &intern->std; } +static int +php_zstd_context_create_compress(php_zstd_context *ctx, + zend_long level, zend_string *dict) +{ + ctx->cctx = ZSTD_createCCtx(); + if (ctx->cctx == NULL) { + ZSTD_WARNING("failed to create compress context"); + return FAILURE; + } + + ZSTD_CCtx_reset(ctx->cctx, ZSTD_reset_session_only); + ZSTD_CCtx_setParameter(ctx->cctx, ZSTD_c_compressionLevel, level); + + if (dict) { + ctx->cdict = ZSTD_createCDict(ZSTR_VAL(dict), ZSTR_LEN(dict), + (int)level); + if (!ctx->cdict) { + ZSTD_WARNING("failed to load dictionary"); + return FAILURE; + } + + ZSTD_CCtx_refCDict(ctx->cctx, ctx->cdict); + } + + ctx->output.size = ZSTD_CStreamOutSize(); + ctx->output.dst = emalloc(ctx->output.size); + ctx->output.pos = 0; + + return SUCCESS; +} + +static int +php_zstd_context_create_decompress(php_zstd_context *ctx, zend_string *dict) +{ + ctx->dctx = ZSTD_createDCtx(); + if (ctx->dctx == NULL) { + ZSTD_WARNING("failed to prepare uncompression"); + return FAILURE; + } + + ZSTD_DCtx_reset(ctx->dctx, ZSTD_reset_session_only); + + if (dict) { + ctx->ddict = ZSTD_createDDict(ZSTR_VAL(dict), ZSTR_LEN(dict)); + if (!ctx->ddict) { + ZSTD_WARNING("failed to load dictionary"); + return FAILURE; + } + + ZSTD_DCtx_refDDict(ctx->dctx, ctx->ddict); + } + + ctx->output.size = ZSTD_DStreamOutSize(); + ctx->output.dst = emalloc(ctx->output.size); + ctx->output.pos = 0; + + return SUCCESS; +} + /* Zstd Compress Context */ zend_class_entry *php_zstd_compress_context_ce; static zend_object_handlers php_zstd_compress_context_object_handlers; @@ -235,12 +311,6 @@ static php_zstd_context* php_zstd_output_handler_context_init(void) #define php_zstd_output_handler_context_free(ctx) php_zstd_context_free(ctx) -#define ZSTD_WARNING(...) \ - php_error_docref(NULL, E_WARNING, __VA_ARGS__) - -#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) @@ -310,35 +380,16 @@ static size_t zstd_check_compress_level(zend_long level) return 1; } -// Truncate string to given size -static zend_always_inline zend_string* -zstd_string_output_truncate(zend_string* output, size_t real_length) -{ - size_t capacity = ZSTR_LEN(output); - size_t free_space = capacity - real_length; - - // Reallocate just when capacity and real size differs a lot - // or the free space is bigger than 1 MB - if (UNEXPECTED(free_space > (capacity / 8) - || free_space > (1024 * 1024))) { - output = zend_string_truncate(output, real_length, 0); - } - ZSTR_LEN(output) = real_length; - ZSTR_VAL(output)[real_length] = '\0'; - return output; -} - ZEND_FUNCTION(zstd_compress) { - zend_string *output; - size_t size, result; + size_t result; + smart_string out = { 0 }; zend_long level = DEFAULT_COMPRESS_LEVEL; - - char *input; - size_t input_len; + zend_string *input; + php_zstd_context ctx; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(input, input_len) + Z_PARAM_STR(input) Z_PARAM_OPTIONAL Z_PARAM_LONG(level) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); @@ -347,124 +398,110 @@ ZEND_FUNCTION(zstd_compress) RETURN_FALSE; } - size = ZSTD_compressBound(input_len); - output = zend_string_alloc(size, 0); + php_zstd_context_init(&ctx); + if (php_zstd_context_create_compress(&ctx, level, NULL) != SUCCESS) { + php_zstd_context_free(&ctx); + RETURN_FALSE; + } - result = ZSTD_compress(ZSTR_VAL(output), size, input, input_len, - (int)level); + ctx.input.src = ZSTR_VAL(input); + ctx.input.size = ZSTR_LEN(input); + ctx.input.pos = 0; - if (ZSTD_IS_ERROR(result)) { - zend_string_efree(output); - RETVAL_FALSE; - } + do { + ctx.output.pos = 0; + result = ZSTD_compressStream2(ctx.cctx, &ctx.output, + &ctx.input, ZSTD_e_end); + if (ZSTD_isError(result)) { + ZSTD_WARNING("%s", ZSTD_getErrorName(result)); + smart_string_free(&out); + php_zstd_context_free(&ctx); + RETURN_FALSE; + } + smart_string_appendl(&out, ctx.output.dst, ctx.output.pos); + } while (result > 0); - output = zstd_string_output_truncate(output, result); - RETVAL_NEW_STR(output); + RETVAL_STRINGL(out.c, out.len); + smart_string_free(&out); + + php_zstd_context_free(&ctx); } ZEND_FUNCTION(zstd_uncompress) { + size_t chunk, result; uint64_t size; - size_t result; - zend_string *output; - uint8_t streaming = 0; - - char *input; - size_t input_len; + smart_string out = { 0 }; + zend_string *input; + php_zstd_context ctx; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(input, input_len) + Z_PARAM_STR(input) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - size = ZSTD_getFrameContentSize(input, input_len); + size = ZSTD_getFrameContentSize(ZSTR_VAL(input), ZSTR_LEN(input)); if (size == ZSTD_CONTENTSIZE_ERROR) { ZSTD_WARNING("it was not compressed by zstd"); RETURN_FALSE; } else if (size == ZSTD_CONTENTSIZE_UNKNOWN) { - streaming = 1; size = ZSTD_DStreamOutSize(); } - output = zend_string_alloc(size, 0); + php_zstd_context_init(&ctx); + if (php_zstd_context_create_decompress(&ctx, NULL) != SUCCESS) { + php_zstd_context_free(&ctx); + RETURN_FALSE; + } - if (!streaming) { - result = ZSTD_decompress(ZSTR_VAL(output), size, - input, input_len); + chunk = ZSTD_DStreamOutSize(); - if (ZSTD_IS_ERROR(result)) { - zend_string_efree(output); - ZSTD_WARNING("%s", ZSTD_getErrorName(result)); - RETURN_FALSE; - } + ctx.input.src = ZSTR_VAL(input); + ctx.input.size = ZSTR_LEN(input); + ctx.input.pos = 0; - } else { - ZSTD_DStream *stream; - ZSTD_inBuffer in = { NULL, 0, 0 }; - ZSTD_outBuffer out = { NULL, 0, 0 }; - - stream = ZSTD_createDStream(); - if (stream == NULL) { - zend_string_efree(output); - ZSTD_WARNING("failed to create uncompress context"); - RETURN_FALSE; + ctx.output.dst = emalloc(size); + ctx.output.size = size; + ctx.output.pos = 0; + + while (ctx.input.pos < ctx.input.size) { + if (ctx.output.pos == ctx.output.size) { + ctx.output.size += chunk; + ctx.output.dst = erealloc(ctx.output.dst, ctx.output.size); } - result = ZSTD_initDStream(stream); + ctx.output.pos = 0; + result = ZSTD_decompressStream(ctx.dctx, &ctx.output, &ctx.input); if (ZSTD_IS_ERROR(result)) { - zend_string_efree(output); - ZSTD_freeDStream(stream); + smart_string_free(&out); + php_zstd_context_free(&ctx); ZSTD_WARNING("%s", ZSTD_getErrorName(result)); RETURN_FALSE; } - in.src = input; - in.size = input_len; - in.pos = 0; - - out.dst = ZSTR_VAL(output); - out.size = size; - out.pos = 0; - - while (in.pos < in.size) { - if (out.pos == out.size) { - out.size += size; - output = zend_string_extend(output, out.size, 0); - out.dst = ZSTR_VAL(output); - } - - result = ZSTD_decompressStream(stream, &out, &in); - if (ZSTD_IS_ERROR(result)) { - zend_string_efree(output); - ZSTD_freeDStream(stream); - ZSTD_WARNING("%s", ZSTD_getErrorName(result)); - RETURN_FALSE; - } + smart_string_appendl(&out, ctx.output.dst, ctx.output.pos); - if (result == 0) { - break; - } + if (result == 0) { + break; } - - result = out.pos; - - ZSTD_freeDStream(stream); } - output = zstd_string_output_truncate(output, result); - RETVAL_NEW_STR(output); + RETVAL_STRINGL(out.c, out.len); + smart_string_free(&out); + + php_zstd_context_free(&ctx); } ZEND_FUNCTION(zstd_compress_dict) { + size_t result; + smart_string out = { 0 }; zend_long level = DEFAULT_COMPRESS_LEVEL; - - zend_string *output; - char *input, *dict; - size_t input_len, dict_len; + zend_string *input, *dict; + php_zstd_context ctx; ZEND_PARSE_PARAMETERS_START(2, 3) - Z_PARAM_STRING(input, input_len) - Z_PARAM_STRING(dict, dict_len) + Z_PARAM_STR(input) + Z_PARAM_STR(dict) Z_PARAM_OPTIONAL Z_PARAM_LONG(level) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); @@ -473,141 +510,100 @@ ZEND_FUNCTION(zstd_compress_dict) RETURN_FALSE; } - ZSTD_CCtx* const cctx = ZSTD_createCCtx(); - if (cctx == NULL) { - ZSTD_WARNING("failed to create compress context"); - RETURN_FALSE; - } - ZSTD_CDict* const cdict = ZSTD_createCDict(dict, - dict_len, - (int)level); - if (!cdict) { - ZSTD_freeCStream(cctx); - ZSTD_WARNING("failed to load dictionary"); + php_zstd_context_init(&ctx); + if (php_zstd_context_create_compress(&ctx, level, dict) != SUCCESS) { + php_zstd_context_free(&ctx); RETURN_FALSE; } - size_t const cBuffSize = ZSTD_compressBound(input_len); - output = zend_string_alloc(cBuffSize, 0); + ctx.input.src = ZSTR_VAL(input); + ctx.input.size = ZSTR_LEN(input); + ctx.input.pos = 0; - size_t const cSize = ZSTD_compress_usingCDict(cctx, ZSTR_VAL(output), cBuffSize, - input, - input_len, - cdict); - if (ZSTD_IS_ERROR(cSize)) { - ZSTD_freeCStream(cctx); - ZSTD_freeCDict(cdict); - zend_string_efree(output); - ZSTD_WARNING("%s", ZSTD_getErrorName(cSize)); - RETURN_FALSE; - } + do { + ctx.output.pos = 0; + result = ZSTD_compressStream2(ctx.cctx, &ctx.output, + &ctx.input, ZSTD_e_end); + if (ZSTD_isError(result)) { + ZSTD_WARNING("%s", ZSTD_getErrorName(result)); + smart_string_free(&out); + php_zstd_context_free(&ctx); + RETURN_FALSE; + } + smart_string_appendl(&out, ctx.output.dst, ctx.output.pos); + } while (result > 0); - output = zstd_string_output_truncate(output, cSize); - RETVAL_NEW_STR(output); + RETVAL_STRINGL(out.c, out.len); + smart_string_free(&out); - ZSTD_freeCCtx(cctx); - ZSTD_freeCDict(cdict); + php_zstd_context_free(&ctx); } ZEND_FUNCTION(zstd_uncompress_dict) { - char *input, *dict; - size_t input_len, dict_len; - zend_string *output; - uint8_t streaming = 0; - size_t result; + size_t chunk, result; unsigned long long size; - ZSTD_DCtx *dctx; - ZSTD_DDict *ddict; + smart_string out = { 0 }; + zend_string *input, *dict; + php_zstd_context ctx; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(input, input_len) - Z_PARAM_STRING(dict, dict_len) + Z_PARAM_STR(input) + Z_PARAM_STR(dict) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - size = ZSTD_getFrameContentSize(input, input_len); + size = ZSTD_getFrameContentSize(ZSTR_VAL(input), ZSTR_LEN(input)); if (size == 0) { RETURN_EMPTY_STRING(); } else if (size == ZSTD_CONTENTSIZE_ERROR) { ZSTD_WARNING("it was not compressed by zstd"); RETURN_FALSE; } else if (size == ZSTD_CONTENTSIZE_UNKNOWN) { - streaming = 1; - size = input_len + ZSTD_DStreamOutSize(); + size = ZSTD_DStreamOutSize(); } - dctx = ZSTD_createDCtx(); - if (dctx == NULL) { - ZSTD_WARNING("failed to prepare uncompression"); - RETURN_FALSE; - } - ddict = ZSTD_createDDict(dict, dict_len); - if (!ddict) { - ZSTD_freeDCtx(dctx); - ZSTD_WARNING("failed to load dictionary"); + php_zstd_context_init(&ctx); + if (php_zstd_context_create_decompress(&ctx, dict) != SUCCESS) { + php_zstd_context_free(&ctx); RETURN_FALSE; } - output = zend_string_alloc(size, 0); + chunk = ZSTD_DStreamOutSize(); - if (!streaming) { - result = ZSTD_decompress_usingDDict(dctx, ZSTR_VAL(output), size, - input, input_len, ddict); + ctx.input.src = ZSTR_VAL(input); + ctx.input.size = ZSTR_LEN(input); + ctx.input.pos = 0; + + ctx.output.dst = emalloc(size); + ctx.output.size = size; + ctx.output.pos = 0; + + while (ctx.input.pos < ctx.input.size) { + if (ctx.output.pos == ctx.output.size) { + ctx.output.size += chunk; + ctx.output.dst = erealloc(ctx.output.dst, ctx.output.size); + } + + ctx.output.pos = 0; + result = ZSTD_decompressStream(ctx.dctx, &ctx.output, &ctx.input); if (ZSTD_IS_ERROR(result)) { - zend_string_efree(output); + smart_string_free(&out); + php_zstd_context_free(&ctx); ZSTD_WARNING("%s", ZSTD_getErrorName(result)); - RETVAL_FALSE; - } else if (result != size) { - zend_string_efree(output); - ZSTD_WARNING("failed to uncompress"); - RETVAL_FALSE; - } else { - output = zstd_string_output_truncate(output, result); - RETVAL_NEW_STR(output); + RETURN_FALSE; } - } else { - ZSTD_inBuffer in = { NULL, 0, 0 }; - ZSTD_outBuffer out = { NULL, 0, 0 }; - size_t chunk = ZSTD_DStreamOutSize(); - - ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only); - ZSTD_DCtx_refDDict(dctx, ddict); - - in.src = input; - in.size = input_len; - in.pos = 0; - - out.dst = ZSTR_VAL(output); - out.size = size; - out.pos = 0; - - while (in.pos < in.size) { - if (out.pos == out.size) { - out.size += chunk; - output = zend_string_extend(output, out.size, 0); - out.dst = ZSTR_VAL(output); - } - result = ZSTD_decompressStream(dctx, &out, &in); - if (ZSTD_IS_ERROR(result)) { - zend_string_efree(output); - ZSTD_freeDCtx(dctx); - ZSTD_freeDDict(ddict); - ZSTD_WARNING("%s", ZSTD_getErrorName(result)); - RETURN_FALSE; - } + smart_string_appendl(&out, ctx.output.dst, ctx.output.pos); - if (result == 0) { - break; - } + if (result == 0) { + break; } - - output = zstd_string_output_truncate(output, out.pos); - RETVAL_NEW_STR(output); } - ZSTD_freeDCtx(dctx); - ZSTD_freeDDict(ddict); + RETVAL_STRINGL(out.c, out.len); + smart_string_free(&out); + + php_zstd_context_free(&ctx); } ZEND_FUNCTION(zstd_compress_init) @@ -625,27 +621,16 @@ ZEND_FUNCTION(zstd_compress_init) PHP_ZSTD_CONTEXT_OBJ_INIT_OF_CLASS(php_zstd_compress_context_ce); - ctx->cctx = ZSTD_createCCtx(); - if (ctx->cctx == NULL) { + if (php_zstd_context_create_compress(ctx, level, NULL) != SUCCESS) { zval_ptr_dtor(return_value); - ZSTD_WARNING("failed to create compress context"); 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; } ZEND_FUNCTION(zstd_compress_add) { php_zstd_context *ctx; - char *in_buf; - size_t in_size; + zend_string *input; zend_bool end = 0; smart_string out = {0}; #if PHP_VERSION_ID >= 80000 @@ -660,7 +645,7 @@ ZEND_FUNCTION(zstd_compress_add) #else Z_PARAM_OBJECT_OF_CLASS(obj, php_zstd_compress_context_ce) #endif - Z_PARAM_STRING(in_buf, in_size) + Z_PARAM_STR(input) Z_PARAM_OPTIONAL Z_PARAM_BOOL(end) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); @@ -675,7 +660,7 @@ ZEND_FUNCTION(zstd_compress_add) RETURN_FALSE; } - ZSTD_inBuffer in = { in_buf, in_size, 0 }; + ZSTD_inBuffer in = { ZSTR_VAL(input), ZSTR_LEN(input), 0 }; size_t res; do { @@ -698,27 +683,17 @@ ZEND_FUNCTION(zstd_uncompress_init) { PHP_ZSTD_CONTEXT_OBJ_INIT_OF_CLASS(php_zstd_uncompress_context_ce); - ctx->dctx = ZSTD_createDCtx(); - if (ctx->dctx == NULL) { + if (php_zstd_context_create_decompress(ctx, NULL) != SUCCESS) { zval_ptr_dtor(return_value); - ZSTD_WARNING("failed to create uncompress context"); RETURN_FALSE; } - ctx->cdict = NULL; - - ZSTD_DCtx_reset(ctx->dctx, ZSTD_reset_session_only); - - ctx->output.size = ZSTD_DStreamOutSize(); - ctx->output.dst = emalloc(ctx->output.size); - ctx->output.pos = 0; } ZEND_FUNCTION(zstd_uncompress_add) { zend_object *context; php_zstd_context *ctx; - char *in_buf; - size_t in_size; + zend_string *input; smart_string out = {0}; #if PHP_VERSION_ID >= 80000 zend_object *obj; @@ -732,7 +707,7 @@ ZEND_FUNCTION(zstd_uncompress_add) #else Z_PARAM_OBJECT_OF_CLASS(obj, php_zstd_uncompress_context_ce) #endif - Z_PARAM_STRING(in_buf, in_size) + Z_PARAM_STR(input) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); #if PHP_VERSION_ID >= 80000 @@ -745,7 +720,7 @@ ZEND_FUNCTION(zstd_uncompress_add) RETURN_FALSE; } - ZSTD_inBuffer in = { in_buf, in_size, 0 }; + ZSTD_inBuffer in = { ZSTR_VAL(input), ZSTR_LEN(input), 0 }; size_t res = 1; const size_t grow = ZSTD_DStreamOutSize(); @@ -773,10 +748,7 @@ ZEND_FUNCTION(zstd_uncompress_add) typedef struct _php_zstd_stream_data { char *bufin, *bufout; size_t sizein, sizeout; - ZSTD_CCtx* cctx; - ZSTD_DCtx* dctx; - ZSTD_inBuffer input; - ZSTD_outBuffer output; + php_zstd_context ctx; php_stream *stream; } php_zstd_stream_data; @@ -801,9 +773,9 @@ static int php_zstd_decomp_close(php_stream *stream, int close_handle) } } - ZSTD_freeDCtx(self->dctx); + php_zstd_context_free(&self->ctx); + efree(self->bufin); - efree(self->bufout); efree(self); stream->abstract = NULL; @@ -819,14 +791,15 @@ static int php_zstd_comp_flush_or_end(php_zstd_stream_data *self, int end) /* Flush / End */ do { - self->output.pos = 0; - res = ZSTD_compressStream2(self->cctx, &self->output, &in, + self->ctx.output.pos = 0; + res = ZSTD_compressStream2(self->ctx.cctx, &self->ctx.output, &in, end ? ZSTD_e_end : ZSTD_e_flush); if (ZSTD_isError(res)) { ZSTD_WARNING("zstd: %s", ZSTD_getErrorName(res)); ret = EOF; } - php_stream_write(self->stream, self->output.dst, self->output.pos); + php_stream_write(self->stream, + self->ctx.output.dst, self->ctx.output.pos); } while (res > 0); return ret; @@ -858,8 +831,8 @@ static int php_zstd_comp_close(php_stream *stream, int close_handle) } } - ZSTD_freeCCtx(self->cctx); - efree(self->output.dst); + php_zstd_context_free(&self->ctx); + efree(self); stream->abstract = NULL; @@ -880,29 +853,29 @@ static ssize_t php_zstd_decomp_read(php_stream *stream, char *buf, size_t count) STREAM_DATA_FROM_STREAM(); while (count > 0) { - x = self->output.size - self->output.pos; + x = self->ctx.output.size - self->ctx.output.pos; /* enough available */ if (x >= count) { - memcpy(buf, self->bufout + self->output.pos, count); - self->output.pos += count; + memcpy(buf, self->bufout + self->ctx.output.pos, count); + self->ctx.output.pos += count; ret += count; return ret; } /* take remaining from out */ if (x) { - memcpy(buf, self->bufout + self->output.pos, x); - self->output.pos += x; + memcpy(buf, self->bufout + self->ctx.output.pos, x); + self->ctx.output.pos += x; ret += x; buf += x; count -= x; } /* decompress */ - if (self->input.pos < self->input.size) { + if (self->ctx.input.pos < self->ctx.input.size) { /* for zstd */ - self->output.pos = 0; - self->output.size = self->sizeout; - res = ZSTD_decompressStream(self->dctx, - &self->output, &self->input); + self->ctx.output.pos = 0; + self->ctx.output.size = self->sizeout; + res = ZSTD_decompressStream(self->ctx.dctx, + &self->ctx.output, &self->ctx.input); if (ZSTD_IS_ERROR(res)) { ZSTD_WARNING("zstd: %s", ZSTD_getErrorName(res)); #if PHP_VERSION_ID >= 70400 @@ -910,14 +883,14 @@ static ssize_t php_zstd_decomp_read(php_stream *stream, char *buf, size_t count) #endif } /* for us */ - self->output.size = self->output.pos; - self->output.pos = 0; + self->ctx.output.size = self->ctx.output.pos; + self->ctx.output.pos = 0; } else { /* read */ - self->input.pos = 0; - self->input.size = php_stream_read(self->stream, - self->bufin, self->sizein); - if (!self->input.size) { + self->ctx.input.pos = 0; + self->ctx.input.size = php_stream_read(self->stream, + self->bufin, self->sizein); + if (!self->ctx.input.size) { /* EOF */ count = 0; } @@ -940,8 +913,8 @@ php_zstd_comp_write(php_stream *stream, const char *buf, size_t count) ZSTD_inBuffer in = { buf, count, 0 }; do { - self->output.pos = 0; - res = ZSTD_compressStream2(self->cctx, &self->output, + self->ctx.output.pos = 0; + res = ZSTD_compressStream2(self->ctx.cctx, &self->ctx.output, &in, ZSTD_e_continue); if (ZSTD_isError(res)) { ZSTD_WARNING("zstd: %s", ZSTD_getErrorName(res)); @@ -949,7 +922,8 @@ php_zstd_comp_write(php_stream *stream, const char *buf, size_t count) return -1; #endif } - php_stream_write(self->stream, self->output.dst, self->output.pos); + php_stream_write(self->stream, + self->ctx.output.dst, self->ctx.output.pos); } while (res > 0); @@ -996,8 +970,7 @@ php_stream_zstd_opener( php_zstd_stream_data *self; int level = ZSTD_CLEVEL_DEFAULT; int compress; - ZSTD_CDict *cdict = NULL; - ZSTD_DDict *ddict = NULL; + zend_string *dict = NULL; if (strncasecmp(STREAM_NAME, path, sizeof(STREAM_NAME)-1) == 0) { path += sizeof(STREAM_NAME)-1; @@ -1022,7 +995,6 @@ php_stream_zstd_opener( if (context) { zval *tmpzval; - zend_string *data; tmpzval = php_stream_context_get_option(context, "zstd", "level"); if (NULL != tmpzval) { @@ -1030,13 +1002,7 @@ php_stream_zstd_opener( } tmpzval = php_stream_context_get_option(context, "zstd", "dict"); if (NULL != tmpzval) { - data = zval_get_string(tmpzval); - if (compress) { - cdict = ZSTD_createCDict(ZSTR_VAL(data), ZSTR_LEN(data), level); - } else { - ddict = ZSTD_createDDict(ZSTR_VAL(data), ZSTR_LEN(data)); - } - zend_string_release(data); + dict = zval_get_string(tmpzval); } } @@ -1051,48 +1017,54 @@ php_stream_zstd_opener( options | REPORT_ERRORS, NULL); if (!self->stream) { efree(self); + if (dict) { + zend_string_release(dict); + } return NULL; } + php_zstd_context_init(&self->ctx); + /* File */ if (compress) { - self->dctx = NULL; - self->cctx = ZSTD_createCCtx(); - if (!self->cctx) { + if (php_zstd_context_create_compress(&self->ctx, + level, dict) != SUCCESS) { ZSTD_WARNING("zstd: compression context failed"); php_stream_close(self->stream); efree(self); + if (dict) { + zend_string_release(dict); + } return NULL; } - ZSTD_CCtx_reset(self->cctx, ZSTD_reset_session_only); - ZSTD_CCtx_refCDict(self->cctx, cdict); - ZSTD_CCtx_setParameter(self->cctx, ZSTD_c_compressionLevel, level); - self->output.size = ZSTD_CStreamOutSize(); - self->output.dst = emalloc(self->output.size); - self->output.pos = 0; + if (dict) { + zend_string_release(dict); + } return php_stream_alloc(&php_stream_zstd_write_ops, self, NULL, mode); } else { - self->dctx = ZSTD_createDCtx(); - if (!self->dctx) { + if (php_zstd_context_create_decompress(&self->ctx, dict) != SUCCESS) { ZSTD_WARNING("zstd: compression context failed"); php_stream_close(self->stream); efree(self); + if (dict) { + zend_string_release(dict); + } return NULL; } - self->cctx = NULL; self->bufin = emalloc(self->sizein = ZSTD_DStreamInSize()); - self->bufout = emalloc(self->sizeout = ZSTD_DStreamOutSize()); - ZSTD_DCtx_reset(self->dctx, ZSTD_reset_session_only); - ZSTD_DCtx_refDDict(self->dctx, ddict); - self->input.src = self->bufin; - self->input.pos = 0; - self->input.size = 0; - self->output.dst = self->bufout; - self->output.pos = 0; - self->output.size = 0; + self->bufout = self->ctx.output.dst; + self->sizeout = self->ctx.output.size; + self->ctx.input.src = self->bufin; + self->ctx.input.pos = 0; + self->ctx.input.size = 0; + self->ctx.output.size = 0; + + if (dict) { + zend_string_release(dict); + } return php_stream_alloc(&php_stream_zstd_read_ops, self, NULL, mode); } @@ -1229,18 +1201,17 @@ static int php_zstd_output_encoding(void) return PHP_ZSTD_G(compression_coding); } -static void -php_zstd_output_handler_load_dict(php_zstd_context *ctx, int level) +static zend_string* +php_zstd_output_handler_load_dict(php_zstd_context *ctx) { php_stream *stream = NULL; zval *zcontext = NULL; php_stream_context *context = NULL; - zend_string *contents = NULL; zend_long maxlen = (ssize_t) PHP_STREAM_COPY_ALL; char *dict = PHP_ZSTD_G(output_compression_dict); if (!dict || strlen(dict) <= 0) { - return; + return NULL; } context = php_stream_context_from_zval(zcontext, 0); @@ -1249,7 +1220,7 @@ php_zstd_output_handler_load_dict(php_zstd_context *ctx, int level) NULL, context); if (!stream) { ZSTD_WARNING("could not open dictionary stream: %s", dict); - return; + return NULL; } if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) { @@ -1257,21 +1228,11 @@ php_zstd_output_handler_load_dict(php_zstd_context *ctx, int level) PHP_STREAM_BUFFER_NONE, NULL); } - contents = php_stream_copy_to_mem(stream, maxlen, 0); - - if (contents) { - ctx->cdict = ZSTD_createCDict(ZSTR_VAL(contents), ZSTR_LEN(contents), - level); - if (!ctx->cdict) { - ZSTD_WARNING("failed to create compression dictionary: %s", dict); - } - - zend_string(contents); - } else { - ZSTD_WARNING("failed to get dictionary stream: %s", dict); - } + zend_string *data = php_stream_copy_to_mem(stream, maxlen, 0); php_stream_close(stream); + + return data; } static zend_result php_zstd_output_handler_context_start(php_zstd_context *ctx) @@ -1282,21 +1243,12 @@ static zend_result php_zstd_output_handler_context_start(php_zstd_context *ctx) level = ZSTD_CLEVEL_DEFAULT; } - ctx->cctx = ZSTD_createCCtx(); - if (!ctx->cctx) { + zend_string *dict = php_zstd_output_handler_load_dict(ctx); + + if (php_zstd_context_create_compress(ctx, level, dict) != SUCCESS) { return FAILURE; } - php_zstd_output_handler_load_dict(ctx, level); - - ZSTD_CCtx_reset(ctx->cctx, ZSTD_reset_session_only); - ZSTD_CCtx_refCDict(ctx->cctx, ctx->cdict); - 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 SUCCESS; }