File Coverage

CompressLZ4Frame.xs
Criterion Covered Total %
statement 97 133 72.9
branch 42 120 35.0
condition n/a
subroutine n/a
pod n/a
total 139 253 54.9


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2              
3             #include "EXTERN.h"
4             #include "perl.h"
5             #include "XSUB.h"
6             #include "ppport.h"
7              
8             #include "lz4frame.h"
9             #include "lz4frame_static.h"
10              
11             enum { CHUNK_SIZE = 65536 }; // 64 KiB
12              
13 6           SV * decompress_single_frame(pTHX_ char * src, size_t src_len, size_t * bytes_processed)
14             {
15             size_t result, bytes_read, dest_len;
16             LZ4F_decompressionContext_t ctx;
17             LZ4F_frameInfo_t info;
18 6           SV * decompressed = NULL;
19 6           char * dest = NULL;
20              
21 6           *bytes_processed = 0u;
22              
23 6           result = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
24 6 50         if (LZ4F_isError(result)) {
25 0           warn("Could not create decompression context: %s", LZ4F_getErrorName(result));
26 0           return NULL;
27             }
28              
29 6           bytes_read = src_len;
30 6           result = LZ4F_getFrameInfo(ctx, &info, src, &bytes_read);
31 6 50         if (LZ4F_isError(result)) {
32 0           warn("Could not read frame info: %s", LZ4F_getErrorName(result));
33 0           LZ4F_freeDecompressionContext(ctx);
34 0           return NULL;
35             }
36 6           *bytes_processed += bytes_read;
37 6           src_len -= bytes_read;
38              
39 6 100         if (info.contentSize)
40             {
41             // content size header has a value
42 3           dest_len = (size_t)info.contentSize;
43 3           decompressed = newSV(dest_len);
44 3           dest = SvPVX(decompressed);
45 3 50         if (!dest) {
46 0           warn("Could not allocate enough memory (%zu Bytes)", dest_len);
47 0           LZ4F_freeDecompressionContext(ctx);
48 0           SvREFCNT_dec(decompressed);
49 0           return NULL;
50             }
51              
52 3           result = LZ4F_decompress(ctx, dest, &dest_len, src + bytes_read, &src_len, NULL);
53 3           LZ4F_freeDecompressionContext(ctx);
54 3 50         if (LZ4F_isError(result)) {
55 0           warn("Error during decompression: %s", LZ4F_getErrorName(result));
56 0           SvREFCNT_dec(decompressed);
57 0           return NULL;
58             }
59 3           *bytes_processed += src_len;
60              
61 3           SvCUR_set(decompressed, dest_len);
62 3           SvPOK_on(decompressed);
63             }
64             else
65             {
66             // content size header is 0 => decompress in chunks
67 3           size_t dest_offset = 0u, src_offset = bytes_read, current_chunk = CHUNK_SIZE;
68 3           dest_len = CHUNK_SIZE;
69 3           Newx(dest, dest_len, char);
70             for (;;)
71             {
72 517           bytes_read = src_len;
73              
74 517 50         if (!dest) {
75 0           warn("Could not allocate enough memory (%zu Bytes)", dest_len);
76 0           LZ4F_freeDecompressionContext(ctx);
77 1           return NULL;
78             }
79              
80 517           result = LZ4F_decompress(ctx, dest + dest_offset, ¤t_chunk, src + src_offset, &bytes_read, NULL);
81 517 50         if (LZ4F_isError(result) || !current_chunk) {
    100          
82 1 50         if (LZ4F_isError(result))
83 0           warn("Error during decompression: %s", LZ4F_getErrorName(result));
84 1           Safefree(dest);
85 1           LZ4F_freeDecompressionContext(ctx);
86 1           return NULL;
87             }
88              
89             // bytes_processed is relevant for concatenated frames
90 516           *bytes_processed += bytes_read;
91              
92             // current_chunk contains how much was read
93             // dest_offset is where the current chunk started
94             // result contains the number of bytes that LZ4F is still expecting
95             // in combination this should be the full new size of the destination buffer
96 516           dest_len = dest_offset + current_chunk + result;
97              
98 516 100         if (!result) // 0 means no more data in this frame
99 2           break;
100              
101             // where the next chunk will be read to
102 514           dest_offset += current_chunk;
103             // the size of the next chunk
104 514           current_chunk = result;
105             // how much is left to read from the source buffer
106 514           src_len -= bytes_read;
107             // where to read from
108 514           src_offset += bytes_read;
109              
110 514           Renew(dest, dest_len, char);
111 514           }
112              
113             // done uncompressing, now put the stuff into a scalar
114 2           decompressed = newSV(0);
115 2           sv_usepvn_flags(decompressed, dest, dest_len, SV_SMAGIC);
116 2           LZ4F_freeDecompressionContext(ctx);
117             }
118              
119 6           return decompressed;
120             }
121              
122             MODULE = Compress::LZ4Frame PACKAGE = Compress::LZ4Frame
123             PROTOTYPES: ENABLE
124              
125             SV *
126             compress(sv, level = 0)
127             SV * sv
128             int level
129             ALIAS:
130             compress_checksum = 1
131             PREINIT:
132 1           LZ4F_preferences_t prefs = { 0 };
133             char * src, * dest;
134             size_t src_len, dest_len;
135             CODE:
136 1 50         SvGETMAGIC(sv);
    0          
