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