--- CBOR-XS/XS.xs 2020/11/30 20:38:25 1.69 +++ CBOR-XS/XS.xs 2021/10/23 03:00:31 1.73 @@ -30,6 +30,9 @@ # define SvREFCNT_dec_NN(sv) SvREFCNT_dec (sv) #endif +// perl's is_utf8_string interprets len=0 as "calculate len", but we want it to mean 0 +#define cbor_is_utf8_string(str,len) (!(len) || is_utf8_string ((str), (len))) + // known major and minor types enum cbor_type { @@ -111,6 +114,7 @@ AS_FLOAT16 = 4, AS_FLOAT32 = 5, AS_FLOAT64 = 6, + AS_MAP = 7, // possibly future enhancements: (generic) float, (generic) string }; @@ -202,7 +206,7 @@ } // minimum length of a string to be registered for stringref -ecb_inline int +ecb_inline STRLEN minimum_string_length (UV idx) { return idx <= 23 ? 3 @@ -241,6 +245,8 @@ } } +static void encode_sv (enc_t *enc, SV *sv); + ecb_inline void encode_ch (enc_t *enc, char ch) { @@ -425,6 +431,38 @@ encode_ch (enc, istrue ? MAJOR_MISC | SIMPLE_TRUE : MAJOR_MISC | SIMPLE_FALSE); } +// encodes an arrayref containing key-value pairs as CBOR map +ecb_inline void +encode_array_as_map (enc_t *enc, SV *sv) +{ + if (enc->depth >= enc->cbor.max_depth) + croak (ERR_NESTING_EXCEEDED); + + ++enc->depth; + + // as_map does error checking for us, but we re-check in case + // things have changed. + + if (!SvROK (sv) || SvTYPE (SvRV (sv)) != SVt_PVAV) + croak ("CBOR::XS::as_map requires an array reference (did you change the array after calling as_map?)"); + + AV *av = (AV *)SvRV (sv); + int i, len = av_len (av); + + if (!(len & 1)) + croak ("CBOR::XS::as_map requires an even number of elements (did you change the array after calling as_map?)"); + + encode_uint (enc, MAJOR_MAP, (len + 1) >> 1); + + for (i = 0; i <= len; ++i) + { + SV **svp = av_fetch (av, i, 0); + encode_sv (enc, svp ? *svp : &PL_sv_undef); + } + + --enc->depth; +} + ecb_inline void encode_forced (enc_t *enc, UV type, SV *sv) { @@ -463,13 +501,13 @@ case AS_FLOAT32: encode_float32 (enc, SvNV (sv)); break; case AS_FLOAT64: encode_float64 (enc, SvNV (sv)); break; + case AS_MAP: encode_array_as_map (enc, sv); break; + default: croak ("encountered malformed CBOR::XS::Tagged object"); } } -static void encode_sv (enc_t *enc, SV *sv); - static void encode_av (enc_t *enc, AV *av) { @@ -954,7 +992,7 @@ dec->cur += len; if (ecb_expect_false (dec->cbor.flags & F_VALIDATE_UTF8)) - if (!is_utf8_string (key, len)) + if (!cbor_is_utf8_string ((U8 *)key, len)) ERR ("corrupted CBOR data (invalid UTF-8 in map key)"); hv_store (hv, key, -len, decode_sv (dec), 0); @@ -1093,7 +1131,7 @@ if (utf8) { if (ecb_expect_false (dec->cbor.flags & F_VALIDATE_UTF8)) - if (!is_utf8_string (SvPVX (sv), SvCUR (sv))) + if (!cbor_is_utf8_string (SvPVX (sv), SvCUR (sv))) ERR ("corrupted CBOR data (invalid UTF-8 in text string)"); SvUTF8_on (sv); @@ -1536,6 +1574,7 @@ case MAJOR_MAP >> MAJOR_SHIFT: len <<= 1; + /* FALLTHROUGH */ case MAJOR_ARRAY >> MAJOR_SHIFT: if (len) {