File Coverage

xs-src/unpack.c
Criterion Covered Total %
statement 154 182 84.6
branch 81 168 48.2
condition n/a
subroutine n/a
pod n/a
total 235 350 67.1


line stmt bran cond sub pod time code
1             #define NEED_newRV_noinc
2             #define NEED_sv_2pv_flags
3             #include "xshelper.h"
4              
5             #define MY_CXT_KEY "Data::MessagePack::_unpack_guts" XS_VERSION
6             typedef struct {
7             SV* msgpack_true;
8             SV* msgpack_false;
9             } my_cxt_t;
10             START_MY_CXT
11              
12             // context data for execute_template()
13             typedef struct {
14             bool finished;
15             bool utf8;
16             SV* buffer;
17             } unpack_user;
18             #define UNPACK_USER_INIT { false, false, NULL }
19              
20             #include "msgpack/unpack_define.h"
21              
22             #define msgpack_unpack_struct(name) \
23             struct template ## name
24              
25             #define msgpack_unpack_func(ret, name) \
26             STATIC_INLINE ret template ## name
27              
28             #define msgpack_unpack_callback(name) \
29             template_callback ## name
30              
31             #define msgpack_unpack_object SV*
32              
33             #define msgpack_unpack_user unpack_user
34              
35 25           void init_Data__MessagePack_unpack(pTHX_ bool const cloning) {
36             // booleans are load on demand (lazy load).
37             if(!cloning) {
38             MY_CXT_INIT;
39             PERL_UNUSED_VAR(MY_CXT);
40             }
41             else {
42             MY_CXT_CLONE;
43             }
44              
45             dMY_CXT;
46 25           MY_CXT.msgpack_true = NULL;
47 25           MY_CXT.msgpack_false = NULL;
48 25           }
49              
50              
51              
52             /* ---------------------------------------------------------------------- */
53             /* utility functions */
54              
55             static SV*
56 18           load_bool(pTHX_ const char* const name) {
57 18           CV* const cv = get_cv(name, GV_ADD);
58 18           dSP;
59 18           ENTER;
60 18           SAVETMPS;
61 18 50         PUSHMARK(SP);
62 18           call_sv((SV*)cv, G_SCALAR);
63 18           SPAGAIN;
64 18           SV* const sv = newSVsv(POPs);
65 18           PUTBACK;
66 18 50         FREETMPS;
67 18           LEAVE;
68             assert(sv);
69             assert(sv_isobject(sv));
70 18 50         if(!SvOK(sv)) {
    0          
    0          
71 0           croak("Oops: Failed to load %"SVf, name);
72             }
73 18           return sv;
74             }
75              
76             static SV*
77 995           get_bool(bool const value) {
78             dTHX;
79             dMY_CXT;
80 995 100         if(value) {
81 486 100         if(!MY_CXT.msgpack_true) {
82 9           MY_CXT.msgpack_true = load_bool(aTHX_ "Data::MessagePack::true");
83             }
84 486           return newSVsv(MY_CXT.msgpack_true);
85             }
86             else {
87 509 100         if(!MY_CXT.msgpack_false) {
88 9           MY_CXT.msgpack_false = load_bool(aTHX_ "Data::MessagePack::false");
89             }
90 509           return newSVsv(MY_CXT.msgpack_false);
91             }
92             }
93              
94             /* ---------------------------------------------------------------------- */
95             struct template_context;
96             typedef struct template_context msgpack_unpack_t;
97              
98             static void template_init(msgpack_unpack_t* u);
99              
100             static SV* template_data(msgpack_unpack_t* u);
101              
102             static int template_execute(msgpack_unpack_t* u PERL_UNUSED_DECL,
103             const char* data, size_t len, size_t* off);
104              
105             STATIC_INLINE SV* template_callback_root(unpack_user* u PERL_UNUSED_DECL)
106             {
107             return NULL;
108             }
109              
110             #if IVSIZE == 4
111              
112             STATIC_INLINE int template_callback_UV(unpack_user* u PERL_UNUSED_DECL, UV const d, SV** o)
113             {
114             dTHX;
115             *o = newSVuv(d);
116             return 0;
117             }
118              
119             STATIC_INLINE int template_callback_IV(unpack_user* u PERL_UNUSED_DECL, IV const d, SV** o)
120             {
121             dTHX;
122             *o = newSViv(d);
123             return 0;
124             }
125              
126             /* workaround win32 problems (my_snprintf(%llu) returns incorrect values ) */
127             static char* str_from_uint64(char* buf_end, uint64_t v)
128             {
129             char *p = buf_end;
130             *--p = '\0';
131             do {
132             *--p = '0' + v % 10;
133             } while ((v /= 10) != 0);
134             return p;
135             }
136              
137             static const char* str_from_int64(char* buf_end, int64_t const v) {
138             bool const minus = v < 0;
139             char* p = str_from_uint64(buf_end, minus ? -v : v);
140             if (minus)
141             *--p = '-';
142             return p;
143             }
144              
145             static int template_callback_uint64(unpack_user* u PERL_UNUSED_DECL, uint64_t const d, SV** o)
146             {
147             dTHX;
148             char tbuf[64];
149             const char* const s = str_from_uint64(tbuf + sizeof(tbuf), d);
150             *o = newSVpvn(s, tbuf + sizeof(tbuf) - 1 - s);
151             return 0;
152             }
153              
154             static int template_callback_int64(unpack_user* u PERL_UNUSED_DECL, int64_t const d, SV** o)
155             {
156             dTHX;
157             char tbuf[64];
158             const char* const s = str_from_int64(tbuf + sizeof(tbuf), d);
159             *o = newSVpvn(s, tbuf + sizeof(tbuf) - 1 - s);
160             return 0;
161             }
162              
163             #else /* IVSIZE == 8 */
164              
165              
166             STATIC_INLINE int template_callback_UV(unpack_user* u PERL_UNUSED_DECL, UV const d, SV** o)
167             {
168             dTHX;
169 6381           *o = newSVuv(d);
170             return 0;
171             }
172              
173             #define template_callback_uint64 template_callback_UV
174              
175             STATIC_INLINE int template_callback_IV(unpack_user* u PERL_UNUSED_DECL, IV const d, SV** o)
176             {
177             dTHX;
178 3752           *o = newSViv(d);
179             return 0;
180             }
181              
182             #define template_callback_int64 template_callback_IV
183              
184             #endif /* IVSIZE */
185              
186             #define template_callback_uint8 template_callback_UV
187             #define template_callback_uint16 template_callback_UV
188             #define template_callback_uint32 template_callback_UV
189              
190             #define template_callback_int8 template_callback_IV
191             #define template_callback_int16 template_callback_IV
192             #define template_callback_int32 template_callback_IV
193              
194             #define template_callback_float template_callback_double
195              
196             STATIC_INLINE int template_callback_double(unpack_user* u PERL_UNUSED_DECL, double d, SV** o)
197             {
198             dTHX;
199 964           *o = newSVnv(d);
200             return 0;
201             }
202              
203             /* &PL_sv_undef is not so good. see http://gist.github.com/387743 */
204             STATIC_INLINE int template_callback_nil(unpack_user* u PERL_UNUSED_DECL, SV** o)
205             {
206             dTHX;
207 1529           *o = newSV(0);
208             return 0;
209             }
210              
211             STATIC_INLINE int template_callback_true(unpack_user* u PERL_UNUSED_DECL, SV** o)
212             {
213 486           *o = get_bool(true);
214             return 0;
215             }
216              
217             STATIC_INLINE int template_callback_false(unpack_user* u PERL_UNUSED_DECL, SV** o)
218             {
219 17120           *o = get_bool(false);
220             return 0;
221             }
222              
223 5018           STATIC_INLINE int template_callback_array(unpack_user* u PERL_UNUSED_DECL, unsigned int n, SV** o)
224             {
225             dTHX;
226 2509           AV* const a = newAV();
227 2509           *o = newRV_noinc((SV*)a);
228 2509           av_extend(a, n + 1);
229 2509           return 0;
230             }
231              
232             STATIC_INLINE int template_callback_array_item(unpack_user* u PERL_UNUSED_DECL, SV** c, SV* o)
233             {
234             dTHX;
235 16278           AV* const a = (AV*)SvRV(*c);
236             assert(SvTYPE(a) == SVt_PVAV);
237 16278           (void)av_store(a, AvFILLp(a) + 1, o); // the same as av_push(a, o)
238             return 0;
239             }
240              
241 2586           STATIC_INLINE int template_callback_map(unpack_user* u PERL_UNUSED_DECL, unsigned int n, SV** o)
242             {
243             dTHX;
244 1293           HV* const h = newHV();
245 1293           hv_ksplit(h, n);
246 1293           *o = newRV_noinc((SV*)h);
247 1293           return 0;
248             }
249              
250 989           STATIC_INLINE int template_callback_map_item(unpack_user* u PERL_UNUSED_DECL, SV** c, SV* k, SV* v)
251             {
252             dTHX;
253 989           HV* const h = (HV*)SvRV(*c);
254             assert(SvTYPE(h) == SVt_PVHV);
255 989           (void)hv_store_ent(h, k, v, 0);
256             SvREFCNT_dec(k);
257 989           return 0;
258             }
259              
260 110           STATIC_INLINE int template_callback_str(unpack_user* u PERL_UNUSED_DECL, const char* b PERL_UNUSED_DECL, const char* p, unsigned int l, SV** o)
261             {
262             dTHX;
263             /* newSVpvn(p, l) returns an undef if p == NULL */
264 55 100         *o = ((l==0) ? newSVpvs("") : newSVpvn(p, l));
265 55           sv_utf8_decode(*o);
266 55           return 0;
267             }
268              
269 5458           STATIC_INLINE int template_callback_bin(unpack_user* u PERL_UNUSED_DECL, const char* b PERL_UNUSED_DECL, const char* p, unsigned int l, SV** o)
270             {
271             dTHX;
272 2729 100         *o = ((l==0) ? newSVpvs("") : newSVpvn(p, l));
273 2729 100         if(u->utf8) {
274 4           sv_utf8_decode(*o);
275             }
276 2729           return 0;
277             }
278              
279 0           STATIC_INLINE int template_callback_ext(unpack_user* u PERL_UNUSED_DECL, const char* b PERL_UNUSED_DECL, const char* p PERL_UNUSED_DECL,
280             unsigned int l PERL_UNUSED_DECL, SV** o PERL_UNUSED_DECL)
281             {
282 0           croak("EXT type is not supporeted yet");
283             return 0;
284             }
285              
286             #include "msgpack/unpack_template.h"
287              
288             #define UNPACKER(from, name) \
289             msgpack_unpack_t *name; \
290             { \
291             SV* const obj = from; \
292             if(!(SvROK(obj) && SvIOK(SvRV(obj)))) { \
293             Perl_croak(aTHX_ "Invalid unpacker instance for " #name); \
294             } \
295             name = INT2PTR(msgpack_unpack_t*, SvIVX(SvRV((obj)))); \
296             if(name == NULL) { \
297             Perl_croak(aTHX_ "NULL found for " # name " when shouldn't be"); \
298             } \
299             }
300              
301 157           XS(xs_unpack) {
302 314           dXSARGS;
303 157           SV* const self = ST(0);
304 157           SV* const data = ST(1);
305             unpack_user u = UNPACK_USER_INIT;
306              
307             // setup configuration
308 157 100         if(SvROK(self) && SvTYPE(SvRV(self)) == SVt_PVHV) {
    50          
309             HV* const hv = (HV*)SvRV(self);
310             SV** svp;
311              
312 21           svp = hv_fetchs(hv, "utf8", FALSE);
313 21 100         if(svp) {
314 6 50         u.utf8 = SvTRUE(*svp) ? true : false;
    50          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
315             }
316             }
317              
318 157 50         if (!(items == 2 || items == 3)) {
319 0           Perl_croak(aTHX_ "Usage: Data::MessagePack->unpack('data' [, $limit])");
320             }
321              
322             STRLEN dlen;
323 157 50         const char* const dptr = SvPV_const(data, dlen);
324              
325             msgpack_unpack_t mp;
326             template_init(&mp);
327 157           mp.user = u;
328              
329 157           size_t from = 0;
330 157           int const ret = template_execute(&mp, dptr, (size_t)dlen, &from);
331             SV* const obj = template_data(&mp);
332 157           sv_2mortal(obj);
333              
334 157 50         if(ret < 0) {
335 0           Perl_croak(aTHX_ "Data::MessagePack->unpack: parse error");
336 157 50         } else if(ret == 0) {
337 0           Perl_croak(aTHX_ "Data::MessagePack->unpack: insufficient bytes");
338             } else {
339 157 100         if(from < dlen) {
340 44           Perl_croak(aTHX_ "Data::MessagePack->unpack: extra bytes");
341             }
342             }
343              
344 113           ST(0) = obj;
345 113           XSRETURN(1);
346             }
347              
348             /* ------------------------------ stream -- */
349             /* http://twitter.com/frsyuki/status/13249304748 */
350              
351              
352 37           XS(xs_unpacker_new) {
353 74           dXSARGS;
354 37 50         if (items != 1) {
355 0           Perl_croak(aTHX_ "Usage: Data::MessagePack::Unpacker->new()");
356             }
357              
358 37           SV* const self = sv_newmortal();
359             msgpack_unpack_t *mp;
360              
361 37           Newxz(mp, 1, msgpack_unpack_t);
362             template_init(mp);
363             unpack_user const u = UNPACK_USER_INIT;
364 37           mp->user = u;
365              
366 37           mp->user.buffer = newSV(80);
367 37           sv_setpvs(mp->user.buffer, "");
368              
369 37           sv_setref_pv(self, "Data::MessagePack::Unpacker", mp);
370              
371 37           ST(0) = self;
372 37           XSRETURN(1);
373             }
374              
375 2           XS(xs_unpacker_utf8) {
376 4           dXSARGS;
377 2 50         if (!(items == 1 || items == 2)) {
378 0           Perl_croak(aTHX_ "Usage: $unpacker->utf8([$bool)");
379             }
380 2 50         UNPACKER(ST(0), mp);
    50          
    50          
381 2 100         mp->user.utf8 = (items == 1 || sv_true(ST(1))) ? true : false;
    50          
382 2           XSRETURN(1); // returns $self
383             }
384              
385 4           XS(xs_unpacker_get_utf8) {
386 8           dXSARGS;
387 4 50         if (items != 1) {
388 0           Perl_croak(aTHX_ "Usage: $unpacker->get_utf8()");
389             }
390 4 50         UNPACKER(ST(0), mp);
    50          
    50          
391 4 100         ST(0) = boolSV(mp->user.utf8);
392 4           XSRETURN(1);
393             }
394              
395             STATIC_INLINE size_t
396 3412           _execute_impl(SV* const self, SV* const data, UV const offset, UV const limit) {
397             dTHX;
398              
399 1706 50         if(offset >= limit) {
400 0           Perl_croak(aTHX_
401             "offset (%"UVuf") is bigger than data buffer size (%"UVuf")",
402             offset, limit);
403             }
404              
405 1706 50         UNPACKER(self, mp);
    50          
    50          
406              
407 1706           size_t from = offset;
408 1706 50         const char* dptr = SvPV_nolen_const(data);
409 1706           STRLEN dlen = limit;
410              
411 1706 100         if(SvCUR(mp->user.buffer) != 0) {
412 1468           sv_catpvn(mp->user.buffer, dptr, dlen);
413 1468 50         dptr = SvPV_const(mp->user.buffer, dlen);
414 1468           from = 0;
415             }
416              
417 1706           int const ret = template_execute(mp, dptr, dlen, &from);
418             // ret < 0 : error
419             // ret == 0 : insufficient
420             // ret > 0 : success
421              
422 1706 50         if(ret < 0) {
423 0           Perl_croak(aTHX_
424             "Data::MessagePack::Unpacker: parse error while executing");
425             }
426              
427 1706           mp->user.finished = (ret > 0) ? true : false;
428 1706 100         if(!mp->user.finished) {
429             template_init(mp); // reset the state
430 1468           sv_setpvn(mp->user.buffer, dptr, dlen);
431 1468           from = 0;
432             }
433             else {
434 238           sv_setpvs(mp->user.buffer, "");
435             }
436             //warn(">> (%d) dlen=%d, from=%d, rest=%d",
437             // (int)ret, (int)dlen, (int)from, dlen - from);
438 1706           return from;
439             }
440              
441 1706           XS(xs_unpacker_execute) {
442 3412           dXSARGS;
443 1706           SV* const self = ST(0);
444 1706           SV* const data = ST(1);
445             UV offset;
446              
447 1706 100         if (items == 2) {
448             offset = 0;
449             }
450 224 50         else if (items == 3) {
451 224 50         offset = SvUVx(ST(2));
452             }
453             else {
454 0           Perl_croak(aTHX_ "Usage: $unpacker->execute(data, offset = 0)");
455             }
456              
457 1706 50         dXSTARG;
458 1706           sv_setuv(TARG, _execute_impl(self, data, offset, sv_len(data)));
459 1706           ST(0) = TARG;
460 1706           XSRETURN(1);
461             }
462              
463 0           XS(xs_unpacker_execute_limit) {
464 0           dXSARGS;
465 0 0         if (items != 4) {
466 0           Perl_croak(aTHX_ "Usage: $unpacker->execute_limit(data, offset, limit)");
467             }
468              
469 0           SV* const self = ST(0);
470 0           SV* const data = ST(1);
471 0 0         UV const offset = SvUVx(ST(2));
472 0 0         UV const limit = SvUVx(ST(3));
473              
474 0 0         dXSTARG;
475 0           sv_setuv(TARG, _execute_impl(self, data, offset, limit));
476 0           ST(0) = TARG;
477 0           XSRETURN(1);
478             }
479              
480 87           XS(xs_unpacker_is_finished) {
481 174           dXSARGS;
482 87 50         if (items != 1) {
483 0           Perl_croak(aTHX_ "Usage: $unpacker->is_finished()");
484             }
485              
486 87 50         UNPACKER(ST(0), mp);
    50          
    50          
487 87 50         ST(0) = boolSV(mp->user.finished);
488 87           XSRETURN(1);
489             }
490              
491 238           XS(xs_unpacker_data) {
492 476           dXSARGS;
493 238 50         if (items != 1) {
494 0           Perl_croak(aTHX_ "Usage: $unpacker->data()");
495             }
496              
497 238 50         UNPACKER(ST(0), mp);
    50          
    50          
498 238           ST(0) = template_data(mp);
499 238           XSRETURN(1);
500             }
501              
502 207           XS(xs_unpacker_reset) {
503 414           dXSARGS;
504 207 50         if (items != 1) {
505 0           Perl_croak(aTHX_ "Usage: $unpacker->reset()");
506             }
507              
508 207 50         UNPACKER(ST(0), mp);
    50          
    50          
509              
510             SV* const data = template_data(mp);
511             SvREFCNT_dec(data);
512              
513             template_init(mp);
514 207           sv_setpvs(mp->user.buffer, "");
515              
516 207           XSRETURN(0);
517             }
518              
519 37           XS(xs_unpacker_destroy) {
520 74           dXSARGS;
521 37 50         if (items != 1) {
522 0           Perl_croak(aTHX_ "Usage: $unpacker->DESTROY()");
523             }
524              
525 37 50         UNPACKER(ST(0), mp);
    50          
    50          
526              
527             SV* const data = template_data(mp);
528             SvREFCNT_dec(data);
529 37           SvREFCNT_dec(mp->user.buffer);
530 37           Safefree(mp);
531              
532 37           XSRETURN(0);
533             }