File Coverage

CompressLZ4Frame.xs
Criterion Covered Total %
statement 96 132 72.7
branch 42 120 35.0
condition n/a
subroutine n/a
pod n/a
total 138 252 54.7


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             }
117              
118 6           return decompressed;
119             }
120              
121             MODULE = Compress::LZ4Frame PACKAGE = Compress::LZ4Frame
122             PROTOTYPES: ENABLE
123              
124             SV *
125             compress(sv, level = 0)
126             SV * sv
127             int level
128             ALIAS:
129             compress_checksum = 1
130             PREINIT:
131 1           LZ4F_preferences_t prefs = { 0 };
132             char * src, * dest;
133             size_t src_len, dest_len;
134             CODE:
135 1 50         SvGETMAGIC(sv);
    0          
136 1 50         if (SvROK(sv) && !SvAMAGIC(sv)) {
    0          
    0          
    0          
137 0           sv = SvRV(sv);
138 0 0         SvGETMAGIC(sv);
    0          
139             }
140 1 50         if (!SvOK(sv))
    0          
    0          
141 0           XSRETURN_NO;
142              
143 1 50         src = SvPVbyte(sv, src_len);
144 1 50         if (!src_len)
145 0           XSRETURN_NO;
146              
147 1           prefs.frameInfo.contentChecksumFlag = (ix == 1 ? LZ4F_contentChecksumEnabled : LZ4F_noContentChecksum);
148 1           prefs.frameInfo.contentSize = (unsigned long long)src_len;
149 1           prefs.compressionLevel = level;
150 1           prefs.autoFlush = 1u;
151              
152 1           dest_len = LZ4F_compressFrameBound(src_len, &prefs);
153 1           RETVAL = newSV(dest_len);
154 1           dest = SvPVX(RETVAL);
155 1 50         if (!dest) {
156 0           warn("Could not allocate enough memory (%zu Bytes)", dest_len);
157 0           SvREFCNT_dec(RETVAL);
158 0           XSRETURN_UNDEF;
159             }
160              
161 1           dest_len = LZ4F_compressFrame(dest, dest_len, src, src_len, &prefs);
162 1 50         if (LZ4F_isError(dest_len)) {
163 0           warn("Error during compression: %s", LZ4F_getErrorName(dest_len));
164 0           SvREFCNT_dec(RETVAL);
165 0           XSRETURN_UNDEF;
166             }
167              
168 1           SvCUR_set(RETVAL, dest_len);
169 1           SvPOK_on(RETVAL);
170             OUTPUT:
171             RETVAL
172              
173             SV *
174             decompress(sv)
175             SV * sv
176             PREINIT:
177             char * src;
178             size_t src_len, bytes_read;
179 5           SV * current = (SV*)1; /* simply not NULL */
180             CODE:
181 5 50         SvGETMAGIC(sv);
    0          
182 5 50         if (SvROK(sv) && !SvAMAGIC(sv)) {
    0          
    0          
    0          
183 0           sv = SvRV(sv);
184 0 0         SvGETMAGIC(sv);
    0          
185             }
186 5 50         if (!SvOK(sv))
    0          
    0          
187 1           XSRETURN_NO;
188              
189 5 50         src = SvPVbyte(sv, src_len);
190 5 50         if (!src_len)
191 0           XSRETURN_NO;
192              
193 5           RETVAL = decompress_single_frame(aTHX_ src, src_len, &bytes_read);
194 5 100         if (RETVAL == NULL)
195 1           XSRETURN_UNDEF;
196 4           src += bytes_read;
197 4 50         src_len = src_len >= bytes_read ? src_len - bytes_read : 0u;
198 5 100         while (src_len && (current = decompress_single_frame(aTHX_ src, src_len, &bytes_read)) && (bytes_read > 0))
    50          
    50          
199             {
200 1           sv_catsv(RETVAL, current);
201 1           SvREFCNT_dec(current);
202 1           src += bytes_read;
203 1 50         src_len = src_len >= bytes_read ? src_len - bytes_read : 0u;
204             }
205 4 50         if (current == NULL)
206             {
207 0           SvREFCNT_dec(RETVAL);
208 0           XSRETURN_UNDEF;
209             }
210              
211             OUTPUT:
212             RETVAL
213              
214             int
215             looks_like_lz4frame(sv)
216             SV * sv
217             PREINIT:
218             LZ4F_decompressionContext_t ctx;
219             LZ4F_frameInfo_t info;
220             char * src;
221             size_t src_len;
222             size_t result;
223             CODE:
224 2 50         SvGETMAGIC(sv);
    0          
225 2 50         if (SvROK(sv) && !SvAMAGIC(sv)) {
    0          
    0          
    0          
226 0           sv = SvRV(sv);
227 0 0         SvGETMAGIC(sv);
    0          
228             }
229 2 50         if (!SvOK(sv))
    0          
    0          
230 0           XSRETURN_NO;
231              
232 2 50         src = SvPVbyte(sv, src_len);
233 2 50         if (!src_len)
234 0           XSRETURN_NO;
235              
236 2           result = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
237 2 50         if (LZ4F_isError(result)) {
238 0           warn("Could not create decompression context: %s", LZ4F_getErrorName(result));
239 0           XSRETURN_UNDEF;
240             }
241              
242 2           result = LZ4F_getFrameInfo(ctx, &info, src, &src_len);
243 2 100         if (LZ4F_isError(result)) {
244             /*
245             * No warning: we actually just wanted to check if this is valid LZ4 Frame data
246             * warn("Could not read frame info: %s", LZ4F_getErrorName(result));
247             */
248 1           LZ4F_freeDecompressionContext(ctx);
249 1           XSRETURN_NO;
250             }
251              
252 1           LZ4F_freeDecompressionContext(ctx);
253 2           XSRETURN_YES;
254