1 #ifdef __cplusplus
2 extern "C" {
3 #endif
4 #define PERL_NO_GET_CONTEXT
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8 #ifdef __cplusplus
9 }
10 #endif
11
12 #define NEED_sv_2pvbyte
13 #define NEED_newCONSTSUB
14 #include "ppport.h"
15 #include "zstd.h"
16 #include "compress/zstdmt_compress.h"
17
18 typedef struct Compress__Zstd__Compressor_s {
19 ZSTD_CStream* stream;
20 char* buf;
21 size_t bufsize;
22 }* Compress__Zstd__Compressor;
23
24 typedef struct Compress__Zstd__Decompressor_s {
25 ZSTD_DStream* stream;
26 char* buf;
27 size_t bufsize;
28 }* Compress__Zstd__Decompressor;
29
30 typedef ZSTD_CCtx* Compress__Zstd__CompressionContext;
31
32 typedef ZSTD_DCtx* Compress__Zstd__DecompressionContext;
33
34 typedef ZSTD_CDict* Compress__Zstd__CompressionDictionary;
35
36 typedef ZSTD_DDict* Compress__Zstd__DecompressionDictionary;
37
38 static SV*
decompress_using_streaming(pTHX_ const char * src,size_t srcSize)39 decompress_using_streaming(pTHX_ const char* src, size_t srcSize)
40 {
41 char* buf;
42 size_t bufsize;
43 SV* output;
44 ZSTD_inBuffer inbuf = { src, srcSize, 0 };
45 int iserror = 0;
46
47 ZSTD_DStream* stream = ZSTD_createDStream();
48 if (stream == NULL) {
49 croak("Failed to call ZSTD_createDStream()");
50 }
51 ZSTD_initDStream(stream);
52
53 bufsize = ZSTD_DStreamOutSize();
54 Newx(buf, bufsize, char);
55
56 output = newSVpv("", 0);
57 while (inbuf.pos < inbuf.size) {
58 ZSTD_outBuffer outbuf = { buf, bufsize, 0 };
59 size_t ret = ZSTD_decompressStream(stream, &outbuf, &inbuf);
60 if (ZSTD_isError(ret)) {
61 iserror = 1;
62 break;
63 }
64 sv_catpvn(output, outbuf.dst, outbuf.pos);
65 }
66 Safefree(buf);
67 ZSTD_freeDStream(stream);
68 if (iserror != 0) {
69 SvREFCNT_dec(output);
70 return NULL;
71 }
72 return output;
73 }
74
75 MODULE = Compress::Zstd PACKAGE = Compress::Zstd
76
77 BOOT:
78 {
79 HV* stash = gv_stashpv("Compress::Zstd", 1);
80 newCONSTSUB(stash, "ZSTD_VERSION_NUMBER", newSViv(ZSTD_VERSION_NUMBER));
81 newCONSTSUB(stash, "ZSTD_VERSION_STRING", newSVpvs(ZSTD_VERSION_STRING));
82 newCONSTSUB(stash, "ZSTD_MAX_CLEVEL", newSViv(ZSTD_maxCLevel()));
83 }
84
85 PROTOTYPES: DISABLE
86
87 void
88 compress(source, level = 1)
89 SV* source;
90 int level;
91 PREINIT:
92 const char* src;
93 STRLEN src_len;
94 SV* dest;
95 char* dst;
96 size_t bound, ret;
97 PPCODE:
98 if (SvROK(source)) {
99 source = SvRV(source);
100 }
101 if (!SvOK(source)) {
102 XSRETURN_UNDEF;
103 }
104 src = SvPVbyte(source, src_len);
105 bound = ZSTD_compressBound(src_len);
106 dest = sv_2mortal(newSV(bound + 1));
107 dst = SvPVX(dest);
108 ret = ZSTD_compress(dst, bound + 1, src, src_len, level);
109 if (ZSTD_isError(ret)) {
110 XSRETURN_UNDEF;
111 }
112 dst[ret] = '\0';
113 SvCUR_set(dest, ret);
114 SvPOK_on(dest);
115 EXTEND(SP, 1);
116 PUSHs(dest);
117
118 void
119 compress_mt(source, nbThreads, level = 1)
120 SV* source;
121 unsigned int nbThreads;
122 int level;
123 PREINIT:
124 const char* src;
125 STRLEN src_len;
126 SV* dest;
127 char* dst;
128 size_t bound, ret;
129 ZSTDMT_CCtx* cctx;
130 PPCODE:
131 if (SvROK(source)) {
132 source = SvRV(source);
133 }
134 if (!SvOK(source)) {
135 XSRETURN_UNDEF;
136 }
137 cctx = ZSTDMT_createCCtx(nbThreads);
138 src = SvPVbyte(source, src_len);
139 bound = ZSTD_compressBound(src_len);
140 dest = sv_2mortal(newSV(bound + 1));
141 dst = SvPVX(dest);
142 ret = ZSTDMT_compressCCtx(cctx, dst, bound + 1, src, src_len, level);
143 ZSTDMT_freeCCtx(cctx);
144 if (ZSTD_isError(ret)) {
145 XSRETURN_UNDEF;
146 }
147 dst[ret] = '\0';
148 SvCUR_set(dest, ret);
149 SvPOK_on(dest);
150 EXTEND(SP, 1);
151 PUSHs(dest);
152
153 void
154 decompress(source)
155 SV* source;
156 ALIAS:
157 uncompress = 1
158 PREINIT:
159 const char* src;
160 STRLEN src_len;
161 unsigned long long dest_len;
162 SV* dest;
163 char* dst;
164 size_t ret;
165 PPCODE:
166 if (SvROK(source)) {
167 source = SvRV(source);
168 }
169 if (!SvOK(source)) {
170 XSRETURN_UNDEF;
171 }
172 src = SvPVbyte(source, src_len);
173 dest_len = ZSTD_getFrameContentSize(src, src_len);
174 if (dest_len == ZSTD_CONTENTSIZE_UNKNOWN) {
175 SV* output = decompress_using_streaming(aTHX_ src, src_len);
176 if (output == NULL) {
177 XSRETURN_UNDEF;
178 }
179 EXTEND(SP, 1);
180 mPUSHs(output);
181 XSRETURN(1);
182 }
183 if (dest_len == ULLONG_MAX || ZSTD_isError(dest_len)) {
184 XSRETURN_UNDEF;
185 }
186 dest = sv_2mortal(newSV(dest_len + 1));
187 dst = SvPVX(dest);
188 ret = ZSTD_decompress(dst, dest_len + 1, src, src_len);
189 if (ZSTD_isError(ret)) {
190 XSRETURN_UNDEF;
191 }
192 dst[ret] = '\0';
193 SvCUR_set(dest, ret);
194 SvPOK_on(dest);
195 EXTEND(SP, 1);
196 PUSHs(dest);
197 XSRETURN(1);
198
199 MODULE = Compress::Zstd PACKAGE = Compress::Zstd::Compressor
200
201 PROTOTYPES: DISABLE
202
203 BOOT:
204 {
205 HV* stash = gv_stashpv("Compress::Zstd::Compressor", 1);
206 newCONSTSUB(stash, "ZSTD_CSTREAM_IN_SIZE", newSViv(ZSTD_CStreamInSize()));
207 }
208
209 Compress::Zstd::Compressor
210 new(klass, level = 1)
211 const char* klass;
212 int level;
213 PREINIT:
214 Compress__Zstd__Compressor self;
215 char* buf;
216 size_t bufsize;
217 CODE:
218 ZSTD_CStream* stream = ZSTD_createCStream();
219 if (stream == NULL) {
220 croak("Failed to call ZSTD_createCStream()");
221 }
222 ZSTD_initCStream(stream, level);
223
224 Newx(self, sizeof(struct Compress__Zstd__Compressor_s), struct Compress__Zstd__Compressor_s);
225 self->stream = stream;
226 bufsize = ZSTD_CStreamOutSize();
227 Newx(buf, bufsize, char);
228 self->buf = buf;
229 self->bufsize = bufsize;
230 RETVAL = self;
231 OUTPUT:
232 RETVAL
233
234 void
235 init(self, level = 1)
236 Compress::Zstd::Compressor self;
237 int level;
238 CODE:
239 ZSTD_initCStream(self->stream, level);
240
241 SV*
242 compress(self, input)
243 Compress::Zstd::Compressor self;
244 SV* input;
245 PREINIT:
246 STRLEN len;
247 SV* output;
248 CODE:
249 const char* in = SvPVbyte(input, len);
250 ZSTD_inBuffer inbuf = { in, len, 0 };
251 output = newSVpv("", 0);
252 while (inbuf.pos < inbuf.size) {
253 ZSTD_outBuffer outbuf = { self->buf, self->bufsize, 0 };
254 size_t toread = ZSTD_compressStream(self->stream, &outbuf, &inbuf);
255 if (ZSTD_isError(toread)) {
256 croak("%s", ZSTD_getErrorName(toread));
257 }
258 sv_catpvn(output, outbuf.dst, outbuf.pos);
259 }
260 RETVAL = output;
261 OUTPUT:
262 RETVAL
263
264 SV*
265 flush(self)
266 Compress::Zstd::Compressor self;
267 PREINIT:
268 SV* output;
269 size_t ret;
270 CODE:
271 output = newSVpv("", 0);
272 do {
273 ZSTD_outBuffer outbuf = { self->buf, self->bufsize, 0 };
274 ret = ZSTD_flushStream(self->stream, &outbuf);
275 if (ZSTD_isError(ret)) {
276 croak("%s", ZSTD_getErrorName(ret));
277 }
278 sv_catpvn(output, outbuf.dst, outbuf.pos);
279 } while (ret > 0);
280 RETVAL = output;
281 OUTPUT:
282 RETVAL
283
284 SV*
285 end(self)
286 Compress::Zstd::Compressor self;
287 PREINIT:
288 SV* output;
289 size_t ret;
290 CODE:
291 output = newSVpv("", 0);
292 do {
293 ZSTD_outBuffer outbuf = { self->buf, self->bufsize, 0 };
294 ret = ZSTD_endStream(self->stream, &outbuf);
295 if (ZSTD_isError(ret)) {
296 croak("%s", ZSTD_getErrorName(ret));
297 }
298 sv_catpvn(output, outbuf.dst, outbuf.pos);
299 } while (ret > 0);
300 RETVAL = output;
301 OUTPUT:
302 RETVAL
303
304 void
305 DESTROY(self)
306 Compress::Zstd::Compressor self;
307 CODE:
308 ZSTD_freeCStream(self->stream);
309 Safefree(self->buf);
310 Safefree(self);
311
312 MODULE = Compress::Zstd PACKAGE = Compress::Zstd::Decompressor
313
314 PROTOTYPES: DISABLE
315
316 BOOT:
317 {
318 HV* stash = gv_stashpv("Compress::Zstd::Decompressor", 1);
319 newCONSTSUB(stash, "ZSTD_DSTREAM_IN_SIZE", newSViv(ZSTD_DStreamInSize()));
320 }
321
322 Compress::Zstd::Decompressor
323 new(klass)
324 const char* klass;
325 PREINIT:
326 Compress__Zstd__Decompressor self;
327 char* buf;
328 size_t bufsize;
329 CODE:
330 ZSTD_DStream* stream = ZSTD_createDStream();
331 if (stream == NULL) {
332 croak("Failed to call ZSTD_createDStream()");
333 }
334 ZSTD_initDStream(stream);
335
336 Newx(self, sizeof(struct Compress__Zstd__Decompressor_s), struct Compress__Zstd__Decompressor_s);
337 self->stream = stream;
338 bufsize = ZSTD_DStreamOutSize();
339 Newx(buf, bufsize, char);
340 self->buf = buf;
341 self->bufsize = bufsize;
342 RETVAL = self;
343 OUTPUT:
344 RETVAL
345
346 void
347 init(self)
348 Compress::Zstd::Decompressor self;
349 CODE:
350 ZSTD_initDStream(self->stream);
351
352 SV*
353 decompress(self, input)
354 Compress::Zstd::Decompressor self;
355 SV* input;
356 PREINIT:
357 STRLEN len;
358 SV* output;
359 CODE:
360 const char* in = SvPVbyte(input, len);
361 ZSTD_inBuffer inbuf = { in, len, 0 };
362 output = newSVpv("", 0);
363 while (inbuf.pos < inbuf.size) {
364 ZSTD_outBuffer outbuf = { self->buf, self->bufsize, 0 };
365 size_t ret = ZSTD_decompressStream(self->stream, &outbuf, &inbuf);
366 if (ZSTD_isError(ret)) {
367 croak("%s", ZSTD_getErrorName(ret));
368 }
369 sv_catpvn(output, outbuf.dst, outbuf.pos);
370 }
371 RETVAL = output;
372 OUTPUT:
373 RETVAL
374
375 void
376 DESTROY(self)
377 Compress::Zstd::Decompressor self;
378 CODE:
379 ZSTD_freeDStream(self->stream);
380 Safefree(self->buf);
381 Safefree(self);
382
383
384 MODULE = Compress::Zstd PACKAGE = Compress::Zstd::CompressionContext
385
386 PROTOTYPES: DISABLE
387
388 Compress::Zstd::CompressionContext
389 new(klass)
390 const char* klass;
391 CODE:
392 ZSTD_CCtx* cctx = ZSTD_createCCtx();
393 if (cctx == NULL) {
394 croak("Failed to call ZSTD_createCCtx()");
395 }
396 RETVAL = (Compress__Zstd__CompressionContext) cctx;
397 OUTPUT:
398 RETVAL
399
400 SV*
401 compress(self, source, level = 1)
402 Compress::Zstd::CompressionContext self;
403 SV* source;
404 int level;
405 PREINIT:
406 const char* src;
407 STRLEN src_len;
408 SV* dest;
409 char* dst;
410 size_t bound, ret;
411 PPCODE:
412 if (!SvOK(source)) {
413 XSRETURN_UNDEF;
414 }
415 src = SvPVbyte(source, src_len);
416 bound = ZSTD_compressBound(src_len);
417 dest = sv_2mortal(newSV(bound + 1));
418 dst = SvPVX(dest);
419 ret = ZSTD_compressCCtx((ZSTD_CCtx*) self, dst, bound + 1, src, src_len, level);
420 if (ZSTD_isError(ret)) {
421 XSRETURN_UNDEF;
422 }
423 dst[ret] = '\0';
424 SvCUR_set(dest, ret);
425 SvPOK_on(dest);
426 EXTEND(SP, 1);
427 PUSHs(dest);
428
429 SV*
430 compress_using_dict(self, source, dict)
431 Compress::Zstd::CompressionContext self;
432 SV* source;
433 Compress::Zstd::CompressionDictionary dict;
434 PREINIT:
435 const char* src;
436 STRLEN src_len;
437 SV* dest;
438 char* dst;
439 size_t bound, ret;
440 PPCODE:
441 if (!SvOK(source)) {
442 XSRETURN_UNDEF;
443 }
444 src = SvPVbyte(source, src_len);
445 bound = ZSTD_compressBound(src_len);
446 dest = sv_2mortal(newSV(bound + 1));
447 dst = SvPVX(dest);
448 ret = ZSTD_compress_usingCDict((ZSTD_CCtx*) self, dst, bound + 1, src, src_len, (ZSTD_CDict*) dict);
449 if (ZSTD_isError(ret)) {
450 XSRETURN_UNDEF;
451 }
452 dst[ret] = '\0';
453 SvCUR_set(dest, ret);
454 SvPOK_on(dest);
455 EXTEND(SP, 1);
456 PUSHs(dest);
457
458 void
459 DESTROY(self)
460 Compress::Zstd::CompressionContext self;
461 CODE:
462 ZSTD_freeCCtx((ZSTD_CCtx*) self);
463
464
465 MODULE = Compress::Zstd PACKAGE = Compress::Zstd::DecompressionContext
466
467 PROTOTYPES: DISABLE
468
469 Compress::Zstd::DecompressionContext
470 new(klass)
471 const char* klass;
472 CODE:
473 ZSTD_DCtx* dctx = ZSTD_createDCtx();
474 if (dctx == NULL) {
475 croak("Failed to call ZSTD_createDCtx()");
476 }
477 RETVAL = (Compress__Zstd__DecompressionContext) dctx;
478 OUTPUT:
479 RETVAL
480
481 SV*
482 decompress(self, source)
483 Compress::Zstd::DecompressionContext self;
484 SV* source;
485 ALIAS:
486 uncompress = 1
487 PREINIT:
488 const char* src;
489 STRLEN src_len;
490 unsigned long long dest_len;
491 SV* dest;
492 char* dst;
493 size_t ret;
494 PPCODE:
495 if (!SvOK(source)) {
496 XSRETURN_UNDEF;
497 }
498 src = SvPVbyte(source, src_len);
499 dest_len = ZSTD_getFrameContentSize(src, src_len);
500 if (dest_len == ZSTD_CONTENTSIZE_UNKNOWN || dest_len == ULLONG_MAX || ZSTD_isError(dest_len)) {
501 /* TODO: Support ZSTD_CONTENTSIZE_UNKNOWN */
502 XSRETURN_UNDEF;
503 }
504 dest = sv_2mortal(newSV(dest_len + 1));
505 dst = SvPVX(dest);
506 ret = ZSTD_decompressDCtx((ZSTD_DCtx*) self, dst, dest_len + 1, src, src_len);
507 if (ZSTD_isError(ret)) {
508 XSRETURN_UNDEF;
509 }
510 dst[ret] = '\0';
511 SvCUR_set(dest, ret);
512 SvPOK_on(dest);
513 EXTEND(SP, 1);
514 PUSHs(dest);
515
516 SV*
517 decompress_using_dict(self, source, dict)
518 Compress::Zstd::DecompressionContext self;
519 SV* source;
520 Compress::Zstd::DecompressionDictionary dict;
521 PREINIT:
522 const char* src;
523 STRLEN src_len;
524 unsigned long long dest_len;
525 SV* dest;
526 char* dst;
527 size_t ret;
528 PPCODE:
529 if (!SvOK(source)) {
530 XSRETURN_UNDEF;
531 }
532 src = SvPVbyte(source, src_len);
533 dest_len = ZSTD_getFrameContentSize(src, src_len);
534 if (dest_len == ZSTD_CONTENTSIZE_UNKNOWN || dest_len == ULLONG_MAX || ZSTD_isError(dest_len)) {
535 /* TODO: Support ZSTD_CONTENTSIZE_UNKNOWN */
536 XSRETURN_UNDEF;
537 }
538 dest = sv_2mortal(newSV(dest_len + 1));
539 dst = SvPVX(dest);
540 ret = ZSTD_decompress_usingDDict((ZSTD_DCtx*) self, dst, dest_len + 1, src, src_len, (ZSTD_DDict*) dict);
541 if (ZSTD_isError(ret)) {
542 XSRETURN_UNDEF;
543 }
544 dst[ret] = '\0';
545 SvCUR_set(dest, ret);
546 SvPOK_on(dest);
547 EXTEND(SP, 1);
548 PUSHs(dest);
549
550 void
551 DESTROY(self)
552 Compress::Zstd::DecompressionContext self;
553 CODE:
554 ZSTD_freeDCtx((ZSTD_DCtx*) self);
555
556 MODULE = Compress::Zstd PACKAGE = Compress::Zstd::CompressionDictionary
557
558 PROTOTYPES: DISABLE
559
560 Compress::Zstd::CompressionDictionary
561 new(klass, dict, level = 1)
562 const char* klass;
563 SV* dict;
564 int level;
565 PREINIT:
566 ZSTD_CDict* cdict;
567 const char* dct;
568 size_t dct_len;
569 CODE:
570 dct = SvPVbyte(dict, dct_len);
571 cdict = ZSTD_createCDict(dct, dct_len, level);
572 if (cdict == NULL) {
573 croak("Failed to call ZSTD_createCDict()");
574 }
575 RETVAL = (Compress__Zstd__CompressionDictionary) cdict;
576 OUTPUT:
577 RETVAL
578
579 void
580 DESTROY(self)
581 Compress::Zstd::CompressionDictionary self;
582 CODE:
583 ZSTD_freeCDict((ZSTD_CDict*) self);
584
585 MODULE = Compress::Zstd PACKAGE = Compress::Zstd::DecompressionDictionary
586
587 PROTOTYPES: DISABLE
588
589 Compress::Zstd::DecompressionDictionary
590 new(klass, dict)
591 const char* klass;
592 SV* dict;
593 PREINIT:
594 ZSTD_DDict* ddict;
595 const char* dct;
596 size_t dct_len;
597 CODE:
598 dct = SvPVbyte(dict, dct_len);
599 ddict = ZSTD_createDDict(dct, dct_len);
600 if (ddict == NULL) {
601 croak("Failed to call ZSTD_createDDict()");
602 }
603 RETVAL = (Compress__Zstd__DecompressionDictionary) ddict;
604 OUTPUT:
605 RETVAL
606
607 void
608 DESTROY(self)
609 Compress::Zstd::DecompressionDictionary self;
610 CODE:
611 ZSTD_freeDDict((ZSTD_DDict*) self);
612