--- Convert-BER-XS/XS.xs 2019/04/20 01:03:59 1.6 +++ Convert-BER-XS/XS.xs 2019/04/20 01:31:07 1.7 @@ -389,6 +389,42 @@ return newSVpvn (oid, app - oid); } +// TODO: this is unacceptably slow +static SV * +decode_ucs (int chrsize) +{ + SV *res = NEWSV (0, 0); + + U32 len = get_length (); + + if (len & (chrsize - 1)) + croak ("BER_TYPE_UCS has an invalid number of octets (%d)", len); + + while (len) + { + U8 b1 = get_u8 (); + U8 b2 = get_u8 (); + U32 chr = (b1 << 8) | b2; + + if (chrsize == 4) + { + U8 b3 = get_u8 (); + U8 b4 = get_u8 (); + chr = (chr << 16) | (b3 << 8) | b4; + } + + U8 uchr [UTF8_MAXBYTES]; + int uclen = uvuni_to_utf8 (uchr, chr) - uchr; + + sv_catpvn (res, (const char *)uchr, uclen); + len -= chrsize; + } + + SvUTF8_on (res); + + return res; +} + static SV * decode_ber () { @@ -475,9 +511,15 @@ } break; - case BER_TYPE_REAL: case BER_TYPE_UCS2: + res = decode_ucs (2); + break; + case BER_TYPE_UCS4: + res = decode_ucs (4); + break; + + case BER_TYPE_REAL: case BER_TYPE_CROAK: default: croak ("unconfigured/unsupported class/tag %d/%d", klass, tag); @@ -591,7 +633,7 @@ static void put_length (U32 val) { - need (5); + need (5 + val); cur = put_length_at (val, cur); } @@ -607,7 +649,6 @@ encode_data (const char *ptr, STRLEN len) { put_length (len); - need (len); memcpy (cur, ptr, len); cur += len; } @@ -763,6 +804,33 @@ } static void +encode_ucs (SV *data, int chrsize) +{ + STRLEN uchars = sv_len_utf8 (data); + STRLEN len;; + char *ptr = SvPVutf8 (data, len); + + put_length (uchars * chrsize); + + while (uchars--) + { + STRLEN uclen; + UV uchr = utf8_to_uvchr_buf (ptr, ptr + len, &uclen); + + ptr += uclen; + len -= uclen; + + if (chrsize == 4) + { + *cur++ = uchr >> 24; + *cur++ = uchr >> 16; + } + + *cur++ = uchr >> 8; + *cur++ = uchr; + } +} +static void encode_ber (SV *tuple) { AV *av = ber_tuple (tuple); @@ -812,7 +880,7 @@ case BER_TYPE_BOOL: put_length (1); - put_u8 (SvTRUE (data) ? 0xff : 0x00); + *cur++ = SvTRUE (data) ? 0xff : 0x00; break; case BER_TYPE_OID: @@ -851,9 +919,15 @@ } break; - case BER_TYPE_REAL: case BER_TYPE_UCS2: + encode_ucs (data, 2); + break; + case BER_TYPE_UCS4: + encode_ucs (data, 4); + break; + + case BER_TYPE_REAL: case BER_TYPE_CROAK: default: croak ("unconfigured/unsupported class/tag %d/%d", klass, tag);