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