File Coverage

CompactProtocol.xs
Criterion Covered Total %
statement 194 200 97.0
branch 148 468 31.6
condition n/a
subroutine n/a
pod n/a
total 342 668 51.2


line stmt bran cond sub pod time code
1             MODULE = Thrift::XS PACKAGE = Thrift::XS::CompactProtocol
2              
3             # new() in parent
4              
5             #` Not a standard method, but used for tests
6             void
7             resetState(TBinaryProtocol *p)
8             CODE:
9             {
10             DEBUG_TRACE("resetState()\n");
11              
12 3           p->bool_type = -1;
13 3           p->bool_id = -1;
14 3           p->bool_value_id = -1;
15 3           p->last_field_id = 0;
16              
17             // clear field queue
18             struct field_entry *entry;
19 5 100         while (!SIMPLEQ_EMPTY(p->last_fields)) {
20             entry = SIMPLEQ_FIRST(p->last_fields);
21 2 100         SIMPLEQ_REMOVE_HEAD(p->last_fields, entries);
22             MEM_TRACE("free entry @ %p\n", entry);
23 2           Safefree(entry);
24             }
25             }
26              
27             void
28             writeMessageBegin(TBinaryProtocol *p, SV *name, int type, uint32_t seqid)
29             CODE:
30             {
31             DEBUG_TRACE("writeMessageBegin()\n");
32              
33 3           SV *namecopy = sv_mortalcopy(name); // because we can't modify the original name
34 3           sv_utf8_encode(namecopy);
35 3           uint32_t namelen = sv_len(namecopy);
36 3           SV *data = sv_2mortal(newSV(16 + namelen));
37             char tmp[5]; // 5 required for varint32
38              
39             // byte protocol ID
40 3           tmp[0] = PROTOCOL_ID;
41              
42             // byte version/type
43 3           tmp[1] = (VERSION_N & VERSION_MASK_COMPACT) | ((type << TYPE_SHIFT_AMOUNT) & TYPE_MASK);
44              
45 3           sv_setpvn(data, tmp, 2);
46              
47             // varint32 seqid
48             int varlen;
49 5 100         UINT_TO_VARINT(varlen, tmp, seqid, 0);
50 3           sv_catpvn(data, tmp, varlen);
51              
52             // varint32 len + string
53 3 50         UINT_TO_VARINT(varlen, tmp, namelen, 0);
54 3           sv_catpvn(data, tmp, varlen);
55 3           sv_catsv(data, namecopy);
56              
57 3 50         WRITE_SV(p, data);
    0          
    0          
    0          
    0          
58             }
59              
60             # writeMessageEnd in parent
61              
62             void
63             writeStructBegin(TBinaryProtocol *p, SV *)
64             CODE:
65             {
66             DEBUG_TRACE("writeStructBegin()\n");
67              
68             // No writing here, but we push last_field_id onto the fields stack
69             // and reset last_field_id to 0
70             struct field_entry *entry;
71 5           New(0, entry, sizeof(struct field_entry), struct field_entry);
72             MEM_TRACE("New entry @ %p\n", entry);
73 5           entry->field_id = p->last_field_id;
74 5 50         SIMPLEQ_INSERT_HEAD(p->last_fields, entry, entries);
75              
76             DEBUG_TRACE("writeStructBegin(), insert_head %d\n", entry->field_id);
77              
78 5           p->last_field_id = 0;
79             }
80              
81             void
82             writeStructEnd(TBinaryProtocol *p)
83             CODE:
84             {
85             DEBUG_TRACE("writeStructEnd()\n");
86              
87             // pop last field off the stack and save it in
88             // last_field_id
89 4           struct field_entry *entry = SIMPLEQ_FIRST(p->last_fields);
90 4           p->last_field_id = entry->field_id;
91 4 50         SIMPLEQ_REMOVE_HEAD(p->last_fields, entries);
92              
93             DEBUG_TRACE("writeStructEnd(), remove_head %d\n", entry->field_id);
94              
95             MEM_TRACE("free entry @ %p\n", entry);
96 4           Safefree(entry);
97             }
98              
99             void
100             writeFieldBegin(TBinaryProtocol *p, SV *name, int type, int id)
101             CODE:
102             {
103             DEBUG_TRACE("writeFieldBegin()\n");
104              
105             PERL_UNUSED_VAR(name);
106 10 100         if (unlikely(type == T_BOOL)) {
107             // Special case, save type/id for use later
108 3           p->bool_type = type;
109 3           p->bool_id = id;
110             }
111             else {
112 7           write_field_begin_internal(p, type, id, -1);
113             }
114             }
115              
116             # writeFieldEnd in parent
117              
118             # writeFieldStop in parent
119              
120             void
121             writeMapBegin(TBinaryProtocol *p, int keytype, int valtype, uint32_t size)
122             CODE:
123             {
124             DEBUG_TRACE("writeMapBegin()\n");
125              
126             char data[6];
127              
128 5 100         if (size == 0) {
129 1           data[0] = 0;
130 1 50         WRITE(p, data, 1);
    0          
    0          
    0          
    0          
131             }
132             else {
133             int varlen;
134 4 50         UINT_TO_VARINT(varlen, data, size, 0);
135 4           data[varlen] = (get_compact_type(keytype) << 4) | get_compact_type(valtype);
136 4 50         WRITE(p, data, varlen + 1);
    0          
    0          
    0          
    0          
137             }
138             }
139              
140             # writeMapEnd in parent
141              
142             void
143             writeListBegin(TBinaryProtocol *p, int elemtype, int size)
144             CODE:
145             {
146             DEBUG_TRACE("writeListBegin()\n");
147              
148 5           write_collection_begin_internal(p, elemtype, size);
149             }
150              
151             # writeListEnd in parent
152              
153             void
154             writeSetBegin(TBinaryProtocol *p, int elemtype, int size)
155             CODE:
156             {
157             DEBUG_TRACE("writeSetBegin()\n");
158              
159 2           write_collection_begin_internal(p, elemtype, size);
160             }
161              
162             # writeSetEnd in parent
163              
164             void
165             writeBool(TBinaryProtocol *p, SV *value)
166             CODE:
167             {
168             DEBUG_TRACE("writeBool()\n");
169              
170 5 100         if (unlikely(p->bool_type != -1)) {
171             // we haven't written the field header yet
172 5 100         write_field_begin_internal(p, p->bool_type, p->bool_id, SvTRUE(value) ? CTYPE_BOOLEAN_TRUE: CTYPE_BOOLEAN_FALSE);
173 3           p->bool_type = -1;
174 3           p->bool_id = -1;
175             }
176             else {
177             // we're not part of a field, so just write the value.
178             char data[1];
179 2           data[0] = SvTRUE(value) ? 1 : 0;
180              
181 2 50         WRITE(p, data, 1);
    0          
    0          
    0          
    0          
182             }
183             }
184              
185             # writeByte in parent
186              
187             void
188             writeI16(TBinaryProtocol *p, int value)
189             CODE:
190             {
191             DEBUG_TRACE("writeI16()\n");
192              
193             char data[3];
194             int varlen;
195              
196             uint32_t uvalue = int_to_zigzag(value);
197 7 100         UINT_TO_VARINT(varlen, data, uvalue, 0);
198 3 50         WRITE(p, data, varlen);
    0          
    0          
    0          
    0          
199             }
200              
201             void
202             writeI32(TBinaryProtocol *p, int value)
203             CODE:
204             {
205             DEBUG_TRACE("writeI32()\n");
206              
207             char data[5];
208             int varlen;
209              
210             uint32_t uvalue = int_to_zigzag(value);
211 15 100         UINT_TO_VARINT(varlen, data, uvalue, 0);
212 4 50         WRITE(p, data, varlen);
    0          
    0          
    0          
    0          
213             }
214              
215             void
216             writeI64(TBinaryProtocol *p, SV *value)
217             CODE:
218             {
219             char data[10];
220             int varlen = 0;
221              
222             // Stringify the value, then convert to an int64_t
223 5 100         const char *str = SvPOK(value) ? SvPVX(value) : SvPV_nolen(value);
224 5           int64_t i64 = (int64_t)strtoll(str, NULL, 10);
225              
226             DEBUG_TRACE("writeI64(%" PRId64 ")\n", i64);
227              
228             uint64_t uvalue = ll_to_zigzag(i64);
229 30 100         UINT_TO_VARINT(varlen, data, uvalue, 0);
230 5 50         WRITE(p, data, varlen);
    0          
    0          
    0          
    0          
231             }
232              
233             void
234             writeDouble(TBinaryProtocol *p, SV *value)
235             CODE:
236             {
237             DEBUG_TRACE("writeDouble()\n");
238              
239             char data[8];
240             union {
241             double d;
242             int64_t i;
243             } u;
244              
245 2           u.d = (double)SvNV(value);
246              
247 2           data[0] = u.i & 0xff;
248 2           data[1] = (u.i >> 8) & 0xff;
249 2           data[2] = (u.i >> 16) & 0xff;
250 2           data[3] = (u.i >> 24) & 0xff;
251 2           data[4] = (u.i >> 32) & 0xff;
252 2           data[5] = (u.i >> 40) & 0xff;
253 2           data[6] = (u.i >> 48) & 0xff;
254 2           data[7] = (u.i >> 56) & 0xff;
255              
256 2 50         WRITE(p, data, 8);
    0          
    0          
    0          
    0          
257             }
258              
259             void
260             writeString(TBinaryProtocol *p, SV *value)
261             CODE:
262             {
263             DEBUG_TRACE("writeString()\n");
264              
265 19           SV *valuecopy = sv_mortalcopy(value);
266 19 100         if (SvUTF8(value) != 0) {
267 2           sv_utf8_encode(valuecopy);
268             }
269 19           int len = sv_len(valuecopy);
270 19           SV *data = sv_2mortal(newSV(5 + len));
271             char tmp[5];
272              
273             int varlen;
274 20 100         UINT_TO_VARINT(varlen, tmp, len, 0);
275 19           sv_setpvn(data, tmp, varlen);
276 19           sv_catsv(data, valuecopy);
277              
278 19 50         WRITE_SV(p, data);
    0          
    0          
    0          
    0          
279             }
280              
281             void
282             readMessageBegin(TBinaryProtocol *p, SV *name, SV *type, SV *seqid)
283             CODE:
284             {
285             DEBUG_TRACE("readMessageBegin()\n");
286              
287             SV *tmp;
288             char *tmps;
289             uint32_t tmpui;
290              
291             // read protocol id, version, type
292 1 50         READ_SV(p, tmp, 2);
    50          
    0          
    0          
    0          
    0          
293 1           tmps = SvPVX(tmp);
294              
295 1           uint8_t protocol_id = (uint8_t) tmps[0];
296 1 50         if (protocol_id != (uint8_t) PROTOCOL_ID) {
297 0           THROW_SV("Thrift::TException", newSVpvf("Expected protocol id %d but got %d", PROTOCOL_ID, protocol_id));
298             }
299              
300 1           uint8_t version_and_type = (uint8_t) tmps[1];
301             uint8_t version = version_and_type & VERSION_MASK_COMPACT;
302 1 50         if (version != VERSION_N) {
303 0           THROW_SV("Thrift::TException", newSVpvf("Expected version id %d but got %d", VERSION_N, version));
304             }
305              
306             // set type
307 1 50         if (SvROK(type))
308 1           sv_setiv(SvRV(type), (version_and_type >> TYPE_SHIFT_AMOUNT) & 0x03);
309              
310             // read/set seqid
311 2 50         READ_VARINT(p, tmpui);
    50          
    50          
    0          
    0          
    0          
    0          
    100          
312 1 50         if (SvROK(seqid))
313 1           sv_setiv(SvRV(seqid), tmpui);
314              
315             // read/set name
316 1 50         READ_VARINT(p, tmpui);
    50          
    50          
    0          
    0          
    0          
    0          
    50          
317 1 50         if (tmpui) {
318 1 50         READ_SV(p, tmp, tmpui);
    50          
    0          
    0          
    0          
    0          
319 1           sv_utf8_decode(tmp);
320 1 50         if (SvROK(name))
321 1           sv_setsv(SvRV(name), tmp);
322             }
323             else {
324 0 0         if (SvROK(name))
325 0           sv_setpv(SvRV(name), "");
326             }
327             }
328              
329             # readMessageEnd in parent
330              
331             void
332             readStructBegin(TBinaryProtocol *p, SV *name)
333             CODE:
334             {
335             DEBUG_TRACE("readStructBegin()\n");
336              
337             // No reading here, but we push last_field_id onto the fields stack
338             struct field_entry *entry;
339 4           New(0, entry, sizeof(struct field_entry), struct field_entry);
340             MEM_TRACE("New entry @ %p\n", entry);
341 4           entry->field_id = p->last_field_id;
342 4 100         SIMPLEQ_INSERT_HEAD(p->last_fields, entry, entries);
343              
344 4           p->last_field_id = 0;
345              
346 4 50         if (SvROK(name))
347 4           sv_setpv(SvRV(name), "");
348             }
349              
350             void
351             readStructEnd(TBinaryProtocol *p)
352             CODE:
353             {
354             DEBUG_TRACE("readStructEnd()\n");
355              
356             // pop last field off the stack
357 3           struct field_entry *entry = SIMPLEQ_FIRST(p->last_fields);
358 3 50         SIMPLEQ_REMOVE_HEAD(p->last_fields, entries);
359 3           p->last_field_id = entry->field_id;
360              
361             MEM_TRACE("free entry @ %p\n", entry);
362 3           Safefree(entry);
363             }
364              
365             void
366             readFieldBegin(TBinaryProtocol *p, SV *name, SV *fieldtype, SV *fieldid)
367             CODE:
368             {
369             DEBUG_TRACE("readFieldBegin()\n");
370              
371             SV *tmp;
372             char *tmps;
373              
374             PERL_UNUSED_VAR(name);
375             // fieldtype byte
376 10 50         READ_SV(p, tmp, 1);
    50          
    0          
    0          
    0          
    0          
377 10           tmps = SvPVX(tmp);
378 10           int type = tmps[0];
379              
380 10 50         if (SvROK(fieldtype))
381 10           sv_setiv(SvRV(fieldtype), get_ttype(type & 0x0f));
382              
383 10 100         if (type == T_STOP) {
384 3 50         if (SvROK(fieldid))
385 3           sv_setiv(SvRV(fieldid), 0);
386 3           XSRETURN_EMPTY;
387             }
388              
389             // fieldid i16 varint
390             int fid;
391              
392             // mask off the 4 MSB of the type header. it could contain a field id delta.
393 7           uint8_t modifier = ((type & 0xf0) >> 4);
394 7 100         if (modifier == 0) {
395             // pop field
396 2           struct field_entry *entry = SIMPLEQ_FIRST(p->last_fields);
397 2 100         SIMPLEQ_REMOVE_HEAD(p->last_fields, entries);
398             MEM_TRACE("free entry @ %p\n", entry);
399 2           Safefree(entry);
400              
401             // not a delta. look ahead for the zigzag varint field id.
402 3 50         READ_VARINT(p, fid);
    50          
    50          
    0          
    0          
    0          
    0          
    100          
403 2           fid = zigzag_to_int(fid);
404             }
405             else {
406             // has a delta. add the delta to the last read field id.
407             // pop field
408 5           struct field_entry *entry = SIMPLEQ_FIRST(p->last_fields);
409 5 50         SIMPLEQ_REMOVE_HEAD(p->last_fields, entries);
410 5           int last = entry->field_id;
411             MEM_TRACE("free entry @ %p\n", entry);
412 5           Safefree(entry);
413              
414 5           fid = last + modifier;
415             }
416              
417             // if this happens to be a boolean field, the value is encoded in the type
418 7 100         if (is_bool_type(type)) {
419             // save the boolean value in a special instance variable.
420 1           p->bool_value_id = (type & 0x0f) == CTYPE_BOOLEAN_TRUE ? 1 : 0;
421             }
422              
423             // push the new field onto the field stack so we can keep the deltas going.
424             struct field_entry *entry;
425 7           New(0, entry, sizeof(struct field_entry), struct field_entry);
426             MEM_TRACE("New entry @ %p\n", entry);
427 7           entry->field_id = fid;
428 7 100         SIMPLEQ_INSERT_HEAD(p->last_fields, entry, entries);
429              
430 7 50         if (SvROK(fieldid))
431 7           sv_setiv(SvRV(fieldid), fid);
432             }
433              
434             # readFieldEnd in parent
435              
436             void
437             readMapBegin(TBinaryProtocol *p, SV *keytype, SV *valtype, SV *size)
438             CODE:
439             {
440             DEBUG_TRACE("readMapBegin()\n");
441              
442             SV *tmp;
443             char *tmps;
444              
445             // size
446             uint32_t isize;
447 3 50         READ_VARINT(p, isize);
    50          
    50          
    0          
    0          
    0          
    0          
    50          
448 3 50         if (SvROK(size))
449 3           sv_setiv(SvRV(size), isize);
450              
451             // key and value type
452 3 50         READ_SV(p, tmp, 1);
    50          
    0          
    0          
    0          
    0          
453 3           tmps = SvPVX(tmp);
454              
455             // keytype byte
456 3 50         if (SvROK(keytype))
457 3           sv_setiv(SvRV(keytype), get_ttype((tmps[0] >> 4) & 0xf));
458              
459             // valtype byte
460 3 50         if (SvROK(valtype))
461 3           sv_setiv(SvRV(valtype), get_ttype(tmps[0] & 0xf));
462             }
463              
464             # readMapEnd in parent
465              
466             void
467             readListBegin(TBinaryProtocol *p, SV *elemtype, SV *size)
468             CODE:
469             {
470             DEBUG_TRACE("readListBegin()\n");
471              
472             SV *tmp;
473             char *tmps;
474              
475             // size and type may be in the same byte
476 3 50         READ_SV(p, tmp, 1);
    50          
    0          
    0          
    0          
    0          
477 3           tmps = SvPVX(tmp);
478 3           int isize = (tmps[0] >> 4) & 0x0f;
479 3 100         if (isize == 15) {
480             // size is in a varint
481 2 50         READ_VARINT(p, isize);
    50          
    50          
    0          
    0          
    0          
    0          
    100          
482             }
483 3           int type = get_ttype(tmps[0] & 0x0f);
484              
485             // elemtype byte
486 3 50         if (SvROK(elemtype))
487 3           sv_setiv(SvRV(elemtype), type);
488              
489             // size
490 3 50         if (SvROK(size))
491 3           sv_setiv(SvRV(size), isize);
492             }
493              
494             # readListEnd in parent
495              
496             void
497             readSetBegin(TBinaryProtocol *p, SV *elemtype, SV *size)
498             CODE:
499             {
500             DEBUG_TRACE("readSetBegin()\n");
501              
502             SV *tmp;
503             char *tmps;
504              
505             // size and type may be in the same byte
506 1 50         READ_SV(p, tmp, 1);
    50          
    0          
    0          
    0          
    0          
507 1           tmps = SvPVX(tmp);
508 1           int isize = (tmps[0] >> 4) & 0x0f;
509 1 50         if (isize == 15) {
510             // size is in a varint
511 2 50         READ_VARINT(p, isize);
    50          
    50          
    0          
    0          
    0          
    0          
    100          
512             }
513 1           int type = get_ttype(tmps[0] & 0x0f);
514              
515             // elemtype byte
516 1 50         if (SvROK(elemtype))
517 1           sv_setiv(SvRV(elemtype), type);
518              
519             // size
520 1 50         if (SvROK(size))
521 1           sv_setiv(SvRV(size), isize);
522             }
523              
524             # readSetEnd in parent
525              
526             void
527             readBool(TBinaryProtocol *p, SV *value)
528             CODE:
529             {
530             DEBUG_TRACE("readBool()\n");
531              
532             SV *tmp;
533             char *tmps;
534              
535             // Check for bool_value encoded in the fieldBegin type
536 3 100         if (p->bool_value_id != -1) {
537 1 50         if (SvROK(value))
538 1           sv_setiv(SvRV(value), p->bool_value_id);
539 1           p->bool_value_id = -1;
540             }
541             else {
542 2 50         READ_SV(p, tmp, 1);
    50          
    0          
    0          
    0          
    0          
543 2           tmps = SvPVX(tmp);
544              
545 2 50         if (SvROK(value))
546 2           sv_setiv(SvRV(value), tmps[0] ? 1 : 0);
547             }
548             }
549              
550             # readByte in parent
551              
552             void
553             readI16(TBinaryProtocol *p, SV *value)
554             CODE:
555             {
556             DEBUG_TRACE("readI16()\n");
557              
558             uint32_t varint;
559 3 50         READ_VARINT(p, varint);
    50          
    50          
    0          
    0          
    0          
    0          
    100          
560              
561 1 50         if (SvROK(value))
562 1           sv_setiv(SvRV(value), zigzag_to_int(varint));
563             }
564              
565             void
566             readI32(TBinaryProtocol *p, SV *value)
567             CODE:
568             {
569             DEBUG_TRACE("readI32()\n");
570              
571             uint32_t varint;
572 6 50         READ_VARINT(p, varint);
    50          
    50          
    0          
    0          
    0          
    0          
    100          
573              
574 2 50         if (SvROK(value))
575 2           sv_setiv(SvRV(value), zigzag_to_int(varint));
576             }
577              
578             void
579             readI64(TBinaryProtocol *p, SV *value)
580             CODE:
581             {
582             uint64_t varint;
583 7 50         READ_VARINT(p, varint);
    50          
    50          
    0          
    0          
    0          
    0          
    100          
584              
585             DEBUG_TRACE("readI64(%" PRId64 ") (from varint %" PRIu64 ")\n", zigzag_to_ll(varint), varint);
586              
587             // Store as a string so it works on 32-bit platforms
588 1 50         if (SvROK(value)) {
589             char string[25];
590             STRLEN length;
591 1           length = sprintf(string, "%" PRId64, zigzag_to_ll(varint));
592 1           sv_setpvn(SvRV(value), string, length);
593             }
594             }
595              
596             void
597             readDouble(TBinaryProtocol *p, SV *value)
598             CODE:
599             {
600             DEBUG_TRACE("readDouble()\n");
601              
602             SV *tmp;
603             char *tmps;
604              
605 1 50         READ_SV(p, tmp, 8);
    50          
    0          
    0          
    0          
    0          
606 1           tmps = SvPVX(tmp);
607              
608 1           uint32_t lo = (uint8_t)tmps[0] |
609 1           ((uint8_t)tmps[1] << 8) |
610 1           ((uint8_t)tmps[2] << 16) |
611 1           ((uint8_t)tmps[3] << 24);
612 1           uint64_t hi = (uint8_t)tmps[4] |
613 1           ((uint8_t)tmps[5] << 8) |
614 1           ((uint8_t)tmps[6] << 16) |
615 1           ((uint8_t)tmps[7] << 24);
616              
617             union {
618             double d;
619             int64_t i;
620             } u;
621 1           u.i = (hi << 32) | lo;
622              
623 1 50         if (SvROK(value))
624 1           sv_setnv(SvRV(value), u.d);
625             }
626              
627             void
628             readString(TBinaryProtocol *p, SV *value)
629             CODE:
630             {
631             DEBUG_TRACE("readString()\n");
632              
633             SV *tmp;
634              
635             uint64_t len;
636 15 50         READ_VARINT(p, len);
    50          
    50          
    0          
    0          
    0          
    0          
    50          
637 15 50         if (len) {
638 15 50         READ_SV(p, tmp, len);
    50          
    0          
    0          
    0          
    0          
639 15           sv_utf8_decode(tmp);
640 15 50         if (SvROK(value))
641 15           sv_setsv(SvRV(value), tmp);
642             }
643             else {
644 0 0         if (SvROK(value))
645 0           sv_setpv(SvRV(value), "");
646             }
647             }