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