File Coverage

c/compress.c
Criterion Covered Total %
statement 39 74 52.7
branch 25 74 33.7
condition n/a
subroutine n/a
pod n/a
total 64 148 43.2


line stmt bran cond sub pod time code
1             static const char *fugz_imps[] = {"", "libdeflate", "zlib-ng", "zlib"};
2             static int fugz_imp = -1;
3              
4              
5             /* zlib & zlib-ng */
6              
7             typedef struct {
8             const char *next_in;
9             unsigned int avail_in;
10             unsigned long total_in;
11             char *next_out;
12             unsigned int avail_out;
13             unsigned long total_out;
14             const char *msg;
15             struct internal_state *state;
16             void *zalloc;
17             void *zfree;
18             void *opaque;
19             int data_type;
20             unsigned long adler;
21             unsigned long reserved;
22             } z_stream;
23              
24             static int (*deflate)(z_stream *, int);
25             static int (*deflateEnd)(z_stream *);
26             static int (*deflateInit2)(z_stream *, int, int, int, int, int);
27             static int (*deflateInit2_)(z_stream *, int, int, int, int, int, const char *, int);
28             static unsigned long (*compressBound)(unsigned long);
29              
30              
31             /* libdeflate */
32              
33             static struct libdeflate_compressor *fugz_ld_ctx;
34             static int fugz_ld_comp = -1;
35              
36             static struct libdeflate_compressor *(*libdeflate_alloc_compressor)(int);
37             static void (*libdeflate_free_compressor)(struct libdeflate_compressor *);
38             static size_t (*libdeflate_gzip_compress_bound)(struct libdeflate_compressor *, size_t);
39             static size_t (*libdeflate_gzip_compress)(struct libdeflate_compressor *, const void *, size_t, void *, size_t);
40              
41              
42              
43 11           static const char *fugz_lib() {
44 11 100         if (fugz_imp >= 0) goto done;
45              
46             void *handle;
47 1 50         if ((handle = dlopen("libdeflate.so", RTLD_LAZY))) {
48 0 0         if ((libdeflate_alloc_compressor = dlsym(handle, "libdeflate_alloc_compressor"))
49 0 0         && (libdeflate_free_compressor = dlsym(handle, "libdeflate_free_compressor"))
50 0 0         && (libdeflate_gzip_compress_bound = dlsym(handle, "libdeflate_gzip_compress_bound"))
51 0 0         && (libdeflate_gzip_compress = dlsym(handle, "libdeflate_gzip_compress"))) {
52 0           fugz_imp = 1;
53 0           goto done;
54             }
55             }
56              
57             int i;
58 2 50         for (i=2; i<=3; i++) {
59 2 100         if ((handle = dlopen(i == 2 ? "libz-ng.so" : "libz.so", RTLD_LAZY))) {
    100          
60 1 50         if (((deflate = dlsym(handle, "zng_deflate")) || (deflate = dlsym(handle, "deflate")))
    50          
61 1 50         && ((deflateEnd = dlsym(handle, "zng_deflateEnd")) || (deflateEnd = dlsym(handle, "deflateEnd")))
    50          
62 1 50         && ((deflateInit2 = dlsym(handle, "zng_deflateInit2")) || (deflateInit2_ = dlsym(handle, "deflateInit2_")))
    50          
63 1 50         && ((compressBound = dlsym(handle, "zng_compressBound")) || (compressBound = dlsym(handle, "compressBound")))) {
    50          
64 1           fugz_imp = i;
65 1           goto done;
66             }
67             }
68             }
69 0           fugz_imp = 0;
70              
71 11           done:
72 11           return fugz_imps[fugz_imp];
73             }
74              
75              
76 0           static SV *fugz_compress_ld(pTHX_ int level, const char *bytes, size_t inlen) {
77 0 0         if (fugz_ld_comp != level) {
78 0 0         if (fugz_ld_ctx) libdeflate_free_compressor(fugz_ld_ctx);
79 0           fugz_ld_ctx = NULL;
80 0           fugz_ld_comp = level;
81             }
82 0 0         if (!fugz_ld_ctx) fugz_ld_ctx = libdeflate_alloc_compressor(level);
83              
84 0           size_t outlen = libdeflate_gzip_compress_bound(fugz_ld_ctx, inlen);
85 0           SV *out = sv_2mortal(newSV(outlen));
86 0           SvPOK_only(out);
87 0           size_t len = libdeflate_gzip_compress(fugz_ld_ctx, bytes, inlen, SvPVX(out), outlen);
88 0 0         if (!len) fu_confess("Libdeflate compression failed"); /* Shouldn't happen */
89 0           SvCUR_set(out, len);
90 0           SvPVX(out)[len] = 0;
91 0           return out;
92             }
93              
94              
95 8           static SV *fugz_compress_zlib(pTHX_ int level, const char *bytes, size_t inlen) {
96             z_stream stream;
97 8           memset(&stream, 0, sizeof(stream));
98              
99 8           int r = deflateInit2
100 0           ? deflateInit2(&stream, level > 9 ? 9 : level, 8, 16+15, 9, 0)
101 8 50         : deflateInit2_(&stream, level > 9 ? 9 : level, 8, 16+15, 9, 0, "1.3.1", (int)sizeof(stream));
102 8 50         if (r) fu_confess("Zlib compression failed (%d)", r);
103              
104 8           stream.avail_out = compressBound(inlen) + 64; /* compressBound() does not include the gzip header */
105 8           SV *out = sv_2mortal(newSV(stream.avail_out));
106 8           SvPOK_only(out);
107 8           stream.next_out = SvPVX(out);
108 8           stream.next_in = bytes;
109 8           stream.avail_in = inlen;
110              
111 8 50         if ((r = deflate(&stream, 4)) != 1) fu_confess("Zlib compression failed (%d)", r);
112              
113 8           SvCUR_set(out, stream.total_out);
114 8           SvPVX(out)[stream.total_out] = 0;
115 8           deflateEnd(&stream);
116 8           return out;
117             }
118              
119              
120 8           static SV *fugz_compress(pTHX_ IV level, SV *in) {
121 8 50         if (level < 0 || level > 12) fu_confess("Invalid compression level: %"IVdf, level);
    50          
122 8 50         if (!*fugz_lib()) fu_confess("Unable to load a suitable compression library");
123              
124             STRLEN inlen;
125 8           const char *bytes = SvPVbyte(in, inlen);
126              
127 8 50         if (fugz_imp == 1) return fugz_compress_ld(aTHX_ level, bytes, inlen);
128 8           else return fugz_compress_zlib(aTHX_ level, bytes, inlen);
129             }
130              
131              
132              
133             /* Brotli */
134              
135             typedef enum { BROTLI_MODE_GENERIC = 0, BROTLI_MODE_TEXT = 1, BROTLI_MODE_FONT = 2 } BrotliEncoderMode;
136              
137             static size_t (*BrotliEncoderMaxCompressedSize)(size_t);
138             static int (*BrotliEncoderCompress)(int, int, BrotliEncoderMode, size_t, const char *, size_t *, char *);
139              
140 1           static SV *fubr_compress(pTHX_ IV level, SV *in) {
141 1 50         if (!BrotliEncoderCompress) {
142             void *handle;
143 1 50         if (!(handle = dlopen("libbrotlienc.so", RTLD_LAZY))
144 0 0         || !(BrotliEncoderMaxCompressedSize = dlsym(handle, "BrotliEncoderMaxCompressedSize"))
145 0 0         || !(BrotliEncoderCompress = dlsym(handle, "BrotliEncoderCompress")))
146 1           fu_confess("Unable to load libbrotlienc.so: %s", dlerror());
147             }
148 0 0         if (level < 0 || level > 11) fu_confess("Invalid compression level: %"IVdf, level);
    0          
149              
150             STRLEN inlen;
151 0           const char *bytes = SvPVbyte(in, inlen);
152              
153 0           size_t outlen = BrotliEncoderMaxCompressedSize(inlen);
154             /* "Result is only valid if quality is at least 2", so let's use a (more conservative?) fallback */
155 0 0         if (level < 2 && outlen < inlen + 256) outlen = inlen + 256;
    0          
156              
157 0           SV *out = sv_2mortal(newSV(outlen));
158 0           SvPOK_only(out);
159 0 0         if (!BrotliEncoderCompress(level, 22, BROTLI_MODE_GENERIC, inlen, bytes, &outlen, SvPVX(out)))
160 0           fu_confess("Brotli compression failed");
161 0           SvCUR_set(out, outlen);
162 0           SvPVX(out)[outlen] = 0;
163 0           return out;
164             }