File Coverage

xs-src/pack.c
Criterion Covered Total %
statement 121 138 87.6
branch 123 274 44.8
condition n/a
subroutine n/a
pod n/a
total 244 412 59.2


line stmt bran cond sub pod time code
1             /*
2             * code is written by tokuhirom.
3             * buffer alocation technique is taken from JSON::XS. thanks to mlehmann.
4             */
5             #include "xshelper.h"
6              
7             #include "msgpack/pack_define.h"
8              
9             #define msgpack_pack_inline_func(name) \
10             static inline void msgpack_pack ## name
11              
12             #define msgpack_pack_inline_func_cint(name) \
13             static inline void msgpack_pack ## name
14              
15             // serialization context
16             typedef struct {
17             char *cur; /* SvPVX (sv) + current output position */
18             const char *end; /* SvEND (sv) */
19             SV *sv; /* result scalar */
20              
21             bool prefer_int;
22             bool canonical;
23             } enc_t;
24              
25             STATIC_INLINE void
26 7367           dmp_append_buf(enc_t* const enc, const void* const buf, STRLEN const len)
27             {
28 7367 100         if (enc->cur + len >= enc->end) {
29             dTHX;
30 393           STRLEN const cur = enc->cur - SvPVX_const(enc->sv);
31 393           sv_grow (enc->sv, cur + (len < (cur >> 2) ? cur >> 2 : len) + 1);
32 393           enc->cur = SvPVX_mutable(enc->sv) + cur;
33 393           enc->end = SvPVX_const(enc->sv) + SvLEN (enc->sv) - 1;
34             }
35              
36 7367           memcpy(enc->cur, buf, len);
37 7367           enc->cur += len;
38 7367           }
39              
40             #define msgpack_pack_user enc_t*
41              
42             #define msgpack_pack_append_buffer(enc, buf, len) \
43             dmp_append_buf(enc, buf, len)
44              
45             #include "msgpack/pack_template.h"
46              
47             #define INIT_SIZE 32 /* initial scalar size to be allocated */
48              
49             #if IVSIZE == 8
50             # define PACK_IV msgpack_pack_int64
51             # define PACK_UV msgpack_pack_uint64
52             #elif IVSIZE == 4
53             # define PACK_IV msgpack_pack_int32
54             # define PACK_UV msgpack_pack_uint32
55             #elif IVSIZE == 2
56             # define PACK_IV msgpack_pack_int16
57             # define PACK_UV msgpack_pack_uint16
58             #else
59             # error "msgpack only supports IVSIZE = 8,4,2 environment."
60             #endif
61              
62             #define ERR_NESTING_EXCEEDED "perl structure exceeds maximum nesting level (max_depth set too low?)"
63              
64             #define DMP_PREF_INT "PreferInteger"
65              
66             /* interpreter global variables */
67             #define MY_CXT_KEY "Data::MessagePack::_pack_guts" XS_VERSION
68             typedef struct {
69             bool prefer_int;
70             bool canonical;
71             } my_cxt_t;
72             START_MY_CXT
73              
74              
75 89           static int dmp_config_set(pTHX_ SV* sv, MAGIC* mg) {
76             dMY_CXT;
77             assert(mg->mg_ptr);
78 89 50         if(strEQ(mg->mg_ptr, DMP_PREF_INT)) {
79 89 50         MY_CXT.prefer_int = SvTRUE(sv) ? true : false;
    50          
    0          
    100          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    100          
    50          
    0          
    100          
    0          
80             }
81             else {
82             assert(0);
83             }
84 89           return 0;
85             }
86              
87             MGVTBL dmp_config_vtbl = {
88             NULL,
89             dmp_config_set,
90             NULL,
91             NULL,
92             NULL,
93             NULL,
94             NULL,
95             #ifdef MGf_LOCAL
96             NULL,
97             #endif
98             };
99              
100 25           void init_Data__MessagePack_pack(pTHX_ bool const cloning) {
101 25 50         if(!cloning) {
102             MY_CXT_INIT;
103 25           MY_CXT.prefer_int = false;
104 25           MY_CXT.canonical = false;
105             }
106             else {
107             MY_CXT_CLONE;
108             }
109              
110 25           SV* var = get_sv("Data::MessagePack::" DMP_PREF_INT, GV_ADDMULTI);
111 25           sv_magicext(var, NULL, PERL_MAGIC_ext, &dmp_config_vtbl,
112             DMP_PREF_INT, 0);
113 25 50         SvSETMAGIC(var);
114 25           }
115              
116              
117 35           STATIC_INLINE int try_int(enc_t* enc, const char *p, size_t len) {
118             int negative = 0;
119 35           const char* pe = p + len;
120             uint64_t num = 0;
121              
122 35 100         if (len == 0) { return 0; }
123              
124 34 100         if (*p == '-') {
125             /* length(-0x80000000) == 11 */
126 12 100         if (len <= 1 || len > 11) { return 0; }
127             negative = 1;
128 4           ++p;
129             } else {
130             /* length(0xFFFFFFFF) == 10 */
131 26 100         if (len > 10) { return 0; }
132             }
133              
134             #if '9'=='8'+1 && '8'=='7'+1 && '7'=='6'+1 && '6'=='5'+1 && '5'=='4'+1 \
135             && '4'=='3'+1 && '3'=='2'+1 && '2'=='1'+1 && '1'=='0'+1
136             do {
137 66           unsigned int c = ((int)*(p++)) - '0';
138 66 100         if (c > 9) { return 0; }
139 61           num = num * 10 + c;
140 61 100         } while(p < pe);
141             #else
142             do {
143             switch (*(p++)) {
144             case '0': num = num * 10 + 0; break;
145             case '1': num = num * 10 + 1; break;
146             case '2': num = num * 10 + 2; break;
147             case '3': num = num * 10 + 3; break;
148             case '4': num = num * 10 + 4; break;
149             case '5': num = num * 10 + 5; break;
150             case '6': num = num * 10 + 6; break;
151             case '7': num = num * 10 + 7; break;
152             case '8': num = num * 10 + 8; break;
153             case '9': num = num * 10 + 9; break;
154             default: return 0;
155             }
156             } while(p < pe);
157             #endif
158              
159 17 100         if (negative) {
160 4 50         if (num > 0x80000000) { return 0; }
161 4           msgpack_pack_int32(enc, ((int32_t)-num));
162             } else {
163 13 50         if (num > 0xFFFFFFFF) { return 0; }
164 13           msgpack_pack_uint32(enc, (uint32_t)num);
165             }
166              
167             return 1;
168             }
169              
170              
171             STATIC_INLINE void _msgpack_pack_rv(pTHX_ enc_t *enc, SV* sv, int depth, bool utf8);
172              
173 5719           STATIC_INLINE void _msgpack_pack_sv(pTHX_ enc_t* const enc, SV* const sv, int const depth, bool utf8) {
174             assert(sv);
175 5719 100         if (UNLIKELY(depth <= 0)) Perl_croak(aTHX_ ERR_NESTING_EXCEEDED);
176 5715 100         SvGETMAGIC(sv);
177              
178 5715 100         if (SvPOKp(sv)) {
179 1669           STRLEN const len = SvCUR(sv);
180 1669           const char* const pv = SvPVX_const(sv);
181              
182 1669 100         if (enc->prefer_int && try_int(enc, pv, len)) {
    100          
183             return;
184             } else {
185 1652 100         if (utf8) {
186 26           msgpack_pack_str(enc, len);
187             msgpack_pack_str_body(enc, pv, len);
188             } else {
189 1626           msgpack_pack_bin(enc, len);
190             msgpack_pack_bin_body(enc, pv, len);
191             }
192             }
193 4046 100         } else if (SvNOKp(sv)) {
194 27           msgpack_pack_double(enc, (double)SvNVX(sv));
195 4019 100         } else if (SvIOKp(sv)) {
196 1249 50         if(SvUOK(sv)) {
197 0           PACK_UV(enc, SvUVX(sv));
198             } else {
199 1249           PACK_IV(enc, SvIVX(sv));
200             }
201 2770 100         } else if (SvROK(sv)) {
202 1972           _msgpack_pack_rv(aTHX_ enc, SvRV(sv), depth-1, utf8);
203 798 50         } else if (!SvOK(sv)) {
    50          
    50          
204             msgpack_pack_nil(enc);
205 0 0         } else if (isGV(sv)) {
206 0           Perl_croak(aTHX_ "msgpack cannot pack the GV\n");
207             } else {
208 0           sv_dump(sv);
209 0           Perl_croak(aTHX_ "msgpack for perl doesn't supported this type: %d\n", SvTYPE(sv));
210             }
211             }
212              
213             STATIC_INLINE
214 787           void _msgpack_pack_he(pTHX_ enc_t* enc, HV* hv, HE* he, int depth, bool utf8) {
215 787           _msgpack_pack_sv(aTHX_ enc, hv_iterkeysv(he), depth, utf8);
216 787           _msgpack_pack_sv(aTHX_ enc, hv_iterval(hv, he), depth, utf8);
217 787           }
218              
219 1972           STATIC_INLINE void _msgpack_pack_rv(pTHX_ enc_t *enc, SV* sv, int depth, bool utf8) {
220             svtype svt;
221             assert(sv);
222 1972 50         SvGETMAGIC(sv);
223 1972           svt = SvTYPE(sv);
224              
225 1972 100         if (SvOBJECT (sv)) {
226 62           HV *stash = gv_stashpv ("Data::MessagePack::Boolean", 1); // TODO: cache?
227 62 50         if (SvSTASH (sv) == stash) {
228 62 50         if (SvIV(sv)) {
    100          
229             msgpack_pack_true(enc);
230             } else {
231             msgpack_pack_false(enc);
232             }
233             } else {
234 0 0         croak ("encountered object '%s', Data::MessagePack doesn't allow the object",
235 0           SvPV_nolen(sv_2mortal(newRV_inc(sv))));
236             }
237 1910 100         } else if (svt == SVt_PVHV) {
238             HV* hval = (HV*)sv;
239 754           int count = hv_iterinit(hval);
240             HE* he;
241              
242 754 100         if (SvTIED_mg(sv,PERL_MAGIC_tied)) {
    50          
243             count = 0;
244 8 100         while (hv_iternext (hval))
245 6           ++count;
246 2           hv_iterinit (hval);
247             }
248 754           msgpack_pack_map(enc, count);
249              
250 754 100         if (enc->canonical) {
251 11           AV* const keys = newAV();
252 11           sv_2mortal((SV*)keys);
253 11           av_extend(keys, count);
254              
255 53 100         while ((he = hv_iternext(hval))) {
256 84           av_push(keys, SvREFCNT_inc(hv_iterkeysv(he)));
257             }
258              
259 11           int const len = av_len(keys) + 1;
260 11           sortsv(AvARRAY(keys), len, Perl_sv_cmp);
261              
262             int i;
263 53 100         for (i=0; i
264 42           SV* sv = *av_fetch(keys, i, TRUE);
265 42           he = hv_fetch_ent(hval, sv, FALSE, 0U);
266 42           _msgpack_pack_he(aTHX_ enc, hval, he, depth, utf8);
267             }
268             } else {
269 1488 100         while ((he = hv_iternext(hval))) {
270 745           _msgpack_pack_he(aTHX_ enc, hval, he, depth, utf8);
271             }
272             }
273 1156 50         } else if (svt == SVt_PVAV) {
274             AV* ary = (AV*)sv;
275 1156           int len = av_len(ary) + 1;
276             int i;
277 1156           msgpack_pack_array(enc, len);
278 3998 100         for (i=0; i
279 3868           SV** svp = av_fetch(ary, i, 0);
280 3868 50         if (svp) {
281 3868           _msgpack_pack_sv(aTHX_ enc, *svp, depth, utf8);
282             } else {
283             msgpack_pack_nil(enc);
284             }
285             }
286 0 0         } else if (svt < SVt_PVAV) {
287 0           STRLEN len = 0;
288 0 0         char *pv = svt ? SvPV (sv, len) : 0;
    0          
289              
290 0 0         if (len == 1 && *pv == '1')
    0          
291             msgpack_pack_true(enc);
292 0 0         else if (len == 1 && *pv == '0')
    0          
293             msgpack_pack_false(enc);
294             else {
295             //sv_dump(sv);
296 0 0         croak("cannot encode reference to scalar '%s' unless the scalar is 0 or 1",
297 0           SvPV_nolen (sv_2mortal (newRV_inc (sv))));
298             }
299             } else {
300 0 0         croak ("encountered %s, but msgpack can only represent references to arrays or hashes",
301 0           SvPV_nolen (sv_2mortal (newRV_inc (sv))));
302             }
303 946           }
304              
305 277           XS(xs_pack) {
306 554           dXSARGS;
307 277 50         if (items < 2) {
308 0           Perl_croak(aTHX_ "Usage: Data::MessagePack->pack($dat [,$max_depth])");
309             }
310              
311 277           SV* self = ST(0);
312 277           SV* val = ST(1);
313             int depth = 512;
314             bool utf8 = false;
315 277 100         if (items >= 3) depth = SvIVx(ST(2));
    50          
316              
317             enc_t enc;
318 277           enc.sv = sv_2mortal(newSV(INIT_SIZE));
319 277           enc.cur = SvPVX(enc.sv);
320 277           enc.end = SvEND(enc.sv);
321 277           SvPOK_only(enc.sv);
322              
323             // setup configuration
324             dMY_CXT;
325 277           enc.prefer_int = MY_CXT.prefer_int; // back compat
326 277 100         if(SvROK(self) && SvTYPE(SvRV(self)) == SVt_PVHV) {
    50          
327             HV* const hv = (HV*)SvRV(self);
328             SV** svp;
329              
330 44           svp = hv_fetchs(hv, "prefer_integer", FALSE);
331 44 100         if(svp) {
332 14 50         enc.prefer_int = SvTRUE(*svp) ? true : false;
    50          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    100          
    50          
    0          
    100          
    0          
333             }
334              
335 44           svp = hv_fetchs(hv, "canonical", FALSE);
336 44 100         if(svp) {
337 11 50         enc.canonical = SvTRUE(*svp) ? true : false;
    50          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
338             }
339              
340 44           svp = hv_fetchs(hv, "utf8", FALSE);
341 44 100         if (svp) {
342 24 50         utf8 = SvTRUE(*svp) ? true : false;
    50          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
343             }
344             }
345              
346 277           _msgpack_pack_sv(aTHX_ &enc, val, depth, utf8);
347              
348 273           SvCUR_set(enc.sv, enc.cur - SvPVX (enc.sv));
349 273           *SvEND (enc.sv) = 0; /* many xs functions expect a trailing 0 for text strings */
350              
351 273           ST(0) = enc.sv;
352 273           XSRETURN(1);
353             }