File Coverage

XS.xs
Criterion Covered Total %
statement 528 571 92.4
branch 294 446 65.9
condition n/a
subroutine n/a
pod n/a
total 822 1017 80.8


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4              
5             #include
6              
7             // C99 required!
8             // this is not just for comments, but also for
9             // integer constant semantics,
10             // sscanf format modifiers and more.
11              
12             enum {
13             // ASN_TAG
14             ASN_BOOLEAN = 0x01,
15             ASN_INTEGER = 0x02,
16             ASN_BIT_STRING = 0x03,
17             ASN_OCTET_STRING = 0x04,
18             ASN_NULL = 0x05,
19             ASN_OBJECT_IDENTIFIER = 0x06,
20             ASN_OID = 0x06,
21             ASN_OBJECT_DESCRIPTOR = 0x07,
22             ASN_EXTERNAL = 0x08,
23             ASN_REAL = 0x09,
24             ASN_ENUMERATED = 0x0a,
25             ASN_EMBEDDED_PDV = 0x0b,
26             ASN_UTF8_STRING = 0x0c,
27             ASN_RELATIVE_OID = 0x0d,
28             ASN_SEQUENCE = 0x10,
29             ASN_SET = 0x11,
30             ASN_NUMERIC_STRING = 0x12,
31             ASN_PRINTABLE_STRING = 0x13,
32             ASN_TELETEX_STRING = 0x14,
33             ASN_T61_STRING = 0x14,
34             ASN_VIDEOTEX_STRING = 0x15,
35             ASN_IA5_STRING = 0x16,
36             ASN_ASCII_STRING = 0x16,
37             ASN_UTC_TIME = 0x17,
38             ASN_GENERALIZED_TIME = 0x18,
39             ASN_GRAPHIC_STRING = 0x19,
40             ASN_VISIBLE_STRING = 0x1a,
41             ASN_ISO646_STRING = 0x1a,
42             ASN_GENERAL_STRING = 0x1b,
43             ASN_UNIVERSAL_STRING = 0x1c,
44             ASN_CHARACTER_STRING = 0x1d,
45             ASN_BMP_STRING = 0x1e,
46              
47             ASN_TAG_BER = 0x1f,
48             ASN_TAG_MASK = 0x1f,
49              
50             // primitive/constructed
51             ASN_CONSTRUCTED = 0x20,
52              
53             // ASN_CLASS
54             ASN_UNIVERSAL = 0x00,
55             ASN_APPLICATION = 0x01,
56             ASN_CONTEXT = 0x02,
57             ASN_PRIVATE = 0x03,
58              
59             ASN_CLASS_MASK = 0xc0,
60             ASN_CLASS_SHIFT = 6,
61              
62             // ASN_APPLICATION SNMP
63             SNMP_IPADDRESS = 0x00,
64             SNMP_COUNTER32 = 0x01,
65             SNMP_GAUGE32 = 0x02,
66             SNMP_UNSIGNED32 = 0x02,
67             SNMP_TIMETICKS = 0x03,
68             SNMP_OPAQUE = 0x04,
69             SNMP_COUNTER64 = 0x06,
70             };
71              
72             // tlow-level types this module can ecode the above (and more) into
73             enum {
74             BER_TYPE_BYTES,
75             BER_TYPE_UTF8,
76             BER_TYPE_UCS2,
77             BER_TYPE_UCS4,
78             BER_TYPE_INT,
79             BER_TYPE_OID,
80             BER_TYPE_RELOID,
81             BER_TYPE_NULL,
82             BER_TYPE_BOOL,
83             BER_TYPE_REAL,
84             BER_TYPE_IPADDRESS,
85             BER_TYPE_CROAK,
86             };
87              
88             // tuple array indices
89             enum {
90             BER_CLASS = 0,
91             BER_TAG = 1,
92             BER_FLAGS = 2,
93             BER_DATA = 3,
94             BER_ARRAYSIZE
95             };
96              
97             #define MAX_OID_STRLEN 4096
98              
99             typedef void profile_type;
100              
101             static profile_type *cur_profile, *default_profile;
102             static SV *buf_sv; // encoding buffer
103             static U8 *buf, *cur, *end; // buffer start, current, end
104              
105             #if PERL_VERSION < 18
106             # define utf8_to_uvchr_buf(s,e,l) utf8_to_uvchr (s, l)
107             #endif
108              
109             #ifndef SvREFCNT_inc_NN
110             #define SvREFCNT_inc_NN(x) SvREFCNT_inc (x)
111             #endif
112             #ifndef SvREFCNT_dec_NN
113             #define SvREFCNT_dec_NN(x) SvREFCNT_dec (x)
114             #endif
115              
116             #if __GNUC__ >= 3
117             # define expect(expr,value) __builtin_expect ((expr), (value))
118             # define INLINE static inline
119             #else
120             # define expect(expr,value) (expr)
121             # define INLINE static
122             #endif
123              
124             #define expect_false(expr) expect ((expr) != 0, 0)
125             #define expect_true(expr) expect ((expr) != 0, 1)
126              
127             /////////////////////////////////////////////////////////////////////////////
128              
129             static SV *sviv_cache[32];
130              
131             // for "small" integers, return a readonly sv, otherwise create a new one
132 3374           static SV *newSVcacheint (int val)
133             {
134 3374 50         if (expect_false (val < 0 || val >= sizeof (sviv_cache)))
    50          
    50          
135 0           return newSViv (val);
136              
137 3374 100         if (expect_false (!sviv_cache [val]))
138             {
139 53           sviv_cache [val] = newSVuv (val);
140 53           SvREADONLY_on (sviv_cache [val]);
141             }
142              
143 3374           return SvREFCNT_inc_NN (sviv_cache [val]);
144             }
145              
146             /////////////////////////////////////////////////////////////////////////////
147              
148             static HV *profile_stash;
149              
150             static profile_type *
151 1722           SvPROFILE (SV *profile)
152             {
153 1722 100         if (!SvOK (profile))
    50          
    50          
154 1211           return default_profile;
155              
156 511 50         if (!SvROK (profile))
157 0           croak ("Convert::BER::XS::Profile expected");
158              
159 511           profile = SvRV (profile);
160              
161 511 50         if (SvSTASH (profile) != profile_stash)
162 0           croak ("Convert::BER::XS::Profile expected");
163              
164 511           return (void *)profile;
165             }
166              
167             static int
168 1927           profile_lookup (profile_type *profile, int klass, int tag)
169             {
170 1927           SV *sv = (SV *)profile;
171 1927           U32 idx = (tag << 2) + klass;
172              
173 1927 50         if (expect_false (idx >= SvCUR (sv)))
174 0           return BER_TYPE_BYTES;
175              
176 1927           return SvPVX (sv)[idx];
177             }
178              
179             static void
180 743           profile_set (profile_type *profile, int klass, int tag, int type)
181             {
182 743           SV *sv = (SV *)profile;
183 743           U32 idx = (tag << 2) + klass;
184 743           STRLEN oldlen = SvCUR (sv);
185 743           STRLEN newlen = idx + 2;
186              
187 743 100         if (idx >= oldlen)
188             {
189 220           sv_grow (sv, newlen);
190 220           memset (SvPVX (sv) + oldlen, BER_TYPE_BYTES, newlen - oldlen);
191 220           SvCUR_set (sv, newlen);
192             }
193              
194 743           SvPVX (sv)[idx] = type;
195 743           }
196              
197             static SV *
198 30           profile_new (void)
199             {
200 30           SV *sv = newSVpvn ("", 0);
201              
202             static const struct {
203             int klass;
204             int tag;
205             int type;
206             } *celem, default_map[] = {
207             { ASN_UNIVERSAL, ASN_BOOLEAN , BER_TYPE_BOOL },
208             { ASN_UNIVERSAL, ASN_INTEGER , BER_TYPE_INT },
209             { ASN_UNIVERSAL, ASN_NULL , BER_TYPE_NULL },
210             { ASN_UNIVERSAL, ASN_OBJECT_IDENTIFIER, BER_TYPE_OID },
211             { ASN_UNIVERSAL, ASN_RELATIVE_OID , BER_TYPE_RELOID },
212             { ASN_UNIVERSAL, ASN_REAL , BER_TYPE_REAL },
213             { ASN_UNIVERSAL, ASN_ENUMERATED , BER_TYPE_INT },
214             { ASN_UNIVERSAL, ASN_UTF8_STRING , BER_TYPE_UTF8 },
215             { ASN_UNIVERSAL, ASN_BMP_STRING , BER_TYPE_UCS2 },
216             { ASN_UNIVERSAL, ASN_UNIVERSAL_STRING , BER_TYPE_UCS4 },
217             };
218              
219 330 100         for (celem = default_map + sizeof (default_map) / sizeof (default_map [0]); celem-- > default_map; )
220 300           profile_set ((profile_type *)sv, celem->klass, celem->tag, celem->type);
221              
222 30           return sv_bless (newRV_noinc (sv), profile_stash);
223             }
224              
225             /////////////////////////////////////////////////////////////////////////////
226             // decoder
227              
228             static void
229 631           error (const char *errmsg)
230             {
231 631           croak ("%s at offset 0x%04x", errmsg, cur - buf);
232             }
233              
234             static void
235 831           want (UV count)
236             {
237 831 100         if (expect_false ((uintptr_t)(end - cur) < count))
238 3           error ("unexpected end of message buffer");
239 828           }
240              
241             // get_* functions fetch something from the buffer
242             // decode_* functions use get_* fun ctions to decode ber values
243              
244             // get single octet
245             static U8
246 10982           get_u8 (void)
247             {
248 10982 100         if (cur == end)
249 3           error ("unexpected end of message buffer");
250              
251 10979           return *cur++;
252             }
253              
254             // get n octets
255             static U8 *
256 426           get_n (UV count)
257             {
258 426           want (count);
259 425           U8 *res = cur;
260 425           cur += count;
261 425           return res;
262             }
263              
264             // get ber-encoded integer (i.e. pack "w")
265             static UV
266 1376           get_w (void)
267             {
268 1376           UV res = 0;
269 1376           U8 c = get_u8 ();
270              
271 1376 100         if (expect_false (c == 0x80))
272 1           error ("invalid BER padding (X.690 8.1.2.4.2, 8.19.2)");
273              
274             for (;;)
275             {
276 7510 100         if (expect_false (res >> UVSIZE * 8 - 7))
277 545           error ("BER variable length integer overflow");
278              
279 6965           res = (res << 7) | (c & 0x7f);
280              
281 6965 100         if (expect_true (!(c & 0x80)))
282 829           return res;
283              
284 6136           c = get_u8 ();
285 6135           }
286             }
287              
288             static UV
289 1667           get_length (void)
290             {
291 1667           UV res = get_u8 ();
292              
293 1667 100         if (expect_false (res & 0x80))
294             {
295 87           U8 cnt = res & 0x7f;
296              
297             // this genewrates quite ugly code, but the overhead
298             // of copying the bytes for these lengths is probably so high
299             // that a slightly inefficient get_length won't matter.
300              
301 87 100         if (expect_false (cnt == 0))
302 1           error ("invalid use of indefinite BER length form in primitive encoding (X.690 8.1.3.2)");
303              
304 86 100         if (expect_false (cnt > UVSIZE))
305 1           error ("BER value length too long (must fit into UV) or BER reserved value in length (X.690 8.1.3.5)");
306              
307 85           want (cnt);
308              
309 85           res = 0;
310             do
311 151           res = (res << 8) | *cur++;
312 151 100         while (--cnt);
313             }
314              
315 1665           return res;
316             }
317              
318             static SV *
319 143           decode_int (UV len)
320             {
321 143 50         if (!len)
322 0           error ("invalid BER_TYPE_INT length zero (X.690 8.3.1)");
323              
324 143           U8 *data = get_n (len);
325              
326 143 100         if (expect_false (len > 1))
327             {
328 69           U16 mask = (data [0] << 8) | data [1] & 0xff80;
329              
330 69 100         if (expect_false (mask == 0xff80 || mask == 0x0000))
    100          
    100          
331 3           error ("invalid padding in BER_TYPE_INT (X.690 8.3.2)");
332             }
333              
334 140           int negative = data [0] & 0x80;
335              
336 140 100         UV val = negative ? -1 : 0; // copy signbit to all bits
337              
338 140 100         if (len > UVSIZE + (!negative && !*data))
    100          
    100          
339 5           error ("BER_TYPE_INT overflow");
340              
341             do
342 369           val = (val << 8) | *data++;
343 369 100         while (--len);
344              
345             // the cast to IV relies on implementation-defined behaviour (two's complement cast)
346             // but that's ok, as perl relies on it as well.
347 135 100         return negative ? newSViv ((IV)val) : newSVuv (val);
348             }
349              
350             static SV *
351 264           decode_data (UV len)
352             {
353 264           return newSVpvn ((char *)get_n (len), len);
354             }
355              
356             // helper for decode_object_identifier
357             static char *
358 1189           write_uv (char *buf, UV u)
359             {
360             // the one-digit case is absolutely predominant, so this pays off (hopefully)
361 1189 100         if (expect_true (u < 10))
362 789           *buf++ = u + '0';
363             else
364             {
365             // this *could* be done much faster using branchless fixed-point arithmetics
366 400           char *beg = buf;
367              
368             do
369             {
370 3088           *buf++ = u % 10 + '0';
371 3088           u /= 10;
372             }
373 3088 100         while (u);
374              
375             // reverse digits
376 400           char *ptr = buf;
377 1857 100         while (--ptr > beg)
378             {
379 1457           char c = *ptr;
380 1457           *ptr = *beg;
381 1457           *beg = c;
382 1457           ++beg;
383             }
384             }
385              
386 1189           return buf;
387             }
388              
389             static SV *
390 912           decode_oid (UV len, int relative)
391             {
392 912 100         if (len <= 0)
393             {
394 1           error ("BER_TYPE_OID length must not be zero");
395 0           return &PL_sv_undef;
396             }
397              
398 911           U8 *end = cur + len;
399 911           UV w = get_w ();
400              
401             static char oid[MAX_OID_STRLEN]; // static, because too large for stack
402 366           char *app = oid;
403              
404 366 100         if (relative)
405 3           app = write_uv (app, w);
406             else
407             {
408             UV w1, w2;
409              
410 363 100         if (w < 2 * 40)
411 84           (w1 = w / 40), (w2 = w % 40);
412             else
413 279           (w1 = 2), (w2 = w - 2 * 40);
414              
415 363           app = write_uv (app, w1);
416 363           *app++ = '.';
417 363           app = write_uv (app, w2);
418             }
419              
420 826 100         while (cur < end)
421             {
422             // we assume an oid component is never > 64 digits
423 462 50         if (oid + sizeof (oid) - app < 64)
424 0           croak ("BER_TYPE_OID to long to decode");
425              
426 462           w = get_w ();
427 460           *app++ = '.';
428 460           app = write_uv (app, w);
429             }
430              
431 364           return newSVpvn (oid, app - oid);
432             }
433              
434             // oh my, this is a total mess
435             static SV *
436 39           decode_real (UV len)
437             {
438             SV *res;
439 39           U8 *beg = cur;
440              
441 39 100         if (len == 0)
442 4           res = newSVnv (0.);
443             else
444             {
445 35           U8 info = get_u8 ();
446              
447 34 100         if (info & 0x80)
448             {
449             // binary
450             static const U8 base[] = { 2, 8, 16, 0 };
451 4 50         NV S = info & 0x40 ? -1 : 1; // sign
452 4           NV B = base [(info >> 4) & 3]; // base
453 4           NV F = 1 << ((info >> 2) & 3); // scale factor ("shift")
454 4           int L = info & 3; // exponent length
455              
456 4 50         if (!B)
457 0           croak ("BER_TYPE_REAL binary encoding uses invalid base (0x%02x)", info);
458              
459 4           SAVETMPS;
460              
461 4 100         SV *E = sv_2mortal (decode_int (L == 3 ? get_u8 () : L + 1));
462 4           SV *M = sv_2mortal (decode_int (len - (cur - beg)));
463              
464 4 50         res = newSVnv (S * SvNV (M) * F * Perl_pow (B, SvNV (E)));
    50          
465              
466 4 50         FREETMPS;
467             }
468 30 100         else if (info & 0x40)
469             {
470             // SpecialRealValue
471 14           U8 special = get_u8 ();
472             NV val;
473              
474 13           switch (special)
475             {
476 3           case 0x40: val = NV_INF; break;
477 3           case 0x41: val = -NV_INF; break;
478 3           case 0x42: val = NV_NAN; break;
479 3           case 0x43: val = -(NV)0.; break;
480              
481             default:
482 1           croak ("BER_TYPE_REAL SpecialRealValues invalid encoding 0x%02x (X.690 8.5.9)", special);
483             }
484              
485 12           res = newSVnv (val);
486             }
487             else
488             {
489             // decimal
490 16           dSP;
491 16           SAVETMPS;
492 16 50         PUSHMARK (SP);
493 16 50         EXTEND (SP, 2);
494 16           PUSHs (sv_2mortal (newSVcacheint (info & 0x3f)));
495 16           PUSHs (sv_2mortal (newSVpvn (get_n (len - 1), len - 1)));
496 16           PUTBACK;
497 16           call_pv ("Convert::BER::XS::_decode_real_decimal", G_SCALAR);
498 16           SPAGAIN;
499 16           res = SvREFCNT_inc_NN (POPs);
500 16           PUTBACK;
501 16 50         FREETMPS;
502             }
503             }
504              
505 36 50         if (cur - beg != len)
506             {
507 0           SvREFCNT_dec_NN (res);
508 0           croak ("BER_TYPE_REAL invalid content length (X.690 8,5)");
509             }
510              
511 36           return res;
512             }
513              
514             // TODO: this is unacceptably slow
515             static SV *
516 17           decode_ucs (UV len, int chrsize)
517             {
518 17 100         if (len & (chrsize - 1))
519 5           croak ("BER_TYPE_UCS has an invalid number of octets (%d)", len);
520              
521 12           SV *res = NEWSV (0, 0);
522              
523 30 100         while (len)
524             {
525 18           U8 b1 = get_u8 ();
526 18           U8 b2 = get_u8 ();
527 18           U32 chr = (b1 << 8) | b2;
528              
529 18 100         if (chrsize == 4)
530             {
531 6           U8 b3 = get_u8 ();
532 6           U8 b4 = get_u8 ();
533 6           chr = (chr << 16) | (b3 << 8) | b4;
534             }
535              
536             U8 uchr [UTF8_MAXBYTES];
537 18           int uclen = uvuni_to_utf8 (uchr, chr) - uchr;
538              
539 18           sv_catpvn (res, (const char *)uchr, uclen);
540 18           len -= chrsize;
541             }
542              
543 12           SvUTF8_on (res);
544              
545 12           return res;
546             }
547              
548             static SV *
549 1687           decode_ber (void)
550             {
551 1687           int identifier = get_u8 ();
552              
553             SV *res;
554              
555 1687           int constructed = identifier & ASN_CONSTRUCTED;
556 1687           int klass = (identifier & ASN_CLASS_MASK) >> ASN_CLASS_SHIFT;
557 1687           int tag = identifier & ASN_TAG_MASK;
558              
559 1687 100         if (tag == ASN_TAG_BER)
560 3           tag = get_w ();
561              
562 1687 100         if (constructed)
563             {
564 269           want (1);
565 269           AV *av = (AV *)sv_2mortal ((SV *)newAV ());
566              
567 269 100         if (expect_false (*cur == 0x80))
568             {
569             // indefinite length
570 20           ++cur;
571              
572             for (;;)
573             {
574 51           want (2);
575 49 100         if (!cur [0] && !cur [1])
    50          
576             {
577 18           cur += 2;
578 18           break;
579             }
580              
581 31           av_push (av, decode_ber ());
582 49           }
583             }
584             else
585             {
586 249           UV len = get_length ();
587 249           UV seqend = (cur - buf) + len;
588              
589 809 100         while (cur < buf + seqend)
590 568           av_push (av, decode_ber ());
591              
592 241 50         if (expect_false (cur > buf + seqend))
593 0           croak ("CONSTRUCTED type %02x length overflow (0x%x 0x%x)\n", identifier, (int)(cur - buf), (int)seqend);
594             }
595              
596 259           res = newRV_inc ((SV *)av);
597             }
598             else
599             {
600 1418           UV len = get_length ();
601              
602 1416           switch (profile_lookup (cur_profile, klass, tag))
603             {
604             case BER_TYPE_NULL:
605 25 100         if (expect_false (len))
606 1           croak ("BER_TYPE_NULL value with non-zero length %d encountered (X.690 8.8.2)", len);
607              
608 24           res = &PL_sv_undef;
609 24           break;
610              
611             case BER_TYPE_BOOL:
612 20 100         if (expect_false (len != 1))
613 2           croak ("BER_TYPE_BOOLEAN value with invalid length %d encountered (X.690 8.2.1)", len);
614              
615 18           res = newSVcacheint (!!get_u8 ());
616 18           break;
617              
618             case BER_TYPE_OID:
619 909           res = decode_oid (len, 0);
620 361           break;
621              
622             case BER_TYPE_RELOID:
623 3           res = decode_oid (len, 1);
624 3           break;
625              
626             case BER_TYPE_INT:
627 135           res = decode_int (len);
628 127           break;
629              
630             case BER_TYPE_UTF8:
631 12           res = decode_data (len);
632 12           SvUTF8_on (res);
633 12           break;
634              
635             case BER_TYPE_BYTES:
636 252           res = decode_data (len);
637 251           break;
638              
639             case BER_TYPE_IPADDRESS:
640             {
641 4 100         if (len != 4)
642 1           croak ("BER_TYPE_IPADDRESS type with invalid length %d encountered (RFC 2578 7.1.5)", len);
643              
644 3           U8 *data = get_n (4);
645 3           res = newSVpvf ("%d.%d.%d.%d", data [0], data [1], data [2], data [3]);
646             }
647 3           break;
648              
649             case BER_TYPE_UCS2:
650 8           res = decode_ucs (len, 2);
651 6           break;
652              
653             case BER_TYPE_UCS4:
654 9           res = decode_ucs (len, 4);
655 6           break;
656              
657             case BER_TYPE_REAL:
658 39           res = decode_real (len);
659 36           break;
660              
661             case BER_TYPE_CROAK:
662 0           croak ("class/tag %d/%d mapped to BER_TYPE_CROAK", klass, tag);
663              
664             default:
665 0           croak ("unconfigured/unsupported class/tag %d/%d", klass, tag);
666             }
667             }
668              
669 1106           AV *av = newAV ();
670 1106           av_fill (av, BER_ARRAYSIZE - 1);
671 1106           AvARRAY (av)[BER_CLASS] = newSVcacheint (klass);
672 1106           AvARRAY (av)[BER_TAG ] = newSVcacheint (tag);
673 1106           AvARRAY (av)[BER_FLAGS] = newSVcacheint (constructed ? 1 : 0);
674 1106           AvARRAY (av)[BER_DATA ] = res;
675              
676 1106           return newRV_noinc ((SV *)av);
677             }
678              
679             /////////////////////////////////////////////////////////////////////////////
680             // encoder
681              
682             /* adds two STRLENs together, slow, and with paranoia */
683             static STRLEN
684 408           strlen_sum (STRLEN l1, STRLEN l2)
685             {
686 408           size_t sum = l1 + l2;
687              
688 408 50         if (sum < (size_t)l2 || sum != (size_t)(STRLEN)sum)
689 0           croak ("Convert::BER::XS: string size overflow");
690              
691 408           return sum;
692             }
693              
694             static void
695 183           set_buf (SV *sv)
696             {
697             STRLEN len;
698 183           buf_sv = sv;
699 183 50         buf = (U8 *)SvPVbyte (buf_sv, len);
700 183           cur = buf;
701 183           end = buf + len;
702 183           }
703              
704             /* similar to SvGROW, but somewhat safer and guarantees exponential realloc strategy */
705             static char *
706 204           my_sv_grow (SV *sv, size_t len1, size_t len2)
707             {
708 204           len1 = strlen_sum (len1, len2);
709 204           len1 = strlen_sum (len1, len1 >> 1);
710              
711 204 100         if (len1 > 4096 - 24)
712 1           len1 = (len1 | 4095) - 24;
713              
714 204 50         return SvGROW (sv, len1);
    100          
715             }
716              
717             static void
718 1582           need (STRLEN len)
719             {
720 1582 100         if (expect_false ((uintptr_t)(end - cur) < len))
721             {
722 204           STRLEN pos = cur - buf;
723 204           buf = (U8 *)my_sv_grow (buf_sv, pos, len);
724 204           cur = buf + pos;
725 204           end = buf + SvLEN (buf_sv) - 1;
726             }
727 1582           }
728              
729             static void
730 761           put_u8 (int val)
731             {
732 761           need (1);
733 761           *cur++ = val;
734 761           }
735              
736             static void
737 545           put_w_nocheck (UV val)
738             {
739             #if UVSIZE > 4
740 545           *cur = (val >> 7 * 9) | 0x80; cur += val >= ((UV)1 << (7 * 9));
741 545           *cur = (val >> 7 * 8) | 0x80; cur += val >= ((UV)1 << (7 * 8));
742 545           *cur = (val >> 7 * 7) | 0x80; cur += val >= ((UV)1 << (7 * 7));
743 545           *cur = (val >> 7 * 6) | 0x80; cur += val >= ((UV)1 << (7 * 6));
744 545           *cur = (val >> 7 * 5) | 0x80; cur += val >= ((UV)1 << (7 * 5));
745             #endif
746 545           *cur = (val >> 7 * 4) | 0x80; cur += val >= ((UV)1 << (7 * 4));
747 545           *cur = (val >> 7 * 3) | 0x80; cur += val >= ((UV)1 << (7 * 3));
748 545           *cur = (val >> 7 * 2) | 0x80; cur += val >= ((UV)1 << (7 * 2));
749 545           *cur = (val >> 7 * 1) | 0x80; cur += val >= ((UV)1 << (7 * 1));
750 545           *cur = val & 0x7f; cur += 1;
751 545           }
752              
753             static void
754 0           put_w (UV val)
755             {
756 0           need (5); // we only handle up to 5 bytes
757              
758 0           put_w_nocheck (val);
759 0           }
760              
761             static U8 *
762 647           put_length_at (UV val, U8 *cur)
763             {
764 647 100         if (val <= 0x7fU)
765 570           *cur++ = val;
766             else
767             {
768 77           U8 *lenb = cur++;
769              
770             #if UVSIZE > 4
771 77           *cur = val >> 56; cur += val >= ((UV)1 << (8 * 7));
772 77           *cur = val >> 48; cur += val >= ((UV)1 << (8 * 6));
773 77           *cur = val >> 40; cur += val >= ((UV)1 << (8 * 5));
774 77           *cur = val >> 32; cur += val >= ((UV)1 << (8 * 4));
775             #endif
776 77           *cur = val >> 24; cur += val >= ((UV)1 << (8 * 3));
777 77           *cur = val >> 16; cur += val >= ((UV)1 << (8 * 2));
778 77           *cur = val >> 8; cur += val >= ((UV)1 << (8 * 1));
779 77           *cur = val ; cur += 1;
780              
781 77           *lenb = 0x80 + cur - lenb - 1;
782             }
783              
784 647           return cur;
785             }
786              
787             static void
788 300           put_length (UV val)
789             {
790 300           need (9 + val);
791 300           cur = put_length_at (val, cur);
792 300           }
793              
794             // return how many bytes the encoded length requires
795 347           static int length_length (UV val)
796             {
797             // use hashing with a DeBruin sequence, anyone?
798 347           return expect_true (val <= 0x7fU)
799             ? 1
800 407 100         : 2
801 60 100         + (val > 0x000000000000ffU)
802 60           + (val > 0x0000000000ffffU)
803 60           + (val > 0x00000000ffffffU)
804             #if UVSIZE > 4
805 60           + (val > 0x000000ffffffffU)
806 60           + (val > 0x0000ffffffffffU)
807 60           + (val > 0x00ffffffffffffU)
808 60           + (val > 0xffffffffffffffU)
809             #endif
810             ;
811             }
812              
813             static void
814 240           encode_data (const char *ptr, STRLEN len)
815             {
816 240           put_length (len);
817 240           memcpy (cur, ptr, len);
818 240           cur += len;
819 240           }
820              
821             static void
822 0           encode_uv (UV uv)
823             {
824 0           }
825              
826             static void
827 103           encode_int (SV *sv)
828             {
829 103           need (8 + 1 + 1); // 64 bit + length + extra 0
830              
831 103 50         if (expect_false (!SvIOK (sv)))
832 0           sv_2iv_flags (sv, 0);
833              
834 103           U8 *lenb = cur++;
835              
836 103 100         if (SvIOK_notUV (sv))
837             {
838 99           IV iv = SvIVX (sv);
839              
840 99 100         if (expect_false (iv < 0))
841             {
842             // get two's complement bit pattern - works even on hypothetical non-2c machines
843 8           UV uv = iv;
844              
845             #if UVSIZE > 4
846 8           *cur = uv >> 56; cur += !!(~uv & 0xff80000000000000U);
847 8           *cur = uv >> 48; cur += !!(~uv & 0xffff800000000000U);
848 8           *cur = uv >> 40; cur += !!(~uv & 0xffffff8000000000U);
849 8           *cur = uv >> 32; cur += !!(~uv & 0xffffffff80000000U);
850             #endif
851 8           *cur = uv >> 24; cur += !!(~uv & 0xffffffffff800000U);
852 8           *cur = uv >> 16; cur += !!(~uv & 0xffffffffffff8000U);
853 8           *cur = uv >> 8; cur += !!(~uv & 0xffffffffffffff80U);
854 8           *cur = uv ; cur += 1;
855              
856 8           *lenb = cur - lenb - 1;
857              
858 8           return;
859             }
860             }
861              
862 95 100         UV uv = SvUV (sv);
863              
864             // prepend an extra 0 if the high bit is 1
865 95           *cur = 0; cur += !!(uv & ((UV)1 << (UVSIZE * 8 - 1)));
866              
867             #if UVSIZE > 4
868 95           *cur = uv >> 56; cur += !!(uv & 0xff80000000000000U);
869 95           *cur = uv >> 48; cur += !!(uv & 0xffff800000000000U);
870 95           *cur = uv >> 40; cur += !!(uv & 0xffffff8000000000U);
871 95           *cur = uv >> 32; cur += !!(uv & 0xffffffff80000000U);
872             #endif
873 95           *cur = uv >> 24; cur += !!(uv & 0xffffffffff800000U);
874 95           *cur = uv >> 16; cur += !!(uv & 0xffffffffffff8000U);
875 95           *cur = uv >> 8; cur += !!(uv & 0xffffffffffffff80U);
876 95           *cur = uv ; cur += 1;
877              
878 95           *lenb = cur - lenb - 1;
879             }
880              
881             // we don't know the length yet, so we optimistically
882             // assume the length will need one octet later. If that
883             // turns out to be wrong, we memmove as needed.
884             // mark the beginning
885             static STRLEN
886 347           len_fixup_mark (void)
887             {
888 347           return cur++ - buf;
889             }
890              
891             // patch up the length
892             static void
893 347           len_fixup (STRLEN mark)
894             {
895 347           STRLEN reallen = (cur - buf) - mark - 1;
896 347           int lenlen = length_length (reallen);
897              
898 347 100         if (expect_false (lenlen > 1))
899             {
900             // bad luck, we have to shift the bytes to make room for the length
901 60           need (5);
902 60           memmove (buf + mark + lenlen, buf + mark + 1, reallen);
903 60           cur += lenlen - 1;
904             }
905            
906 347           put_length_at (reallen, buf + mark);
907 347           }
908              
909             static char *
910 640           read_uv (char *str, UV *uv)
911             {
912 640           UV r = 0;
913              
914 1598 100         while (*str >= '0')
915 958           r = r * 10 + *str++ - '0';
916              
917 640           *uv = r;
918              
919 640           str += !!*str; // advance over any non-zero byte
920              
921 640           return str;
922             }
923              
924             static void
925 97           encode_oid (SV *oid, int relative)
926             {
927             STRLEN len;
928 97 50         char *ptr = SvPV (oid, len); // utf8 vs. bytes does not matter
929              
930             // we need at most as many octets as the string form
931 97           need (len + 1);
932 97           STRLEN mark = len_fixup_mark ();
933              
934             UV w1, w2;
935              
936 97 100         if (!relative)
937             {
938 95           ptr = read_uv (ptr, &w1);
939 95           ptr = read_uv (ptr, &w2);
940              
941 95           put_w_nocheck (w1 * 40 + w2);
942             }
943              
944 547 100         while (*ptr)
945             {
946 450           ptr = read_uv (ptr, &w1);
947 450           put_w_nocheck (w1);
948             }
949              
950 97           len_fixup (mark);
951 97           }
952              
953             static void
954 27           encode_real (SV *data)
955             {
956 27 100         NV nv = SvNV (data);
957              
958 27 100         if (expect_false (nv == (NV)0.))
959             {
960 5 100         if (signbit (nv))
961             {
962             // negative zero
963 2           need (3);
964 2           *cur++ = 2;
965 2           *cur++ = 0x40;
966 2           *cur++ = 0x43;
967             }
968             else
969             {
970             // positive zero
971 3           need (1);
972 5           *cur++ = 0;
973             }
974             }
975 22 100         else if (expect_false (Perl_isinf (nv)))
976             {
977 4           need (3);
978 4           *cur++ = 2;
979 4           *cur++ = 0x40;
980 4 100         *cur++ = nv < (NV)0. ? 0x41 : 0x40;
981             }
982 18 100         else if (expect_false (Perl_isnan (nv)))
983             {
984 2           need (3);
985 2           *cur++ = 2;
986 2           *cur++ = 0x40;
987 2           *cur++ = 0x42;
988             }
989             else
990             {
991             // use decimal encoding
992 16           dSP;
993 16           SAVETMPS;
994 16 50         PUSHMARK (SP);
995 16 50         EXTEND (SP, 2);
996 16           PUSHs (data);
997 16           PUSHs (sv_2mortal (newSVcacheint (NV_DIG)));
998 16           PUTBACK;
999 16           call_pv ("Convert::BER::XS::_encode_real_decimal", G_SCALAR);
1000 16           SPAGAIN;
1001              
1002 16           SV *sv = POPs;
1003             STRLEN l;
1004 16 50         char *f = SvPV (sv, l);
1005              
1006 16           put_length (l);
1007 16           memcpy (cur, f, l);
1008 16           cur += l;
1009              
1010 16           PUTBACK;
1011 16 50         FREETMPS;
1012             }
1013 27           }
1014              
1015             static void
1016 8           encode_ucs (SV *data, int chrsize)
1017             {
1018 8           STRLEN uchars = sv_len_utf8 (data);
1019             STRLEN len;;
1020 8 50         char *ptr = SvPVutf8 (data, len);
1021              
1022 8           put_length (uchars * chrsize);
1023              
1024 20 100         while (uchars--)
1025             {
1026             STRLEN uclen;
1027 12 50         UV uchr = utf8_to_uvchr_buf ((U8 *)ptr, (U8 *)ptr + len, &uclen);
1028              
1029 12           ptr += uclen;
1030 12           len -= uclen;
1031              
1032 12 100         if (chrsize == 4)
1033             {
1034 4           *cur++ = uchr >> 24;
1035 4           *cur++ = uchr >> 16;
1036             }
1037              
1038 12           *cur++ = uchr >> 8;
1039 12           *cur++ = uchr;
1040             }
1041 8           }
1042              
1043             // check whether an SV is a BER tuple and returns its AV *
1044             static AV *
1045 778           ber_tuple (SV *tuple)
1046             {
1047             SV *rv;
1048              
1049 778 50         if (expect_false (!SvROK (tuple) || SvTYPE ((rv = SvRV (tuple))) != SVt_PVAV))
    50          
    50          
1050 0           croak ("BER tuple must be array-reference");
1051              
1052 778 50         if (expect_false (SvRMAGICAL (rv)))
1053 0           croak ("BER tuple must not be tied");
1054              
1055 778 50         if (expect_false (AvFILL ((AV *)rv) != BER_ARRAYSIZE - 1))
    50          
1056 0 0         croak ("BER tuple must contain exactly %d elements, not %d", BER_ARRAYSIZE, AvFILL ((AV *)rv) + 1);
1057              
1058 778           return (AV *)rv;
1059             }
1060              
1061             static void
1062 761           encode_ber (SV *tuple)
1063             {
1064 761           AV *av = ber_tuple (tuple);
1065              
1066 761 50         int klass = SvIV (AvARRAY (av)[BER_CLASS]);
1067 761 50         int tag = SvIV (AvARRAY (av)[BER_TAG]);
1068 761 50         int constructed = SvIV (AvARRAY (av)[BER_FLAGS]) & 1 ? ASN_CONSTRUCTED : 0;
    100          
1069 761           SV *data = AvARRAY (av)[BER_DATA];
1070              
1071 761           int identifier = (klass << ASN_CLASS_SHIFT) | constructed;
1072              
1073 761 50         if (expect_false (tag >= ASN_TAG_BER))
1074             {
1075 0           put_u8 (identifier | ASN_TAG_BER);
1076 0           put_w (tag);
1077             }
1078             else
1079 761           put_u8 (identifier | tag);
1080              
1081 761 100         if (constructed)
1082             {
1083             // we optimistically assume that only one length byte is needed
1084             // and adjust later
1085 250           need (1);
1086 250           STRLEN mark = len_fixup_mark ();
1087              
1088 250 50         if (expect_false (!SvROK (data) || SvTYPE (SvRV (data)) != SVt_PVAV))
    50          
    50          
1089 0           croak ("BER CONSTRUCTED data must be array-reference");
1090              
1091 250           AV *av = (AV *)SvRV (data);
1092 250 50         int fill = AvFILL (av);
1093              
1094 250 50         if (expect_false (SvRMAGICAL (av)))
1095 0           croak ("BER CONSTRUCTED data must not be tied");
1096              
1097             int i;
1098 828 100         for (i = 0; i <= fill; ++i)
1099 578           encode_ber (AvARRAY (av)[i]);
1100              
1101 250           len_fixup (mark);
1102             }
1103             else
1104 511           switch (profile_lookup (cur_profile, klass, tag))
1105             {
1106             case BER_TYPE_NULL:
1107 22           put_length (0);
1108 22           break;
1109              
1110             case BER_TYPE_BOOL:
1111 14           put_length (1);
1112 14 50         *cur++ = SvTRUE (data) ? 0xff : 0x00; // 0xff = DER/CER
    50          
    0          
    50          
    0          
    0          
    100          
    50          
    50          
    50          
    100          
    100          
    50          
    50          
    100          
    50          
    0          
    100          
    0          
1113 14           break;
1114              
1115             case BER_TYPE_OID:
1116 95           encode_oid (data, 0);
1117 95           break;
1118              
1119             case BER_TYPE_RELOID:
1120 2           encode_oid (data, 1);
1121 2           break;
1122              
1123             case BER_TYPE_INT:
1124 103           encode_int (data);
1125 103           break;
1126              
1127             case BER_TYPE_BYTES:
1128             {
1129             STRLEN len;
1130 227 50         const char *ptr = SvPVbyte (data, len);
1131 227           encode_data (ptr, len);
1132             }
1133 227           break;
1134              
1135             case BER_TYPE_UTF8:
1136             {
1137             STRLEN len;
1138 11 50         const char *ptr = SvPVutf8 (data, len);
1139 11           encode_data (ptr, len);
1140             }
1141 11           break;
1142              
1143             case BER_TYPE_IPADDRESS:
1144             {
1145             U8 ip[4];
1146 2 50         sscanf (SvPV_nolen (data), "%hhu.%hhu.%hhu.%hhu", ip + 0, ip + 1, ip + 2, ip + 3);
1147 2           encode_data ((const char *)ip, sizeof (ip));
1148             }
1149 2           break;
1150              
1151             case BER_TYPE_UCS2:
1152 4           encode_ucs (data, 2);
1153 4           break;
1154              
1155             case BER_TYPE_UCS4:
1156 4           encode_ucs (data, 4);
1157 4           break;
1158              
1159             case BER_TYPE_REAL:
1160 27           encode_real (data);
1161 27           break;
1162              
1163             case BER_TYPE_CROAK:
1164 0           croak ("class/tag %d/%d mapped to BER_TYPE_CROAK", klass, tag);
1165              
1166             default:
1167 0           croak ("unconfigured/unsupported class/tag %d/%d", klass, tag);
1168             }
1169              
1170 761           }
1171              
1172             /////////////////////////////////////////////////////////////////////////////
1173              
1174             MODULE = Convert::BER::XS PACKAGE = Convert::BER::XS
1175              
1176             PROTOTYPES: ENABLE
1177              
1178             BOOT:
1179             {
1180 7           HV *stash = gv_stashpv ("Convert::BER::XS", 1);
1181              
1182 7           profile_stash = gv_stashpv ("Convert::BER::XS::Profile", 1);
1183              
1184             static const struct {
1185             const char *name;
1186             IV iv;
1187             } *civ, const_iv[] = {
1188             #define const_iv(name) { # name, name },
1189             const_iv (ASN_BOOLEAN)
1190             const_iv (ASN_INTEGER)
1191             const_iv (ASN_BIT_STRING)
1192             const_iv (ASN_OCTET_STRING)
1193             const_iv (ASN_NULL)
1194             const_iv (ASN_OBJECT_IDENTIFIER)
1195             const_iv (ASN_OBJECT_DESCRIPTOR)
1196             const_iv (ASN_OID)
1197             const_iv (ASN_EXTERNAL)
1198             const_iv (ASN_REAL)
1199             const_iv (ASN_SEQUENCE)
1200             const_iv (ASN_ENUMERATED)
1201             const_iv (ASN_EMBEDDED_PDV)
1202             const_iv (ASN_UTF8_STRING)
1203             const_iv (ASN_RELATIVE_OID)
1204             const_iv (ASN_SET)
1205             const_iv (ASN_NUMERIC_STRING)
1206             const_iv (ASN_PRINTABLE_STRING)
1207             const_iv (ASN_TELETEX_STRING)
1208             const_iv (ASN_T61_STRING)
1209             const_iv (ASN_VIDEOTEX_STRING)
1210             const_iv (ASN_IA5_STRING)
1211             const_iv (ASN_ASCII_STRING)
1212             const_iv (ASN_UTC_TIME)
1213             const_iv (ASN_GENERALIZED_TIME)
1214             const_iv (ASN_GRAPHIC_STRING)
1215             const_iv (ASN_VISIBLE_STRING)
1216             const_iv (ASN_ISO646_STRING)
1217             const_iv (ASN_GENERAL_STRING)
1218             const_iv (ASN_UNIVERSAL_STRING)
1219             const_iv (ASN_CHARACTER_STRING)
1220             const_iv (ASN_BMP_STRING)
1221              
1222             const_iv (ASN_UNIVERSAL)
1223             const_iv (ASN_APPLICATION)
1224             const_iv (ASN_CONTEXT)
1225             const_iv (ASN_PRIVATE)
1226              
1227             const_iv (BER_CLASS)
1228             const_iv (BER_TAG)
1229             const_iv (BER_FLAGS)
1230             const_iv (BER_DATA)
1231              
1232             const_iv (BER_TYPE_BYTES)
1233             const_iv (BER_TYPE_UTF8)
1234             const_iv (BER_TYPE_UCS2)
1235             const_iv (BER_TYPE_UCS4)
1236             const_iv (BER_TYPE_INT)
1237             const_iv (BER_TYPE_OID)
1238             const_iv (BER_TYPE_RELOID)
1239             const_iv (BER_TYPE_NULL)
1240             const_iv (BER_TYPE_BOOL)
1241             const_iv (BER_TYPE_REAL)
1242             const_iv (BER_TYPE_IPADDRESS)
1243             const_iv (BER_TYPE_CROAK)
1244              
1245             const_iv (SNMP_IPADDRESS)
1246             const_iv (SNMP_COUNTER32)
1247             const_iv (SNMP_GAUGE32)
1248             const_iv (SNMP_UNSIGNED32)
1249             const_iv (SNMP_TIMETICKS)
1250             const_iv (SNMP_OPAQUE)
1251             const_iv (SNMP_COUNTER64)
1252             };
1253              
1254 420 100         for (civ = const_iv + sizeof (const_iv) / sizeof (const_iv [0]); civ > const_iv; civ--)
1255 413           newCONSTSUB (stash, (char *)civ[-1].name, newSViv (civ[-1].iv));
1256             }
1257              
1258             void
1259             ber_decode (SV *ber, SV *profile = &PL_sv_undef)
1260             ALIAS:
1261             ber_decode_prefix = 1
1262             PPCODE:
1263             {
1264 1088           cur_profile = SvPROFILE (profile);
1265             STRLEN len;
1266 1088 50         buf = (U8 *)SvPVbyte (ber, len);
1267 1088           cur = buf;
1268 1088           end = buf + len;
1269              
1270 1088           PUTBACK;
1271 1088           SV *tuple = decode_ber ();
1272 515           SPAGAIN;
1273              
1274 515 50         EXTEND (SP, 2);
1275 515           PUSHs (sv_2mortal (tuple));
1276              
1277 515 100         if (ix)
1278 68           PUSHs (sv_2mortal (newSViv (cur - buf)));
1279 447 100         else if (cur != end)
1280 68           error ("trailing garbage after BER value");
1281             }
1282              
1283             void
1284             ber_is (SV *tuple, SV *klass = &PL_sv_undef, SV *tag = &PL_sv_undef, SV *flags = &PL_sv_undef, SV *data = &PL_sv_undef)
1285             PPCODE:
1286             {
1287 84 50         if (!SvOK (tuple))
    0          
    0          
1288 0           XSRETURN_NO;
1289              
1290 84 50         if (!SvROK (tuple) || SvTYPE (SvRV (tuple)) != SVt_PVAV)
    50          
1291 0           croak ("ber_is: tuple must be BER tuple (array-ref)");
1292              
1293 84           AV *av = (AV *)SvRV (tuple);
1294              
1295 84 50         XPUSHs (
    100          
    50          
    50          
    50          
    50          
    100          
    100          
    50          
    50          
    50          
    50          
    100          
    100          
    50          
    50          
    50          
    50          
    100          
    100          
    50          
    50          
    100          
1296             (!SvOK (klass) || SvIV (AvARRAY (av)[BER_CLASS]) == SvIV (klass))
1297             && (!SvOK (tag) || SvIV (AvARRAY (av)[BER_TAG ]) == SvIV (tag))
1298             && (!SvOK (flags) || !SvIV (AvARRAY (av)[BER_FLAGS]) == !SvIV (flags))
1299             && (!SvOK (data) || sv_eq (AvARRAY (av)[BER_DATA ], data))
1300             ? &PL_sv_yes : &PL_sv_undef);
1301             }
1302              
1303             void
1304             ber_is_seq (SV *tuple)
1305             PPCODE:
1306             {
1307 3 50         if (!SvOK (tuple))
    0          
    0          
1308 0           XSRETURN_UNDEF;
1309              
1310 3           AV *av = ber_tuple (tuple);
1311              
1312 3 50         XPUSHs (
    50          
    100          
    0          
    50          
    50          
    0          
    50          
    100          
    0          
1313             SvIV (AvARRAY (av)[BER_CLASS]) == ASN_UNIVERSAL
1314             && SvIV (AvARRAY (av)[BER_TAG ]) == ASN_SEQUENCE
1315             && SvIV (AvARRAY (av)[BER_FLAGS])
1316             ? AvARRAY (av)[BER_DATA] : &PL_sv_undef);
1317             }
1318              
1319             void
1320             ber_is_int (SV *tuple, SV *value = &PL_sv_undef)
1321             PPCODE:
1322             {
1323 10 50         if (!SvOK (tuple))
    0          
    0          
1324 0           XSRETURN_NO;
1325              
1326 10           AV *av = ber_tuple (tuple);
1327              
1328 10 50         UV data = SvUV (AvARRAY (av)[BER_DATA]);
1329              
1330 10 50         XPUSHs (
    50          
    100          
    0          
    50          
    50          
    0          
    50          
    100          
    0          
    100          
    50          
    50          
    50          
    50          
    100          
1331             SvIV (AvARRAY (av)[BER_CLASS]) == ASN_UNIVERSAL
1332             && SvIV (AvARRAY (av)[BER_TAG ]) == ASN_INTEGER
1333             && !SvIV (AvARRAY (av)[BER_FLAGS])
1334             && (!SvOK (value) || data == SvUV (value))
1335             ? sv_2mortal (data ? newSVsv (AvARRAY (av)[BER_DATA]) : newSVpv ("0 but true", 0))
1336             : &PL_sv_undef);
1337             }
1338              
1339             void
1340             ber_is_oid (SV *tuple, SV *oid = &PL_sv_undef)
1341             PPCODE:
1342             {
1343 4 50         if (!SvOK (tuple))
    0          
    0          
1344 0           XSRETURN_NO;
1345              
1346 4           AV *av = ber_tuple (tuple);
1347              
1348 4 50         XPUSHs (
    50          
    100          
    0          
    50          
    100          
    0          
    50          
    50          
    0          
    100          
    50          
    50          
    50          
1349             SvIV (AvARRAY (av)[BER_CLASS]) == ASN_UNIVERSAL
1350             && SvIV (AvARRAY (av)[BER_TAG ]) == ASN_OBJECT_IDENTIFIER
1351             && !SvIV (AvARRAY (av)[BER_FLAGS])
1352             && (!SvOK (oid) || sv_eq (AvARRAY (av)[BER_DATA], oid))
1353             ? newSVsv (AvARRAY (av)[BER_DATA]) : &PL_sv_undef);
1354             }
1355              
1356             #############################################################################
1357              
1358             void
1359             ber_encode (SV *tuple, SV *profile = &PL_sv_undef)
1360             PPCODE:
1361             {
1362 183           cur_profile = SvPROFILE (profile);
1363 183           buf_sv = sv_2mortal (NEWSV (0, 256));
1364 183           SvPOK_only (buf_sv);
1365 183           set_buf (buf_sv);
1366              
1367 183           PUTBACK;
1368 183           encode_ber (tuple);
1369 183           SPAGAIN;
1370              
1371 183           SvCUR_set (buf_sv, cur - buf);
1372 183 50         XPUSHs (buf_sv);
1373             }
1374              
1375             SV *
1376             ber_int (SV *sv)
1377             CODE:
1378             {
1379 2           AV *av = newAV ();
1380 2           av_fill (av, BER_ARRAYSIZE - 1);
1381 2           AvARRAY (av)[BER_CLASS] = newSVcacheint (ASN_UNIVERSAL);
1382 2           AvARRAY (av)[BER_TAG ] = newSVcacheint (ASN_INTEGER);
1383 2           AvARRAY (av)[BER_FLAGS] = newSVcacheint (0);
1384 2           AvARRAY (av)[BER_DATA ] = newSVsv (sv);
1385 2           RETVAL = newRV_noinc ((SV *)av);
1386             }
1387             OUTPUT: RETVAL
1388              
1389             # TODO: not arrayref, but elements?
1390             SV *
1391             ber_seq (SV *arrayref)
1392             CODE:
1393             {
1394 0           AV *av = newAV ();
1395 0           av_fill (av, BER_ARRAYSIZE - 1);
1396 0           AvARRAY (av)[BER_CLASS] = newSVcacheint (ASN_UNIVERSAL);
1397 0           AvARRAY (av)[BER_TAG ] = newSVcacheint (ASN_SEQUENCE);
1398 0           AvARRAY (av)[BER_FLAGS] = newSVcacheint (1);
1399 0           AvARRAY (av)[BER_DATA ] = newSVsv (arrayref);
1400 0           RETVAL = newRV_noinc ((SV *)av);
1401             }
1402             OUTPUT: RETVAL
1403              
1404             MODULE = Convert::BER::XS PACKAGE = Convert::BER::XS::Profile
1405              
1406             SV *
1407             new (SV *klass)
1408             CODE:
1409 30           RETVAL = profile_new ();
1410             OUTPUT: RETVAL
1411              
1412             void
1413             set (SV *profile, int klass, int tag, int type)
1414             CODE:
1415 443           profile_set (SvPROFILE (profile), klass, tag, type);
1416              
1417             IV
1418             get (SV *profile, int klass, int tag)
1419             CODE:
1420 0           RETVAL = profile_lookup (SvPROFILE (profile), klass, tag);
1421             OUTPUT: RETVAL
1422              
1423             void
1424             _set_default (SV *profile)
1425             CODE:
1426 8           default_profile = SvPROFILE (profile);
1427              
1428