--- CBOR-XS/XS.xs 2013/11/20 16:29:02 1.24 +++ CBOR-XS/XS.xs 2013/11/28 12:08:07 1.32 @@ -21,17 +21,20 @@ #ifndef HvNAMEUTF8 # define HvNAMEUTF8(hv) 0 #endif +#ifndef SvREFCNT_dec_NN +# define SvREFCNT_dec_NN(sv) SvREFCNT_dec (sv) +#endif // known tags enum cbor_tag { - // inofficial extensions (pending iana registration) - CBOR_TAG_PERL_OBJECT = 24, // http://cbor.schmorp.de/perl-object - CBOR_TAG_GENERIC_OBJECT = 25, // http://cbor.schmorp.de/generic-object - CBOR_TAG_VALUE_SHAREABLE = 26, // http://cbor.schmorp.de/value-sharing - CBOR_TAG_VALUE_SHAREDREF = 27, // http://cbor.schmorp.de/value-sharing - CBOR_TAG_STRINGREF_NAMESPACE = 65537, // http://cbor.schmorp.de/stringref - CBOR_TAG_STRINGREF = 28, // http://cbor.schmorp.de/stringref + // extensions + CBOR_TAG_STRINGREF = 25, // http://cbor.schmorp.de/stringref + CBOR_TAG_PERL_OBJECT = 26, // http://cbor.schmorp.de/perl-object + CBOR_TAG_GENERIC_OBJECT = 27, // http://cbor.schmorp.de/generic-object + CBOR_TAG_VALUE_SHAREABLE = 28, // http://cbor.schmorp.de/value-sharing + CBOR_TAG_VALUE_SHAREDREF = 29, // http://cbor.schmorp.de/value-sharing + CBOR_TAG_STRINGREF_NAMESPACE = 256, // http://cbor.schmorp.de/stringref CBOR_TAG_INDIRECTION = 22098, // http://cbor.schmorp.de/indirection // rfc7049 @@ -58,8 +61,8 @@ #define F_SHRINK 0x00000001UL #define F_ALLOW_UNKNOWN 0x00000002UL -#define F_ALLOW_SHARING 0x00000004UL //TODO -#define F_ALLOW_STRINGREF 0x00000008UL //TODO +#define F_ALLOW_SHARING 0x00000004UL +#define F_PACK_STRINGS 0x00000008UL #define INIT_SIZE 32 // initial scalar size to be allocated @@ -81,12 +84,13 @@ #endif static HV *cbor_stash, *types_boolean_stash, *types_error_stash, *cbor_tagged_stash; // CBOR::XS:: -static SV *types_true, *types_false, *types_error, *sv_cbor; +static SV *types_true, *types_false, *types_error, *sv_cbor, *default_filter; typedef struct { U32 flags; U32 max_depth; STRLEN max_size; + SV *filter; } CBOR; ecb_inline void @@ -96,6 +100,12 @@ cbor->max_depth = 512; } +ecb_inline void +cbor_free (CBOR *cbor) +{ + SvREFCNT_dec (cbor->filter); +} + ///////////////////////////////////////////////////////////////////////////// // utility functions @@ -133,8 +143,8 @@ ? idx > 0xffU ? idx > 0xffffU ? idx > 0xffffffffU - ? 7 - : 6 + ? 11 + : 7 : 5 : 4 : 3; @@ -181,9 +191,9 @@ { need (enc, 9); - if (len < 24) + if (ecb_expect_true (len < 24)) *enc->cur++ = major | len; - else if (len <= 0xff) + else if (ecb_expect_true (len <= 0xff)) { *enc->cur++ = major | 24; *enc->cur++ = len; @@ -222,10 +232,19 @@ encode_uint (enc, 0xc0, tag); } -static void +ecb_inline void encode_str (enc_t *enc, int utf8, char *str, STRLEN len) { - if (ecb_expect_false (enc->cbor.flags & F_ALLOW_STRINGREF)) + encode_uint (enc, utf8 ? 0x60 : 0x40, len); + need (enc, len); + memcpy (enc->cur, str, len); + enc->cur += len; +} + +static void +encode_strref (enc_t *enc, int utf8, char *str, STRLEN len) +{ + if (ecb_expect_false (enc->cbor.flags & F_PACK_STRINGS)) { SV **svp = hv_fetch (enc->stringref[!!utf8], str, len, 1); @@ -244,10 +263,7 @@ } } - encode_uint (enc, utf8 ? 0x60 : 0x40, len); - need (enc, len); - memcpy (enc->cur, str, len); - enc->cur += len; + encode_str (enc, utf8, str, len); } static void encode_sv (enc_t *enc, SV *sv); @@ -273,11 +289,6 @@ --enc->depth; } -ecb_inline void -encode_he (enc_t *enc, HE *he) -{ -} - static void encode_hv (enc_t *enc, HV *hv) { @@ -301,7 +312,7 @@ if (HeKLEN (he) == HEf_SVKEY) encode_sv (enc, HeSVKEY (he)); else - encode_str (enc, HeKUTF8 (he), HeKEY (he), HeKLEN (he)); + encode_strref (enc, HeKUTF8 (he), HeKEY (he), HeKLEN (he)); encode_sv (enc, ecb_expect_false (mg) ? hv_iterval (hv, he) : HeVAL (he)); } @@ -412,7 +423,7 @@ encode_tag (enc, CBOR_TAG_PERL_OBJECT); encode_uint (enc, 0x80, count + 1); - encode_str (enc, HvNAMEUTF8 (stash), HvNAME (stash), HvNAMELEN (stash)); + encode_strref (enc, HvNAMEUTF8 (stash), HvNAME (stash), HvNAMELEN (stash)); while (count) encode_sv (enc, SP[1 - count--]); @@ -481,7 +492,7 @@ { STRLEN len; char *str = SvPV (sv, len); - encode_str (enc, SvUTF8 (sv), str, len); + encode_strref (enc, SvUTF8 (sv), str, len); } else if (SvNOKp (sv)) encode_nv (enc, sv); @@ -517,7 +528,7 @@ SvPOK_only (enc.sv); - if (cbor->flags & F_ALLOW_STRINGREF) + if (cbor->flags & F_PACK_STRINGS) { encode_tag (&enc, CBOR_TAG_STRINGREF_NAMESPACE); enc.stringref[0]= (HV *)sv_2mortal ((SV *)newHV ()); @@ -549,6 +560,7 @@ U32 maxdepth; // recursion depth limit AV *shareable; AV *stringref; + SV *decode_tagged; } dec_t; #define ERR(reason) SB if (!dec->err) dec->err = reason; goto fail; SE @@ -768,15 +780,15 @@ WANT (len); sv = newSVpvn (dec->cur, len); dec->cur += len; + + if (ecb_expect_false (dec->stringref) + && SvCUR (sv) >= minimum_string_length (AvFILLp (dec->stringref) + 1)) + av_push (dec->stringref, SvREFCNT_inc_NN (sv)); } if (utf8) SvUTF8_on (sv); - if (ecb_expect_false (dec->stringref) - && SvCUR (sv) >= minimum_string_length (AvFILLp (dec->stringref) + 1)) - av_push (dec->stringref, SvREFCNT_inc_NN (sv)); - return sv; fail: @@ -912,15 +924,42 @@ { sv = decode_sv (dec); - AV *av = newAV (); - av_push (av, newSVuv (tag)); - av_push (av, sv); - - HV *tagged_stash = !CBOR_SLOW || cbor_tagged_stash - ? cbor_tagged_stash - : gv_stashpv ("CBOR::XS::Tagged" , 1); + dSP; + ENTER; SAVETMPS; PUSHMARK (SP); + EXTEND (SP, 2); + PUSHs (newSVuv (tag)); + PUSHs (sv); - sv = sv_bless (newRV_noinc ((SV *)av), tagged_stash); + PUTBACK; + int count = call_sv (dec->cbor.filter ? dec->cbor.filter : default_filter, G_ARRAY | G_EVAL); + SPAGAIN; + + if (SvTRUE (ERRSV)) + { + FREETMPS; LEAVE; + ERR (SvPVutf8_nolen (sv_2mortal (SvREFCNT_inc (ERRSV)))); + } + + if (count) + { + SvREFCNT_dec (sv); + sv = SvREFCNT_inc (POPs); + } + else + { + AV *av = newAV (); + av_push (av, newSVuv (tag)); + av_push (av, sv); + + HV *tagged_stash = !CBOR_SLOW || cbor_tagged_stash + ? cbor_tagged_stash + : gv_stashpv ("CBOR::XS::Tagged" , 1); + sv = sv_bless (newRV_noinc ((SV *)av), tagged_stash); + } + + PUTBACK; + + FREETMPS; LEAVE; } break; } @@ -1076,6 +1115,8 @@ types_false = get_bool ("Types::Serialiser::false"); types_error = get_bool ("Types::Serialiser::error"); + default_filter = newSVpv ("CBOR::XS::default_filter", 0); + sv_cbor = newSVpv ("CBOR", 0); SvREADONLY_on (sv_cbor); } @@ -1106,7 +1147,7 @@ shrink = F_SHRINK allow_unknown = F_ALLOW_UNKNOWN allow_sharing = F_ALLOW_SHARING - allow_stringref = F_ALLOW_STRINGREF + pack_strings = F_PACK_STRINGS PPCODE: { if (enable) @@ -1122,7 +1163,7 @@ get_shrink = F_SHRINK get_allow_unknown = F_ALLOW_UNKNOWN get_allow_sharing = F_ALLOW_SHARING - get_allow_stringref = F_ALLOW_STRINGREF + get_pack_strings = F_PACK_STRINGS PPCODE: XPUSHs (boolSV (self->flags & ix)); @@ -1148,6 +1189,18 @@ OUTPUT: RETVAL +void filter (CBOR *self, SV *filter = 0) + PPCODE: + SvREFCNT_dec (self->filter); + self->filter = filter ? newSVsv (filter) : filter; + XPUSHs (ST (0)); + +SV *get_filter (CBOR *self) + CODE: + RETVAL = self->filter ? self->filter : NEWSV (0, 0); + OUTPUT: + RETVAL + void encode (CBOR *self, SV *scalar) PPCODE: PUTBACK; scalar = encode_cbor (scalar, self); SPAGAIN; @@ -1169,6 +1222,10 @@ PUSHs (sv_2mortal (newSVuv (offset - SvPVX (cborstr)))); } +void DESTROY (CBOR *self) + PPCODE: + cbor_free (self); + PROTOTYPES: ENABLE void encode_cbor (SV *scalar)