| 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
|
|
|
|
|
|
|
|