File Coverage

encode.c
Criterion Covered Total %
statement 352 381 92.3
branch 145 188 77.1
condition n/a
subroutine n/a
pod n/a
total 497 569 87.3


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5              
6             #include
7             #include "define.h"
8             #include "proto.h"
9             #include "swap.h"
10             #include "encode.h"
11             #include "cc_bignum.h"
12              
13             void encode_tinyint(pTHX_ SV *dest, SV *src);
14             void encode_smallint(pTHX_ SV *dest, SV *src);
15             void encode_int(pTHX_ SV *dest, SV *src);
16             void encode_bigint(pTHX_ SV *dest, SV *src);
17             void encode_blob(pTHX_ SV *dest, SV *src);
18             void encode_float(pTHX_ SV *dest, SV *src);
19             void encode_double(pTHX_ SV *dest, SV *src);
20             void encode_boolean(pTHX_ SV *dest, SV *src);
21             void encode_uuid(pTHX_ SV *dest, SV *src);
22             void encode_inet(pTHX_ SV *dest, SV *src);
23             void encode_time(pTHX_ SV *dest, SV *src);
24             void encode_date(pTHX_ SV *dest, SV *src);
25             void encode_varint(pTHX_ SV *dest, SV *src, int* int_out);
26             void encode_decimal(pTHX_ SV *dest, SV *src);
27             void encode_undef(pTHX_ SV *dest);
28              
29             void encode_list(pTHX_ SV *dest, SV *src, struct cc_type *type);
30             void encode_map(pTHX_ SV *dest, SV *src, struct cc_type *type);
31             void encode_tuple(pTHX_ SV *dest, SV *src, struct cc_type *type);
32             void encode_udt(pTHX_ SV *dest, SV *src, struct cc_type *type);
33              
34 370           void encode_cell(pTHX_ SV *dest, SV *src, struct cc_type *type)
35             {
36             assert(dest && type);
37              
38 370 50         if (!src || !SvOK(src)) {
    100          
39 17           encode_undef(aTHX_ dest);
40 17           return;
41             }
42              
43 353           switch (type->type_id) {
44 20           case CC_TYPE_SMALLINT:
45 20           encode_smallint(aTHX_ dest, src);
46 20           break;
47              
48 47           case CC_TYPE_INT:
49 47           encode_int(aTHX_ dest, src);
50 47           break;
51              
52 20           case CC_TYPE_TINYINT:
53 20           encode_tinyint(aTHX_ dest, src);
54 20           break;
55              
56 23           case CC_TYPE_BIGINT:
57             case CC_TYPE_TIMESTAMP:
58             case CC_TYPE_COUNTER:
59 23           encode_bigint(aTHX_ dest, src);
60 23           break;
61              
62 18           case CC_TYPE_ASCII:
63             case CC_TYPE_CUSTOM:
64             case CC_TYPE_BLOB:
65             case CC_TYPE_VARCHAR:
66             case CC_TYPE_TEXT:
67 18           encode_blob(aTHX_ dest, src);
68 18           break;
69              
70 5           case CC_TYPE_FLOAT:
71 5           encode_float(aTHX_ dest, src);
72 5           break;
73              
74 5           case CC_TYPE_DOUBLE:
75 5           encode_double(aTHX_ dest, src);
76 5           break;
77              
78 14           case CC_TYPE_INET:
79 14           encode_inet(aTHX_ dest, src);
80 14           break;
81              
82 20           case CC_TYPE_BOOLEAN:
83 20           encode_boolean(aTHX_ dest, src);
84 20           break;
85              
86 50           case CC_TYPE_TIME:
87 50           encode_time(aTHX_ dest, src);
88 50           break;
89              
90 69           case CC_TYPE_DATE:
91 69           encode_date(aTHX_ dest, src);
92 69           break;
93              
94 9           case CC_TYPE_DECIMAL:
95 9           encode_decimal(aTHX_ dest, src);
96 9           break;
97              
98 21           case CC_TYPE_VARINT:
99 21           encode_varint(aTHX_ dest, src, NULL);
100 21           break;
101              
102 13           case CC_TYPE_LIST:
103             case CC_TYPE_SET:
104 13           encode_list(aTHX_ dest, src, type);
105 13           break;
106              
107 9           case CC_TYPE_MAP:
108 9           encode_map(aTHX_ dest, src, type);
109 9           break;
110              
111 3           case CC_TYPE_TUPLE:
112 3           encode_tuple(aTHX_ dest, src, type);
113 3           break;
114              
115 5           case CC_TYPE_UUID:
116             case CC_TYPE_TIMEUUID:
117 5           encode_uuid(aTHX_ dest, src);
118 5           break;
119              
120 2           case CC_TYPE_UDT:
121 2           encode_udt(aTHX_ dest, src, type);
122 2           break;
123              
124 0           default:
125 0           warn("Encoder for type %d not implemented yet. Sending undef instead.", type->type_id);
126 0           encode_undef(aTHX_ dest);
127 0           break;
128             }
129             }
130              
131 20           void encode_tinyint(pTHX_ SV *dest, SV *src)
132             {
133             unsigned char bytes[5];
134             int number;
135              
136 20           number = SvIV(src);
137 20 100         if (number > 127 || number < -128) {
    100          
138 2           warn("encode_tinyint: number '%s' out of range", SvPV_nolen(src));
139             }
140              
141 20           memset(bytes, 0, 3);
142 20           bytes[3] = 1;
143 20           bytes[4] = number;
144 20           sv_catpvn(dest, (char*)bytes, 5);
145 20           }
146              
147 20           void encode_smallint(pTHX_ SV *dest, SV *src)
148             {
149             union {
150             uint16_t s[3];
151             unsigned char c[6];
152             } stuff;
153 20           memset(stuff.c, 0, 3);
154 20           stuff.c[3] = 2;
155 20           stuff.s[2] = htons((int16_t)SvIV(src));
156 20           sv_catpvn(dest, (char*)stuff.c, 6);
157 20           }
158              
159 47           void encode_int(pTHX_ SV *dest, SV *src)
160             {
161             union {
162             uint32_t s[2];
163             unsigned char c[8];
164             } stuff;
165 47           memset(stuff.c, 0, 3);
166 47           stuff.c[3] = 4;
167 47           stuff.s[1] = htonl((int32_t)SvIV(src));
168 47           sv_catpvn(dest, (char*)stuff.c, 8);
169 47           }
170              
171             #ifdef CAN_64BIT
172 23           void encode_bigint(pTHX_ SV *dest, SV *src)
173             {
174             unsigned char work[12];
175             union {
176             int64_t iv;
177             unsigned char c[8];
178             } stuff;
179 23           stuff.iv = SvIV(src);
180              
181 23           work[0] = 0;
182 23           work[1] = 0;
183 23           work[2] = 0;
184 23           work[3] = 8;
185 23           bswap8(stuff.c);
186 23           memcpy(work+4, stuff.c, 8);
187 23           sv_catpvn(dest, (char*)work, 12);
188 23           }
189             #else
190             void encode_bigint(pTHX_ SV *dest, SV *src)
191             {
192             SV *tmp_sv;
193             int sv_len;
194             char *ptr;
195             unsigned char work[12];
196              
197             work[0] = 0;
198             work[1] = 0;
199             work[2] = 0;
200             work[3] = 8;
201              
202             tmp_sv = sv_2mortal(newSV(8));
203             SvPOK_on(tmp_sv);
204             SvCUR_set(tmp_sv, 0);
205              
206             encode_varint(aTHX_ tmp_sv, src, &sv_len);
207             if (UNLIKELY(sv_len > 8)) {
208             /* Unlike our 64bit code, we have the chance to actually detect wrapping.
209             * So if you're "lucky" and run a 32bit Perl, enjoy a warning on top of
210             * the wrapping you probably didn't want to happen. */
211             warn("Truncating scalar value: does not fit bigint");
212             sv_chop(tmp_sv, SvPV_nolen(tmp_sv)+(sv_len-8));
213             sv_len = 8;
214             }
215             assert(sv_len > 0);
216             ptr = SvPV_nolen(tmp_sv);
217             if (ptr[0] & 0x80) {
218             /* Negative */
219             memset(work+4, 0xff, 8);
220             } else {
221             /* Positive */
222             memset(work+4, 0, 8);
223             }
224             memcpy(work+4+(8-sv_len), ptr, sv_len);
225              
226             sv_catpvn(dest, (char*)work, 12);
227             }
228             #endif
229              
230 18           void encode_blob(pTHX_ SV *dest, SV *src)
231             {
232             char *ptr;
233             STRLEN length;
234              
235 18           ptr = SvPV(src, length);
236 18 50         if (UNLIKELY(length > INT32_MAX))
237 0           croak("cannot encode blob/string: size exceeds 2GB");
238              
239 18           pack_int(aTHX_ dest, length);
240 18           sv_catpvn(dest, ptr, length);
241 18           }
242              
243 5           void encode_float(pTHX_ SV *dest, SV *src)
244             {
245             unsigned char work[8];
246             union {
247             float f;
248             unsigned char c[8];
249             } stuff;
250 5           stuff.f = SvNV(src);
251 5           work[0] = 0;
252 5           work[1] = 0;
253 5           work[2] = 0;
254 5           work[3] = 4;
255 5           bswap4(stuff.c);
256 5           memcpy(work+4, stuff.c, 4);
257 5           sv_catpvn(dest, (char*)work, 8);
258 5           }
259              
260 5           void encode_double(pTHX_ SV *dest, SV *src)
261             {
262             unsigned char work[12];
263             union {
264             double d;
265             unsigned char c[8];
266             } stuff;
267 5           stuff.d = SvNV(src);
268 5           work[0] = 0;
269 5           work[1] = 0;
270 5           work[2] = 0;
271 5           work[3] = 8;
272 5           bswap8(stuff.c);
273 5           memcpy(work+4, stuff.c, 8);
274 5           sv_catpvn(dest, (char*)work, 12);
275 5           }
276              
277 20           void encode_boolean(pTHX_ SV *dest, SV *src)
278             {
279             unsigned char bytes[5];
280 20           memset(bytes, 0, 3);
281 20           bytes[3] = 1;
282 20 100         if (SvTRUE(src)) {
283 11           bytes[4] = 1;
284             } else {
285 9           bytes[4] = 0;
286             }
287 20           sv_catpvn(dest, (char*)bytes, 5);
288 20           }
289              
290 5           void encode_uuid(pTHX_ SV *dest, SV *src)
291             {
292             char *ptr;
293             STRLEN size;
294             int i, j;
295             unsigned char work[20];
296 5           memset(work, 0, 20);
297              
298 5           work[3] = 16;
299              
300 5           ptr = SvPV(src, size);
301 5           j = 0;
302 5           i = 0;
303 185 100         while (j < 32 && i < size) {
    50          
304 180           char c = ptr[i++];
305 180 100         if (c >= '0' && c <= '9') {
    100          
306 100           c -= '0';
307 80 100         } else if (c >= 'a' && c <= 'f') {
    50          
308 60           c -= 'a' - 10;
309 20 50         } else if (c >= 'A' && c <= 'F') {
    0          
310 0           c -= 'A' - 10;
311             } else {
312 20           continue;
313             }
314              
315 160 100         if (!(j%2))
316 80           c <<= 4;
317 160           work[4 + (j/2)] |= c;
318 160           j++;
319             }
320              
321 5 50         if (j != 32)
322 0           warn("UUID '%s' is invalid", ptr);
323              
324 5           sv_catpvn(dest, (char*)work, 20);
325 5           }
326              
327 14           void encode_inet(pTHX_ SV *dest, SV *src)
328             {
329             char *ptr;
330             STRLEN size;
331             int semicolon, i;
332             unsigned char out[20];
333              
334 14           ptr = SvPV(src, size);
335 14           semicolon = 0;
336              
337 134 100         for (i = 0; i < size; i++) {
338 120 100         if (ptr[i] == ':')
339 24           semicolon++;
340             }
341              
342 14           memset(out, 0, 20);
343              
344 14 100         if (semicolon) { /* IPv6 */
345 9           out[3] = 16;
346              
347 9 50         if (inet_pton(AF_INET6, ptr, out+4)) {
348 9           sv_catpvn(dest, (char*)out, 20);
349             } else {
350 0           warn("Inet address '%s' is invalid", ptr);
351 0           encode_undef(aTHX_ dest);
352             }
353             } else {
354 5           out[3] = 4;
355              
356 5 50         if (inet_pton(AF_INET, ptr, out+4)) {
357 5           sv_catpvn(dest, (char*)out, 8);
358             } else {
359 0           warn("Inet address '%s' is invalid", ptr);
360 0           encode_undef(aTHX_ dest);
361             }
362             }
363 14           }
364              
365 50           void encode_time(pTHX_ SV *dest, SV *src)
366             {
367             unsigned char out[12];
368             STRLEN size, i, j, k;
369             char *ptr;
370             int numbers[4];
371             int seconds, nano;
372              
373 50           memset(out, 0, 12);
374 50           out[3] = 8;
375              
376 50           numbers[0] = numbers[1] = numbers[2] = numbers[3] = 0;
377              
378 50           ptr = SvPV(src, size);
379 826 100         for (i = 0, j = 0; i < size; i++) {
380 776 100         if (ptr[i] == ':' || ptr[i] == '.') {
    100          
381 150           j++;
382 150           k = 0;
383 150 50         if (j > 3)
384 0           croak("Time '%s' is invalid", ptr);
385 626 50         } else if (ptr[i] >= '0' && ptr[i] <= '9') {
    50          
386 626           numbers[j] *= 10;
387 626           numbers[j] += ptr[i]-'0';
388 626           k++;
389             }
390             }
391 50 50         if (j == 3 && numbers[3] > 0) {
    100          
392 145 100         for (; k<9; k++)
393 96           numbers[3] *= 10;
394             }
395              
396 50           nano = numbers[3];
397 50           seconds = (((numbers[0]%24) * 3600) + (numbers[1] * 60) + numbers[2]) % 86400;
398 50           *((int64_t*)(out+4)) = (((int64_t)seconds)*1000000000L) + (int64_t)nano;
399 50           bswap8(out+4);
400 50           sv_catpvn(dest, (char*)out, 12);
401 50           }
402              
403 276           static inline int div_properly(int a, int b)
404             {
405 276           int n = a / b;
406 276 100         if (a < 0 && a%b != 0)
    50          
407 3           n--;
408 276           return n;
409             }
410              
411 69           void encode_date(pTHX_ SV *dest, SV *src)
412             {
413             int negative_year, numbers[3], i, v_a, y, m, jdn;
414             char *ptr;
415             STRLEN size, pos;
416              
417 69           numbers[0] = numbers[1] = numbers[2] = 0;
418              
419 69           ptr = SvPV(src, size);
420 69 50         if (UNLIKELY(size < 5))
421 0           croak("Date '%s' is invalid", ptr);
422              
423 69           pos = 0;
424 69 100         if (ptr[pos] == '-') {
425 1           pos++;
426 1           negative_year = 1;
427             } else {
428 68           negative_year = 0;
429             }
430              
431 69           i = 0;
432 711 100         while (pos < size) {
433 642 100         if (ptr[pos] == '-') {
434 138           i++;
435 138 50         if (UNLIKELY(i >= 3))
436 0           croak("Date '%s' is invalid", ptr);
437 504 50         } else if (ptr[pos] >= '0' && ptr[pos] <= '9') {
    50          
438 504           numbers[i] *= 10;
439 504           numbers[i] += ptr[pos] - '0';
440             } else {
441 0           croak("Date '%s' is invalid", ptr);
442             }
443              
444 642           pos++;
445             }
446              
447 69 100         if (negative_year)
448 1           numbers[0] *= -1;
449              
450 69 100         v_a = (numbers[1] == 1 || numbers[1] == 2) ? 1 : 0;
    100          
451 69           y = numbers[0] + 4800 - v_a;
452 69           m = numbers[1] + (12 * v_a) - 3;
453 69           jdn = 1 << 31;
454 69           jdn -= 2472633;
455 69           jdn += numbers[2];
456 69           jdn += div_properly((153 * m) + 2, 5);
457 69           jdn += 365 * y;
458 69           jdn += div_properly(y, 4);
459 69           jdn -= div_properly(y, 100);
460 69           jdn += div_properly(y, 400);
461 69           pack_int(aTHX_ dest, 4);
462 69           pack_int(aTHX_ dest, jdn);
463 69           }
464              
465 30           void encode_varint(pTHX_ SV *dest, SV *src, int* int_out)
466             {
467             char *ptr;
468             STRLEN size;
469              
470 30           ptr = SvPV(src, size);
471             #ifdef CAN_64BIT
472 30 100         if (size <= 18) {
473             int i;
474             union {
475             int64_t number;
476             unsigned char bytes[8];
477             } stuff;
478 25           stuff.number = SvIV(src);
479 25           bswap8(stuff.bytes);
480             #else
481             if (size <= 9) {
482             int i;
483             union {
484             int32_t number;
485             unsigned char bytes[4];
486             } stuff;
487             stuff.number = SvIV(src);
488             bswap4(stuff.bytes);
489             #endif
490 25 100         if (stuff.bytes[0] & 0x80) { /* negative */
491 51 100         for (i = 0; i < sizeof(stuff.bytes); i++) {
492 49 100         if (stuff.bytes[i] != 0xff || (i < (sizeof(stuff.bytes)-1) && !(stuff.bytes[i+1]&0x80)))
    100          
    100          
493             break;
494             }
495             } else {
496 135 100         for (i = 0; i < sizeof(stuff.bytes); i++) {
497 132 100         if (stuff.bytes[i] || (i < (sizeof(stuff.bytes)-1) && (stuff.bytes[i+1]&0x80)))
    100          
    100          
498             break;
499             }
500             }
501 25 100         if (i == sizeof(stuff.bytes))
502 5           i--;
503 25 100         if (int_out)
504 8           *int_out= sizeof(stuff.bytes)-i;
505             else
506 17           pack_int(aTHX_ dest, sizeof(stuff.bytes)-i);
507 25           sv_catpvn(dest, ((char*)stuff.bytes)+i, sizeof(stuff.bytes)-i);
508              
509             } else {
510             struct cc_bignum bn;
511             unsigned char *tmp, *tmp2;
512             size_t encoded_len, i;
513              
514 5           cc_bignum_init_string(&bn, ptr, size);
515              
516 5           Newxz(tmp, bn.length+2, unsigned char);
517 5           Newxz(tmp2, bn.length+2, unsigned char);
518              
519 5           encoded_len = cc_bignum_byteify(&bn, tmp, bn.length+2);
520 98 100         for (i = 0; i < encoded_len; i++) {
521 93           tmp2[i] = tmp[encoded_len-1-i];
522             }
523              
524 5 100         if (int_out)
525 1           *int_out= encoded_len;
526             else
527 4           pack_int(aTHX_ dest, encoded_len);
528 5           sv_catpvn(dest, (char*)tmp2, encoded_len);
529              
530 5           Safefree(tmp);
531 5           Safefree(tmp2);
532              
533 5           cc_bignum_destroy(&bn);
534             }
535 30           }
536              
537 9           void encode_decimal(pTHX_ SV *dest, SV *src)
538             {
539             SV *tmp;
540             char *ptr;
541             STRLEN size, size_pos, pos;
542             int scale, varint_len;
543              
544 9           ptr = SvPV(src, size);
545              
546 9           tmp = sv_2mortal(newSV(size));
547 9           SvPOK_on(tmp);
548 9           SvCUR_set(tmp, 0);
549              
550 9           pos = 0;
551 9           scale = 0;
552              
553 9 50         if (ptr[pos] == '-') {
554 0           pos++;
555 0           sv_catpvn(tmp, ptr+pos, 1);
556             }
557 36 100         for (; pos < size && ptr[pos] >= '0' && ptr[pos] <= '9'; pos++) {
    100          
    100          
558             /* Main number */
559 27           sv_catpvn(tmp, ptr+pos, 1);
560             }
561 9 100         if (ptr[pos] == '.') {
562             /* Decimal point */
563 2           pos++;
564 21 100         for (; pos < size && ptr[pos] >= '0' && ptr[pos] <= '9'; pos++) {
    50          
    100          
565 19           sv_catpvn(tmp, ptr+pos, 1);
566 19           scale--;
567             }
568             }
569 9 100         if (ptr[pos] == 'e' || ptr[pos] == 'E') {
    100          
570             /* Explicit scale */
571 3           int explicit_scale = 0, neg = 0;
572 3           pos++;
573 3 100         if (ptr[pos] == '-') {
574 1           neg = 1;
575 1           pos++;
576 2 50         } else if (ptr[pos] == '+') {
577 2           pos++;
578             }
579              
580 13 100         for (; pos < size && ptr[pos] >= '0' && ptr[pos] <= '9'; pos++) {
    50          
    50          
581 10           explicit_scale *= 10;
582 10           explicit_scale += ptr[pos] - '0';
583             }
584 3 100         if (neg)
585 1           explicit_scale *= -1;
586 3           scale += explicit_scale;
587             }
588              
589 9 50         if (pos != size)
590 0           warn("Decimal '%s' is invalid", ptr);
591              
592 9           size_pos = pack_int(aTHX_ dest, 0);
593 9           pack_int(aTHX_ dest, scale*-1);
594 9           encode_varint(aTHX_ dest, tmp, &varint_len);
595 9           set_packed_int(aTHX_ dest, size_pos, 4+varint_len);
596 9           }
597              
598 13           void encode_list(pTHX_ SV *dest, SV *src, struct cc_type *type)
599             {
600             AV *list;
601             int count, i;
602             struct cc_type *inner_type;
603             STRLEN size_start, size_pos;
604              
605 13           inner_type = type->inner_type;
606             assert(inner_type);
607              
608 13 50         if (UNLIKELY(!SvROK(src) || SvTYPE(SvRV(src)) != SVt_PVAV))
    50          
609 0           croak("encode_list: argument must be an ARRAY reference");
610              
611 13           list = (AV*)SvRV(src);
612 13 50         if (UNLIKELY(av_len(list)+1 > INT32_MAX))
613 0           croak("encode_list: too many entries");
614              
615 13           size_pos = pack_int(aTHX_ dest, 0);
616 13           size_start = SvCUR(dest);
617              
618 13           count = av_len(list)+1;
619 13           pack_int(aTHX_ dest, count);
620              
621 38 100         for (i = 0; i < count; i++) {
622             SV **entry;
623 25           entry = av_fetch(list, i, 0);
624 25 50         if (!entry)
625 0           encode_undef(aTHX_ dest);
626             else
627 25           encode_cell(aTHX_ dest, *entry, inner_type);
628             }
629              
630 13           set_packed_int(aTHX_ dest, size_pos, SvCUR(dest)-size_start);
631 13           }
632              
633 9           void encode_map(pTHX_ SV *dest, SV *src, struct cc_type *type)
634             {
635             HV *map;
636             HE *key;
637             struct cc_type *key_type, *value_type;
638             int i;
639             STRLEN size_pos, count_pos, size_start;
640              
641 9           key_type = &type->inner_type[0];
642 9           value_type = &type->inner_type[1];
643             assert(key_type && value_type);
644              
645 9           size_pos = pack_int(aTHX_ dest, 0);
646 9           size_start = SvCUR(dest);
647 9           count_pos = pack_int(aTHX_ dest, 0);
648              
649 9 50         if (UNLIKELY(!SvROK(src) || SvTYPE(SvRV(src)) != SVt_PVHV))
    50          
650 0           croak("encode_map: argument must be a HASH reference");
651 9           map = (HV*)SvRV(src);
652              
653 9           i = 0;
654 9           hv_iterinit(map);
655 22 100         while ((key = hv_iternext(map)) != NULL) {
656             SV *key_sv, *value_sv;
657              
658 13 50         key_sv = HeSVKEY_force(key);
    50          
659 13           value_sv = hv_iterval(map, key);
660              
661 13           encode_cell(aTHX_ dest, key_sv, key_type);
662 13           encode_cell(aTHX_ dest, value_sv, value_type);
663              
664 13           i++;
665             }
666 9           set_packed_int(aTHX_ dest, size_pos, SvCUR(dest)-size_start);
667 9           set_packed_int(aTHX_ dest, count_pos, i);
668 9           }
669              
670 3           void encode_tuple(pTHX_ SV *dest, SV *src, struct cc_type *type)
671             {
672             AV *arr;
673             struct cc_tuple *tuple;
674             int i;
675             STRLEN size_pos, size_start;
676              
677 3           tuple = type->tuple;
678             assert(tuple);
679              
680 3 50         if (UNLIKELY(!SvROK(src) || SvTYPE(SvRV(src)) != SVt_PVAV))
    50          
681 0           croak("encode_tuple: argument must be an ARRAY reference");
682 3           arr = (AV*)SvRV(src);
683              
684 3           size_pos = pack_int(aTHX_ dest, 0);
685 3           size_start = SvCUR(dest);
686              
687 9 100         for (i = 0; i < tuple->field_count; i++) {
688             struct cc_type *inner_type;
689             SV **value;
690              
691 6           inner_type = &tuple->fields[i];
692 6           value = av_fetch(arr, i, 0);
693 6 50         if (!value)
694 0           encode_undef(aTHX_ dest);
695             else
696 6           encode_cell(aTHX_ dest, *value, inner_type);
697             }
698              
699 3           set_packed_int(aTHX_ dest, size_pos, SvCUR(dest) - size_start);
700 3           }
701              
702 2           void encode_udt(pTHX_ SV *dest, SV *src, struct cc_type *type)
703             {
704             HV *hash;
705             struct cc_udt *udt;
706             STRLEN size_pos, size_start;
707             int entry_count, i;
708              
709 2           udt = type->udt;
710             assert(udt);
711              
712 2           size_pos = pack_int(aTHX_ dest, 0);
713 2           size_start = SvCUR(dest);
714              
715 2 50         if (UNLIKELY(!SvROK(src) || SvTYPE(SvRV(src)) != SVt_PVHV))
    50          
716 0           croak("encode_udt: argument must be a HASH reference");
717 2           hash = (HV*)SvRV(src);
718              
719 2 50         entry_count = HvUSEDKEYS(hash);
720 2 50         if (UNLIKELY(entry_count > udt->field_count))
721 0           croak("encode_udt: too many fields in input");
722              
723 4 100         for (i = 0; i < entry_count; i++) {
724             struct cc_udt_field *field;
725             HE *entry;
726              
727 2           field = &udt->fields[i];
728 2           entry = hv_fetch_ent(hash, field->name, 0, field->name_hash);
729              
730 2 50         if (UNLIKELY(entry == NULL)) {
731 0 0         if (i == 0) {
732 0           croak("encode_udt: missing required fields in input");
733             } else {
734 0           croak("encode_udt: unexpected fields in input");
735             }
736             }
737              
738 2           encode_cell(aTHX_ dest, HeVAL(entry), &field->type);
739             }
740              
741 2           set_packed_int(aTHX_ dest, size_pos, SvCUR(dest) - size_start);
742 2           }
743              
744 17           void encode_undef(pTHX_ SV *dest)
745             {
746 17           sv_catpvn(dest, "\xff\xff\xff\xff", 4);
747 17           }