--- JSON-XS/XS.xs 2007/07/01 23:40:07 1.49 +++ JSON-XS/XS.xs 2007/07/10 16:22:31 1.54 @@ -64,7 +64,8 @@ typedef struct { U32 flags; - SV *cb_object, *cb_sk_object; + SV *cb_object; + HV *cb_sk_object; } JSON; ///////////////////////////////////////////////////////////////////////////// @@ -474,10 +475,10 @@ { if (SvSTASH (sv) == json_boolean_stash) { - if (SvIV (sv) == 0) - encode_str (enc, "false", 5, 0); - else + if (SvIV (sv)) encode_str (enc, "true", 4, 0); + else + encode_str (enc, "false", 5, 0); } else { @@ -490,20 +491,29 @@ if (enc->json.flags & F_CONV_BLESSED) { // we re-bless the reference to get overload and other niceties right - GV *to_json = gv_fetchmethod_autoload (SvSTASH (sv), "TO_JSON", 1); + GV *to_json = gv_fetchmethod_autoload (SvSTASH (sv), "TO_JSON", 0); if (to_json) { - dSP; ENTER; SAVETMPS; PUSHMARK (SP); + int count; + dSP; + + ENTER; SAVETMPS; PUSHMARK (SP); XPUSHs (sv_bless (sv_2mortal (newRV_inc (sv)), SvSTASH (sv))); - // calling with G_SCALAR ensures that we always get a 1 reutrn value - // check anyways. + // calling with G_SCALAR ensures that we always get a 1 return value PUTBACK; - assert (1 == call_sv ((SV *)GvCV (to_json), G_SCALAR)); + call_sv ((SV *)GvCV (to_json), G_SCALAR); SPAGAIN; - encode_sv (enc, POPs); + // catch this surprisingly common error + if (SvROK (TOPs) && SvRV (TOPs) == sv) + croak ("%s::TO_JSON method returned same object as was passed instead of a new one", HvNAME (SvSTASH (sv))); + + sv = POPs; + PUTBACK; + + encode_sv (enc, sv); FREETMPS; LEAVE; } @@ -526,10 +536,13 @@ encode_av (enc, (AV *)sv); else if (svt < SVt_PVAV) { - if (SvNIOK (sv) && SvIV (sv) == 0) - encode_str (enc, "false", 5, 0); - else if (SvNIOK (sv) && SvIV (sv) == 1) + STRLEN len = 0; + char *pv = svt ? SvPV (sv, len) : 0; + + if (len == 1 && *pv == '1') encode_str (enc, "true", 4, 0); + else if (len == 1 && *pv == '0') + encode_str (enc, "false", 5, 0); else croak ("cannot encode reference to scalar '%s' unless the scalar is 0 or 1", SvPV_nolen (sv_2mortal (newRV_inc (sv)))); @@ -1097,42 +1110,62 @@ // check filter callbacks if (dec->json.flags & F_HOOK) { - ENTER; SAVETMPS; - - if (HvKEYS (hv) == 1 && dec->json.cb_sk_object) + if (dec->json.cb_sk_object && HvKEYS (hv) == 1) { - int count; + HE *cb, *he; - dSP; PUSHMARK (SP); - XPUSHs (sv_2mortal (sv)); + hv_iterinit (hv); + he = hv_iternext (hv); + hv_iterinit (hv); - PUTBACK; count = call_sv (dec->json.cb_sk_object, G_ARRAY); SPAGAIN; + // the next line creates a mortal sv each time its called. + // might want to optimise this for common cases. + cb = hv_fetch_ent (dec->json.cb_sk_object, hv_iterkeysv (he), 0, 0); - if (count == 1) - sv = newSVsv (POPs); - else - SvREFCNT_inc (sv); + if (cb) + { + dSP; + int count; + + ENTER; SAVETMPS; PUSHMARK (SP); + XPUSHs (HeVAL (he)); + + PUTBACK; count = call_sv (HeVAL (cb), G_ARRAY); SPAGAIN; + + if (count == 1) + { + sv = newSVsv (POPs); + FREETMPS; LEAVE; + return sv; + } + + FREETMPS; LEAVE; + } } if (dec->json.cb_object) { + dSP; int count; - dSP; ENTER; SAVETMPS; PUSHMARK (SP); + ENTER; SAVETMPS; PUSHMARK (SP); XPUSHs (sv_2mortal (sv)); PUTBACK; count = call_sv (dec->json.cb_object, G_ARRAY); SPAGAIN; if (count == 1) - sv = newSVsv (POPs); - else - SvREFCNT_inc (sv); - } + { + sv = newSVsv (POPs); + FREETMPS; LEAVE; + return sv; + } - FREETMPS; LEAVE; + SvREFCNT_inc (sv); + FREETMPS; LEAVE; + } } - return newRV_noinc ((SV *)hv); + return sv; fail: SvREFCNT_dec (hv); @@ -1374,18 +1407,32 @@ XPUSHs (ST (0)); } -void filter_json_objects (JSON *self, SV *cb = &PL_sv_undef) - ALIAS: - filter_sk_json_objects = 1 +void filter_json_object (JSON *self, SV *cb = &PL_sv_undef) + PPCODE: +{ + SvREFCNT_dec (self->cb_object); + self->cb_object = SvOK (cb) ? newSVsv (cb) : 0; + + XPUSHs (ST (0)); +} + +void filter_json_single_key_object (JSON *self, SV *key, SV *cb = &PL_sv_undef) PPCODE: { - if (!SvOK (cb)) - cb = 0; + if (!self->cb_sk_object) + self->cb_sk_object = newHV (); - switch (ix) + if (SvOK (cb)) + hv_store_ent (self->cb_sk_object, key, newSVsv (cb), 0); + else { - case 0: self->cb_object = cb; break; - case 1: self->cb_sk_object = cb; break; + hv_delete_ent (self->cb_sk_object, key, G_DISCARD, 0); + + if (!HvKEYS (self->cb_sk_object)) + { + SvREFCNT_dec (self->cb_sk_object); + self->cb_sk_object = 0; + } } XPUSHs (ST (0)); @@ -1408,6 +1455,11 @@ PUSHs (sv_2mortal (newSVuv (offset))); } +void DESTROY (JSON *self) + CODE: + SvREFCNT_dec (self->cb_sk_object); + SvREFCNT_dec (self->cb_object); + PROTOTYPES: ENABLE void to_json (SV *scalar)