137 1 50         if (SvROK(sv) && !SvAMAGIC(sv)) {
    0          
    0          
    0          
138 0           sv = SvRV(sv);
139 0 0         SvGETMAGIC(sv);
    0          
140             }
141 1 50         if (!SvOK(sv))
    0          
    0          
142 0           XSRETURN_NO;
143              
144 1 50         src = SvPVbyte(sv, src_len);
145 1 50         if (!src_len)
146 0           XSRETURN_NO;
147              
148 1           prefs.frameInfo.contentChecksumFlag = (ix == 1 ? LZ4F_contentChecksumEnabled : LZ4F_noContentChecksum);
149 1           prefs.frameInfo.contentSize = (unsigned long long)src_len;
150 1           prefs.compressionLevel = level;
151 1           prefs.autoFlush = 1u;
152              
153 1           dest_len = LZ4F_compressFrameBound(src_len, &prefs);
154 1           RETVAL = newSV(dest_len);
155 1           dest = SvPVX(RETVAL);
156 1 50         if (!dest) {
157 0           warn("Could not allocate enough memory (%zu Bytes)", dest_len);
158 0           SvREFCNT_dec(RETVAL);
159 0           XSRETURN_UNDEF;
160             }
161              
162 1           dest_len = LZ4F_compressFrame(dest, dest_len, src, src_len, &prefs);
163 1 50         if (LZ4F_isError(dest_len)) {
164 0           warn("Error during compression: %s", LZ4F_getErrorName(dest_len));
165 0           SvREFCNT_dec(RETVAL);
166 0           XSRETURN_UNDEF;
167             }
168              
169 1           SvCUR_set(RETVAL, dest_len);
170 1           SvPOK_on(RETVAL);
171             OUTPUT:
172             RETVAL
173              
174             SV *
175             decompress(sv)
176             SV * sv
177             PREINIT:
178             char * src;
179             size_t src_len, bytes_read;
180 5           SV * current = (SV*)1; /* simply not NULL */
181             CODE:
182 5 50         SvGETMAGIC(sv);
    0          
183 5 50         if (SvROK(sv) && !SvAMAGIC(sv)) {
    0          
    0          
    0          
184 0           sv = SvRV(sv);
185 0 0         SvGETMAGIC(sv);
    0          
186             }
187 5 50         if (!SvOK(sv))
    0          
    0          
188 1           XSRETURN_NO;
189              
190 5 50         src = SvPVbyte(sv, src_len);
191 5 50         if (!src_len)
192 0           XSRETURN_NO;
193              
194 5           RETVAL = decompress_single_frame(aTHX_ src, src_len, &bytes_read);
195 5 100         if (RETVAL == NULL)
196 1           XSRETURN_UNDEF;
197 4           src += bytes_read;
198 4 50         src_len = src_len >= bytes_read ? src_len - bytes_read : 0u;
199 5 100         while (src_len && (current = decompress_single_frame(aTHX_ src, src_len, &bytes_read)) && (bytes_read > 0))
    50          
    50          
200             {
201 1           sv_catsv(RETVAL, current);
202 1           SvREFCNT_dec(current);
203 1           src += bytes_read;
204 1 50         src_len = src_len >= bytes_read ? src_len - bytes_read : 0u;
205             }
206 4 50         if (current == NULL)
207             {
208 0           SvREFCNT_dec(RETVAL);
209 0           XSRETURN_UNDEF;
210             }
211              
212             OUTPUT:
213             RETVAL
214              
215             int
216             looks_like_lz4frame(sv)
217             SV * sv
218             PREINIT:
219             LZ4F_decompressionContext_t ctx;
220             LZ4F_frameInfo_t info;
221             char * src;
222             size_t src_len;
223             size_t result;
224             CODE:
225 2 50         SvGETMAGIC(sv);
    0          
226 2 50         if (SvROK(sv) && !SvAMAGIC(sv)) {
    0          
    0          
    0          
227 0           sv = SvRV(sv);
228 0 0         SvGETMAGIC(sv);
    0          
229             }
230 2 50         if (!SvOK(sv))
    0          
    0          
231 0           XSRETURN_NO;
232              
233 2 50         src = SvPVbyte(sv, src_len);
234 2 50         if (!src_len)
235 0           XSRETURN_NO;
236              
237 2           result = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
238 2 50         if (LZ4F_isError(result)) {
239 0           warn("Could not create decompression context: %s", LZ4F_getErrorName(result));
240 0           XSRETURN_UNDEF;
241             }
242              
243 2           result = LZ4F_getFrameInfo(ctx, &info, src, &src_len);
244 2 100         if (LZ4F_isError(result)) {
245             /*
246             * No warning: we actually just wanted to check if this is valid LZ4 Frame data
247             * warn("Could not read frame info: %s", LZ4F_getErrorName(result));
248             */
249 1           LZ4F_freeDecompressionContext(ctx);
250 1           XSRETURN_NO;
251             }
252              
253 1           LZ4F_freeDecompressionContext(ctx);
254 2           XSRETURN_YES;
255