1 /*
2 Copyright (c) 2015 kjdev
3
4 Permission is hereby granted, free of charge, to any person obtaining
5 a copy of this software and associated documentation files (the
6 'Software'), to deal in the Software without restriction, including
7 without limitation the rights to use, copy, modify, merge, publish,
8 distribute, sublicense, and/or sell copies of the Software, and to
9 permit persons to whom the Software is furnished to do so, subject to
10 the following conditions:
11
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <php.h>
29 #include <php_ini.h>
30 #include <ext/standard/info.h>
31 #if ZEND_MODULE_API_NO >= 20141001
32 #include <ext/standard/php_smart_string.h>
33 #else
34 #include <ext/standard/php_smart_str.h>
35 #endif
36 #include "php_zstd.h"
37
38 /* zstd */
39 #ifdef HAVE_SYS_TYPES_H
40 #include <sys/types.h>
41 #endif
42 #ifdef HAVE_STDINT_H
43 #include <stdint.h>
44 #endif
45 #include "zstd.h"
46
47 #ifndef ZSTD_CLEVEL_DEFAULT
48 #define ZSTD_CLEVEL_DEFAULT 3
49 #endif
50
51 #define FRAME_HEADER_SIZE 5
52 #define BLOCK_HEADER_SIZE 3
53 #define MAX_HEADER_SIZE FRAME_HEADER_SIZE+3
54
55 #define DEFAULT_COMPRESS_LEVEL 3
56
57 ZEND_BEGIN_ARG_INFO_EX(arginfo_zstd_compress, 0, 0, 1)
58 ZEND_ARG_INFO(0, data)
59 ZEND_ARG_INFO(0, level)
ZEND_END_ARG_INFO()60 ZEND_END_ARG_INFO()
61
62 ZEND_BEGIN_ARG_INFO_EX(arginfo_zstd_uncompress, 0, 0, 1)
63 ZEND_ARG_INFO(0, data)
64 ZEND_END_ARG_INFO()
65
66 ZEND_BEGIN_ARG_INFO_EX(arginfo_zstd_compress_dict, 0, 0, 1)
67 ZEND_ARG_INFO(0, data)
68 ZEND_ARG_INFO(0, dictBuffer)
69 ZEND_END_ARG_INFO()
70
71 ZEND_BEGIN_ARG_INFO_EX(arginfo_zstd_uncompress_dict, 0, 0, 1)
72 ZEND_ARG_INFO(0, data)
73 ZEND_ARG_INFO(0, dictBuffer)
74 ZEND_END_ARG_INFO()
75
76 ZEND_FUNCTION(zstd_compress)
77 {
78 zval *data;
79 char *output;
80 size_t size, result;
81 long level = DEFAULT_COMPRESS_LEVEL;
82 uint16_t maxLevel = (uint16_t)ZSTD_maxCLevel();
83
84 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
85 "z|l", &data, &level) == FAILURE) {
86 RETURN_FALSE;
87 }
88
89 if (Z_TYPE_P(data) != IS_STRING) {
90 zend_error(E_WARNING, "zstd_compress: expects parameter to be string.");
91 RETURN_FALSE;
92 }
93
94
95 #if ZSTD_VERSION_NUMBER >= 10304
96 if (level > maxLevel) {
97 zend_error(E_WARNING, "zstd_compress: compression level (%ld)"
98 " must be within 1..%d or smaller then 0", level, maxLevel);
99 #else
100 if (level > maxLevel || level < 0) {
101 zend_error(E_WARNING, "zstd_compress: compression level (%ld)"
102 " must be within 1..%d", level, maxLevel);
103 #endif
104 RETURN_FALSE;
105 } else if (level == 0) {
106 #if ZEND_MODULE_API_NO >= 20141001
107 RETURN_STRINGL(Z_STRVAL_P(data), Z_STRLEN_P(data));
108 #else
109 RETURN_STRINGL(Z_STRVAL_P(data), Z_STRLEN_P(data), 1);
110 #endif
111 }
112
113 size = ZSTD_compressBound(Z_STRLEN_P(data));
114 output = (char *)emalloc(size + 1);
115 if (!output) {
116 zend_error(E_WARNING, "zstd_compress: memory error");
117 RETURN_FALSE;
118 }
119
120 result = ZSTD_compress(output, size, Z_STRVAL_P(data), Z_STRLEN_P(data),
121 level);
122
123 if (ZSTD_isError(result)) {
124 RETVAL_FALSE;
125 } else if (result <= 0) {
126 RETVAL_FALSE;
127 } else {
128 #if ZEND_MODULE_API_NO >= 20141001
129 RETVAL_STRINGL(output, result);
130 #else
131 RETVAL_STRINGL(output, result, 1);
132 #endif
133 }
134
135 efree(output);
136 }
137
138 ZEND_FUNCTION(zstd_uncompress)
139 {
140 zval *data;
141 uint64_t size;
142 size_t result;
143 void *output;
144 uint8_t streaming = 0;
145
146 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
147 "z", &data) == FAILURE) {
148 RETURN_FALSE;
149 }
150
151 if (Z_TYPE_P(data) != IS_STRING) {
152 zend_error(E_WARNING,
153 "zstd_uncompress: expects parameter to be string.");
154 RETURN_FALSE;
155 }
156
157 size = ZSTD_getFrameContentSize(Z_STRVAL_P(data), Z_STRLEN_P(data));
158 if (size == ZSTD_CONTENTSIZE_ERROR) {
159 zend_error(E_WARNING, "zstd_uncompress: it was not compressed by zstd");
160 RETURN_FALSE;
161 } else if (size == ZSTD_CONTENTSIZE_UNKNOWN) {
162 streaming = 1;
163 size = ZSTD_DStreamOutSize();
164 }
165
166 output = emalloc(size);
167 if (!output) {
168 zend_error(E_WARNING, "zstd_uncompress: memory error");
169 RETURN_FALSE;
170 }
171
172 if (!streaming) {
173 result = ZSTD_decompress(output, size,
174 Z_STRVAL_P(data), Z_STRLEN_P(data));
175 } else {
176 ZSTD_DStream *stream;
177 ZSTD_inBuffer in = { NULL, 0, 0 };
178 ZSTD_outBuffer out = { NULL, 0, 0 };
179
180 stream = ZSTD_createDStream();
181 if (stream == NULL) {
182 efree(output);
183 zend_error(E_WARNING, "zstd_uncompress: can not create stream");
184 RETURN_FALSE;
185 }
186
187 result = ZSTD_initDStream(stream);
188 if (ZSTD_isError(result)) {
189 efree(output);
190 ZSTD_freeDStream(stream);
191 zend_error(E_WARNING, "zstd_uncompress: can not init stream");
192 RETURN_FALSE;
193 }
194
195 in.src = Z_STRVAL_P(data);
196 in.size = Z_STRLEN_P(data);
197 in.pos = 0;
198
199 out.dst = output;
200 out.size = size;
201 out.pos = 0;
202
203 while (in.pos < in.size) {
204 if (out.pos == out.size) {
205 out.size += size;
206 output = erealloc(output, out.size);
207 out.dst = output;
208 }
209
210 result = ZSTD_decompressStream(stream, &out, &in);
211 if (ZSTD_isError(result)) {
212 efree(output);
213 ZSTD_freeDStream(stream);
214 zend_error(E_WARNING,
215 "zstd_uncompress: can not decompress stream");
216 RETURN_FALSE;
217 }
218
219 if (result == 0) {
220 break;
221 }
222 }
223
224 result = out.pos;
225
226 ZSTD_freeDStream(stream);
227 }
228
229 if (ZSTD_isError(result)) {
230 RETVAL_FALSE;
231 } else if (result < 0) {
232 RETVAL_FALSE;
233 } else {
234 #if ZEND_MODULE_API_NO >= 20141001
235 RETVAL_STRINGL(output, result);
236 #else
237 RETVAL_STRINGL(output, result, 1);
238 #endif
239 }
240
241 efree(output);
242 }
243
244 ZEND_FUNCTION(zstd_compress_dict)
245 {
246 zval *data, *dictBuffer;
247
248 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
249 "z|z", &data, &dictBuffer) == FAILURE) {
250 RETURN_FALSE;
251 }
252 if (Z_TYPE_P(data) != IS_STRING) {
253 zend_error(E_WARNING, "zstd_compress_dict:"
254 " expects the first parameter to be string.");
255 RETURN_FALSE;
256 }
257 if (Z_TYPE_P(dictBuffer) != IS_STRING) {
258 zend_error(E_WARNING, "zstd_compress_dict:"
259 " expects the second parameter to be string.");
260 RETURN_FALSE;
261 }
262
263 size_t const cBuffSize = ZSTD_compressBound(Z_STRLEN_P(data));
264 void* const cBuff = emalloc(cBuffSize);
265 if (!cBuff) {
266 zend_error(E_WARNING, "zstd_compress_dict: memory error");
267 RETURN_FALSE;
268 }
269 ZSTD_CCtx* const cctx = ZSTD_createCCtx();
270 if (cctx == NULL) {
271 efree(cBuff);
272 zend_error(E_WARNING, "ZSTD_createCCtx() error");
273 RETURN_FALSE;
274 }
275 ZSTD_CDict* const cdict = ZSTD_createCDict(Z_STRVAL_P(dictBuffer),
276 Z_STRLEN_P(dictBuffer),
277 DEFAULT_COMPRESS_LEVEL);
278 if (!cdict) {
279 efree(cBuff);
280 zend_error(E_WARNING, "ZSTD_createCDict() error");
281 RETURN_FALSE;
282 }
283 size_t const cSize = ZSTD_compress_usingCDict(cctx, cBuff, cBuffSize,
284 Z_STRVAL_P(data),
285 Z_STRLEN_P(data),
286 cdict);
287 if (ZSTD_isError(cSize)) {
288 efree(cBuff);
289 zend_error(E_WARNING, "zstd_compress_dict: %s",
290 ZSTD_getErrorName(cSize));
291 RETURN_FALSE;
292 }
293 ZSTD_freeCCtx(cctx);
294 ZSTD_freeCDict(cdict);
295
296 #if ZEND_MODULE_API_NO >= 20141001
297 RETVAL_STRINGL(cBuff, cSize);
298 #else
299 RETVAL_STRINGL(cBuff, cSize, 1);
300 #endif
301
302 efree(cBuff);
303 }
304
305 ZEND_FUNCTION(zstd_uncompress_dict)
306 {
307 zval *data, *dictBuffer;
308
309 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
310 "z|z", &data, &dictBuffer) == FAILURE) {
311 RETURN_FALSE;
312 }
313 if (Z_TYPE_P(data) != IS_STRING) {
314 zend_error(E_WARNING, "zstd_uncompress_dict:"
315 " expects the first parameter to be string.");
316 RETURN_FALSE;
317 }
318 if (Z_TYPE_P(dictBuffer) != IS_STRING) {
319 zend_error(E_WARNING, "zstd_uncompress_dict:"
320 " expects the second parameter to be string.");
321 RETURN_FALSE;
322 }
323
324 unsigned long long const rSize = ZSTD_getDecompressedSize(Z_STRVAL_P(data),
325 Z_STRLEN_P(data));
326 if (rSize == 0) {
327 RETURN_FALSE;
328 }
329 void* const rBuff = emalloc((size_t)rSize);
330 if (!rBuff) {
331 zend_error(E_WARNING, "zstd_uncompress_dict: memory error");
332 RETURN_FALSE;
333 }
334
335 ZSTD_DCtx* const dctx = ZSTD_createDCtx();
336 if (dctx == NULL) {
337 efree(rBuff);
338 zend_error(E_WARNING, "ZSTD_createDCtx() error");
339 RETURN_FALSE;
340 }
341 ZSTD_DDict* const ddict = ZSTD_createDDict(Z_STRVAL_P(dictBuffer),
342 Z_STRLEN_P(dictBuffer));
343 if (!ddict) {
344 efree(rBuff);
345 zend_error(E_WARNING, "ZSTD_createDDict() error");
346 RETURN_FALSE;
347 }
348 size_t const dSize = ZSTD_decompress_usingDDict(dctx, rBuff, rSize,
349 Z_STRVAL_P(data),
350 Z_STRLEN_P(data),
351 ddict);
352 if (dSize != rSize) {
353 efree(rBuff);
354 zend_error(E_WARNING, "zstd_uncompress_dict: %s",
355 ZSTD_getErrorName(dSize));
356 RETURN_FALSE;
357 }
358 ZSTD_freeDCtx(dctx);
359 ZSTD_freeDDict(ddict);
360
361 #if ZEND_MODULE_API_NO >= 20141001
362 RETVAL_STRINGL(rBuff, rSize);
363 #else
364 RETVAL_STRINGL(rBuff, rSize, 1);
365 #endif
366
367 efree(rBuff);
368 }
369
370
371 typedef struct _php_zstd_stream_data {
372 char *bufin, *bufout;
373 size_t sizein, sizeout;
374 ZSTD_CCtx* cctx;
375 ZSTD_DCtx* dctx;
376 ZSTD_inBuffer input;
377 ZSTD_outBuffer output;
378 php_stream *stream;
379 } php_zstd_stream_data;
380
381
382 #define STREAM_DATA_FROM_STREAM() \
383 php_zstd_stream_data *self = (php_zstd_stream_data *) stream->abstract
384
385 #define STREAM_NAME "compress.zstd"
386
387 static int php_zstd_decomp_close(php_stream *stream, int close_handle TSRMLS_DC)
388 {
389 STREAM_DATA_FROM_STREAM();
390
391 if (!self) {
392 return EOF;
393 }
394
395 if (close_handle) {
396 if (self->stream) {
397 php_stream_close(self->stream);
398 self->stream = NULL;
399 }
400 }
401
402 ZSTD_freeDCtx(self->dctx);
403 efree(self->bufin);
404 efree(self->bufout);
405 efree(self);
406 stream->abstract = NULL;
407
408 return EOF;
409 }
410
411 static int php_zstd_comp_flush_or_end(php_zstd_stream_data *self, int end TSRMLS_DC)
412 {
413 size_t res;
414 int ret = 0;
415
416 #if ZSTD_VERSION_NUMBER < 10400
417 /* Compress remaining data */
418 if (self->input.size) {
419 self->input.pos = 0;
420 do {
421 self->output.size = self->sizeout;
422 self->output.pos = 0;
423 res = ZSTD_compressStream(self->cctx, &self->output, &self->input);
424 if (ZSTD_isError(res)) {
425 php_error_docref(NULL TSRMLS_CC, E_WARNING, "libzstd error %s\n", ZSTD_getErrorName(res));
426 ret = EOF;
427 }
428 php_stream_write(self->stream, self->bufout, self->output.pos);
429 } while (self->input.pos != self->input.size);
430 }
431 #endif
432
433 /* Flush / End */
434 do {
435 self->output.size = self->sizeout;
436 self->output.pos = 0;
437 #if ZSTD_VERSION_NUMBER >= 10400
438 res = ZSTD_compressStream2(self->cctx, &self->output, &self->input, end ? ZSTD_e_end : ZSTD_e_flush);
439 #else
440 if (end) {
441 res = ZSTD_endStream(self->cctx, &self->output);
442 } else {
443 res = ZSTD_flushStream(self->cctx, &self->output);
444 }
445 #endif
446 if (ZSTD_isError(res)) {
447 php_error_docref(NULL TSRMLS_CC, E_WARNING, "libzstd error %s\n", ZSTD_getErrorName(res));
448 ret = EOF;
449 }
450 php_stream_write(self->stream, self->bufout, self->output.pos);
451 } while (res > 0);
452
453 self->input.pos = 0;
454 self->input.size = 0;
455
456 return ret;
457 }
458
459
460 static int php_zstd_comp_flush(php_stream *stream TSRMLS_DC)
461 {
462 STREAM_DATA_FROM_STREAM();
463
464 return php_zstd_comp_flush_or_end(self, 0 TSRMLS_CC);
465 }
466
467
468 static int php_zstd_comp_close(php_stream *stream, int close_handle TSRMLS_DC)
469 {
470 STREAM_DATA_FROM_STREAM();
471
472 if (!self) {
473 return EOF;
474 }
475
476 php_zstd_comp_flush_or_end(self, 1 TSRMLS_CC);
477
478 if (close_handle) {
479 if (self->stream) {
480 php_stream_close(self->stream);
481 self->stream = NULL;
482 }
483 }
484
485 ZSTD_freeCCtx(self->cctx);
486 efree(self->bufin);
487 efree(self->bufout);
488 efree(self);
489 stream->abstract = NULL;
490
491 return EOF;
492 }
493
494
495 #if PHP_VERSION_ID < 70400
496 static size_t php_zstd_decomp_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
497 {
498 size_t ret = 0;
499 #else
500 static ssize_t php_zstd_decomp_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
501 {
502 ssize_t ret = 0;
503 #endif
504 size_t x, res;
505 STREAM_DATA_FROM_STREAM();
506
507 while (count > 0) {
508 x = self->output.size - self->output.pos;
509 /* enough available */
510 if (x >= count) {
511 memcpy(buf, self->bufout + self->output.pos, count);
512 self->output.pos += count;
513 ret += count;
514 return ret;
515 }
516 /* take remaining from out */
517 if (x) {
518 memcpy(buf, self->bufout + self->output.pos, x);
519 self->output.pos += x;
520 ret += x;
521 buf += x;
522 count -= x;
523 }
524 /* decompress */
525 if (self->input.pos < self->input.size) {
526 /* for zstd */
527 self->output.pos = 0;
528 self->output.size = self->sizeout;
529 res = ZSTD_decompressStream(self->dctx, &self->output , &self->input);
530 if (ZSTD_isError(res)) {
531 php_error_docref(NULL TSRMLS_CC, E_WARNING, "libzstd error %s\n", ZSTD_getErrorName(res));
532 #if PHP_VERSION_ID >= 70400
533 return -1;
534 #endif
535 }
536 /* for us */
537 self->output.size = self->output.pos;
538 self->output.pos = 0;
539 } else {
540 /* read */
541 self->input.pos = 0;
542 self->input.size = php_stream_read(self->stream, self->bufin, self->sizein);
543 if (!self->input.size) {
544 /* EOF */
545 count = 0;
546 }
547 }
548 }
549 return ret;
550 }
551
552
553 #if PHP_VERSION_ID < 70400
554 static size_t php_zstd_comp_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
555 {
556 size_t ret = 0;
557 #else
558 static ssize_t php_zstd_comp_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
559 {
560 ssize_t ret = 0;
561 #endif
562 size_t x, res;
563
564 STREAM_DATA_FROM_STREAM();
565
566 while(count > 0) {
567 /* enough room for full data */
568 if (self->input.size + count < self->sizein) {
569 memcpy(self->bufin + self->input.size, buf, count);
570 self->input.size += count;
571 ret += count;
572 count = 0;
573 break;
574 }
575
576 /* fill input buffer */
577 x = self->sizein - self->input.size;
578 memcpy(self->bufin + self->input.size, buf, x);
579 self->input.size += x;
580 buf += x;
581 count -= x;
582 ret += x;
583
584 /* compress and write */
585 self->input.pos = 0;
586 do {
587 self->output.size = self->sizeout;
588 self->output.pos = 0;
589 #if ZSTD_VERSION_NUMBER >= 10400
590 res = ZSTD_compressStream2(self->cctx, &self->output, &self->input, ZSTD_e_continue);
591 #else
592 res = ZSTD_compressStream(self->cctx, &self->output, &self->input);
593 #endif
594 if (ZSTD_isError(res)) {
595 php_error_docref(NULL TSRMLS_CC, E_WARNING, "libzstd error %s\n", ZSTD_getErrorName(res));
596 #if PHP_VERSION_ID >= 70400
597 return -1;
598 #endif
599 }
600 php_stream_write(self->stream, self->bufout, self->output.pos);
601 } while (self->input.pos != self->input.size);
602
603 self->input.pos = 0;
604 self->input.size = 0;
605 }
606 return ret;
607 }
608
609
610 static php_stream_ops php_stream_zstd_read_ops = {
611 NULL, /* write */
612 php_zstd_decomp_read,
613 php_zstd_decomp_close,
614 NULL, /* flush */
615 STREAM_NAME,
616 NULL, /* seek */
617 NULL, /* cast */
618 NULL, /* stat */
619 NULL /* set_option */
620 };
621
622
623 static php_stream_ops php_stream_zstd_write_ops = {
624 php_zstd_comp_write,
625 NULL, /* read */
626 php_zstd_comp_close,
627 php_zstd_comp_flush,
628 STREAM_NAME,
629 NULL, /* seek */
630 NULL, /* cast */
631 NULL, /* stat */
632 NULL /* set_option */
633 };
634
635
636 static php_stream *
637 php_stream_zstd_opener(
638 php_stream_wrapper *wrapper,
639 #if PHP_VERSION_ID < 50600
640 char *path,
641 char *mode,
642 #else
643 const char *path,
644 const char *mode,
645 #endif
646 int options,
647 #if PHP_MAJOR_VERSION < 7
648 char **opened_path,
649 #else
650 zend_string **opened_path,
651 #endif
652 php_stream_context *context
653 STREAMS_DC TSRMLS_DC)
654 {
655 php_zstd_stream_data *self;
656 int level = ZSTD_CLEVEL_DEFAULT;
657 int compress;
658 #if ZSTD_VERSION_NUMBER >= 10400
659 ZSTD_CDict *cdict = NULL;
660 ZSTD_DDict *ddict = NULL;
661 #endif
662
663 if (strncasecmp(STREAM_NAME, path, sizeof(STREAM_NAME)-1) == 0) {
664 path += sizeof(STREAM_NAME)-1;
665 if (strncmp("://", path, 3) == 0) {
666 path += 3;
667 }
668 }
669
670 if (php_check_open_basedir(path TSRMLS_CC)) {
671 return NULL;
672 }
673
674 if (!strcmp(mode, "w") || !strcmp(mode, "wb")) {
675 compress = 1;
676 } else if (!strcmp(mode, "r") || !strcmp(mode, "rb")) {
677 compress = 0;
678 } else {
679 php_error_docref(NULL TSRMLS_CC, E_ERROR, "zstd: invalid open mode");
680 return NULL;
681 }
682
683 if (context) {
684 #if PHP_MAJOR_VERSION >= 7
685 zval *tmpzval;
686 zend_string *data;
687
688 if (NULL != (tmpzval = php_stream_context_get_option(context, "zstd", "level"))) {
689 level = zval_get_long(tmpzval);
690 }
691 #if ZSTD_VERSION_NUMBER >= 10400
692 if (NULL != (tmpzval = php_stream_context_get_option(context, "zstd", "dict"))) {
693 data = zval_get_string(tmpzval);
694 if (compress) {
695 cdict = ZSTD_createCDict(ZSTR_VAL(data), ZSTR_LEN(data), level);
696 } else {
697 ddict = ZSTD_createDDict(ZSTR_VAL(data), ZSTR_LEN(data));
698 }
699 zend_string_release(data);
700 }
701 #endif
702 #else
703 zval **tmpzval;
704
705 if (php_stream_context_get_option(context, "zstd", "level", &tmpzval) == SUCCESS) {
706 convert_to_long_ex(tmpzval);
707 level = Z_LVAL_PP(tmpzval);
708 }
709 #if ZSTD_VERSION_NUMBER >= 10400
710 if (php_stream_context_get_option(context, "zstd", "dict", &tmpzval) == SUCCESS) {
711 convert_to_string(*tmpzval);
712 if (compress) {
713 cdict = ZSTD_createCDict(Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval), level);
714 } else {
715 ddict = ZSTD_createDDict(Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval));
716 }
717 }
718 #endif
719 #endif
720 }
721
722 if (level > ZSTD_maxCLevel()) {
723 php_error_docref(NULL TSRMLS_CC, E_WARNING, "zstd: compression level (%d) must be less than %d", level, ZSTD_maxCLevel());
724 level = ZSTD_maxCLevel();
725 }
726
727 self = ecalloc(sizeof(*self), 1);
728 self->stream = php_stream_open_wrapper(path, mode, options | REPORT_ERRORS, NULL);
729 if (!self->stream) {
730 efree(self);
731 return NULL;
732 }
733
734 /* File */
735 if (compress) {
736 self->dctx = NULL;
737 self->cctx = ZSTD_createCCtx();
738 if (!self->cctx) {
739 php_error_docref(NULL TSRMLS_CC, E_WARNING, "zstd: compression context failed");
740 php_stream_close(self->stream);
741 efree(self);
742 return NULL;
743 }
744 #if ZSTD_VERSION_NUMBER >= 10400
745 ZSTD_CCtx_reset(self->cctx, ZSTD_reset_session_only);
746 ZSTD_CCtx_refCDict(self->cctx, cdict);
747 ZSTD_CCtx_setParameter(self->cctx, ZSTD_c_compressionLevel, level);
748 #else
749 ZSTD_initCStream(self->cctx, level);
750 #endif
751 self->bufin = emalloc(self->sizein = ZSTD_CStreamInSize());
752 self->bufout = emalloc(self->sizeout = ZSTD_CStreamOutSize());
753 self->input.src = self->bufin;
754 self->input.pos = 0;
755 self->input.size = 0;
756 self->output.dst = self->bufout;
757 self->output.pos = 0;
758 self->output.size = 0;
759
760 return php_stream_alloc(&php_stream_zstd_write_ops, self, NULL, mode);
761
762 } else {
763 self->dctx = ZSTD_createDCtx();
764 if (!self->dctx) {
765 php_error_docref(NULL TSRMLS_CC, E_WARNING, "zstd: compression context failed");
766 php_stream_close(self->stream);
767 efree(self);
768 return NULL;
769 }
770 self->cctx = NULL;
771 self->bufin = emalloc(self->sizein = ZSTD_DStreamInSize());
772 self->bufout = emalloc(self->sizeout = ZSTD_DStreamOutSize());
773 #if ZSTD_VERSION_NUMBER >= 10400
774 ZSTD_DCtx_reset(self->dctx, ZSTD_reset_session_only);
775 ZSTD_DCtx_refDDict(self->dctx, ddict);
776 #else
777 ZSTD_initDStream(self->dctx);
778 #endif
779 self->input.src = self->bufin;
780 self->input.pos = 0;
781 self->input.size = 0;
782 self->output.dst = self->bufout;
783 self->output.pos = 0;
784 self->output.size = 0;
785
786 return php_stream_alloc(&php_stream_zstd_read_ops, self, NULL, mode);
787 }
788 return NULL;
789 }
790
791
792 static php_stream_wrapper_ops zstd_stream_wops = {
793 php_stream_zstd_opener,
794 NULL, /* close */
795 NULL, /* fstat */
796 NULL, /* stat */
797 NULL, /* opendir */
798 STREAM_NAME,
799 NULL, /* unlink */
800 NULL, /* rename */
801 NULL, /* mkdir */
802 NULL /* rmdir */
803 #if PHP_VERSION_ID >= 50400
804 , NULL
805 #endif
806 };
807
808
809 php_stream_wrapper php_stream_zstd_wrapper = {
810 &zstd_stream_wops,
811 NULL,
812 0 /* is_url */
813 };
814
815
816 ZEND_MINIT_FUNCTION(zstd)
817 {
818 REGISTER_LONG_CONSTANT("ZSTD_COMPRESS_LEVEL_MIN",
819 1,
820 CONST_CS | CONST_PERSISTENT);
821 REGISTER_LONG_CONSTANT("ZSTD_COMPRESS_LEVEL_MAX",
822 ZSTD_maxCLevel(),
823 CONST_CS | CONST_PERSISTENT);
824 REGISTER_LONG_CONSTANT("ZSTD_COMPRESS_LEVEL_DEFAULT",
825 DEFAULT_COMPRESS_LEVEL,
826 CONST_CS | CONST_PERSISTENT);
827
828 REGISTER_LONG_CONSTANT("LIBZSTD_VERSION_NUMBER",
829 ZSTD_VERSION_NUMBER,
830 CONST_CS | CONST_PERSISTENT);
831 REGISTER_STRING_CONSTANT("LIBZSTD_VERSION_STRING",
832 ZSTD_VERSION_STRING,
833 CONST_CS | CONST_PERSISTENT);
834
835 php_register_url_stream_wrapper(STREAM_NAME, &php_stream_zstd_wrapper TSRMLS_CC);
836
837 return SUCCESS;
838 }
839
840 ZEND_MINFO_FUNCTION(zstd)
841 {
842 php_info_print_table_start();
843 php_info_print_table_row(2, "Zstd support", "enabled");
844 php_info_print_table_row(2, "Extension Version", PHP_ZSTD_EXT_VERSION);
845 php_info_print_table_row(2, "Interface Version", ZSTD_VERSION_STRING);
846 php_info_print_table_end();
847 }
848
849 static zend_function_entry zstd_functions[] = {
850 ZEND_FE(zstd_compress, arginfo_zstd_compress)
851 ZEND_FE(zstd_uncompress, arginfo_zstd_uncompress)
852 ZEND_FALIAS(zstd_decompress, zstd_uncompress, arginfo_zstd_uncompress)
853
854 ZEND_FE(zstd_compress_dict, arginfo_zstd_compress_dict)
855 ZEND_FE(zstd_uncompress_dict, arginfo_zstd_uncompress_dict)
856 ZEND_FALIAS(zstd_compress_usingcdict,
857 zstd_compress_dict, arginfo_zstd_compress_dict)
858 ZEND_FALIAS(zstd_decompress_dict,
859 zstd_uncompress_dict, arginfo_zstd_uncompress_dict)
860 ZEND_FALIAS(zstd_uncompress_usingcdict,
861 zstd_uncompress_dict, arginfo_zstd_uncompress_dict)
862 ZEND_FALIAS(zstd_decompress_usingcdict,
863 zstd_uncompress_dict, arginfo_zstd_uncompress_dict)
864
865 // PHP 5.3+
866 #if ZEND_MODULE_API_NO >= 20090626
867 ZEND_NS_FALIAS(PHP_ZSTD_NS, compress,
868 zstd_compress, arginfo_zstd_compress)
869 ZEND_NS_FALIAS(PHP_ZSTD_NS, uncompress,
870 zstd_uncompress, arginfo_zstd_uncompress)
871 ZEND_NS_FALIAS(PHP_ZSTD_NS, decompress,
872 zstd_uncompress, arginfo_zstd_uncompress)
873 ZEND_NS_FALIAS(PHP_ZSTD_NS, compress_dict,
874 zstd_compress_dict, arginfo_zstd_compress_dict)
875 ZEND_NS_FALIAS(PHP_ZSTD_NS, compress_usingcdict,
876 zstd_compress_dict, arginfo_zstd_compress_dict)
877 ZEND_NS_FALIAS(PHP_ZSTD_NS, uncompress_dict,
878 zstd_uncompress_dict, arginfo_zstd_uncompress_dict)
879 ZEND_NS_FALIAS(PHP_ZSTD_NS, decompress_dict,
880 zstd_uncompress_dict, arginfo_zstd_uncompress_dict)
881 ZEND_NS_FALIAS(PHP_ZSTD_NS, uncompress_usingcdict,
882 zstd_uncompress_dict, arginfo_zstd_uncompress_dict)
883 ZEND_NS_FALIAS(PHP_ZSTD_NS, decompress_usingcdict,
884 zstd_uncompress_dict, arginfo_zstd_uncompress_dict)
885 #endif
886 {NULL, NULL, NULL}
887 };
888
889 zend_module_entry zstd_module_entry = {
890 #if ZEND_MODULE_API_NO >= 20010901
891 STANDARD_MODULE_HEADER,
892 #endif
893 "zstd",
894 zstd_functions,
895 ZEND_MINIT(zstd),
896 NULL,
897 NULL,
898 NULL,
899 ZEND_MINFO(zstd),
900 #if ZEND_MODULE_API_NO >= 20010901
901 PHP_ZSTD_EXT_VERSION,
902 #endif
903 STANDARD_MODULE_PROPERTIES
904 };
905
906 #ifdef COMPILE_DL_ZSTD
907 ZEND_GET_MODULE(zstd)
908 #endif
909