--- JSON-XS/XS.xs 2007/07/01 22:20:00 1.48 +++ JSON-XS/XS.xs 2007/07/10 15:45:34 1.53 @@ -27,11 +27,12 @@ #define F_ALLOW_NONREF 0x00000100UL #define F_SHRINK 0x00000200UL #define F_ALLOW_BLESSED 0x00000400UL -#define F_CONV_BLESSED 0x00000800UL // NYI +#define F_CONV_BLESSED 0x00000800UL #define F_MAXDEPTH 0xf8000000UL #define S_MAXDEPTH 27 #define F_MAXSIZE 0x01f00000UL #define S_MAXSIZE 20 +#define F_HOOK 0x00080000UL // some hooks exist, so slow-path processing #define DEC_DEPTH(flags) (1UL << ((flags & F_MAXDEPTH) >> S_MAXDEPTH)) #define DEC_SIZE(flags) (1UL << ((flags & F_MAXSIZE ) >> S_MAXSIZE )) @@ -63,6 +64,8 @@ typedef struct { U32 flags; + SV *cb_object; + HV *cb_sk_object; } JSON; ///////////////////////////////////////////////////////////////////////////// @@ -472,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 { @@ -488,26 +491,31 @@ 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); + + 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 + // calling with G_SCALAR ensures that we always get a 1 return value // check anyways. PUTBACK; assert (1 == 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; + FREETMPS; LEAVE; } else if (enc->json.flags & F_ALLOW_BLESSED) encode_str (enc, "null", 4, 0); @@ -528,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)))); @@ -1010,6 +1021,7 @@ static SV * decode_hv (dec_t *dec) { + SV *sv; HV *hv = newHV (); DEC_INC_DEPTH; @@ -1093,7 +1105,67 @@ } DEC_DEC_DEPTH; - return newRV_noinc ((SV *)hv); + sv = newRV_noinc ((SV *)hv); + + // check filter callbacks + if (dec->json.flags & F_HOOK) + { + if (dec->json.cb_sk_object && HvKEYS (hv) == 1) + { + HE *cb, *he; + + hv_iterinit (hv); + he = hv_iternext (hv); + hv_iterinit (hv); + + // 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 (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; + + 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); + FREETMPS; LEAVE; + return sv; + } + + SvREFCNT_inc (sv); + FREETMPS; LEAVE; + } + } + + return sv; fail: SvREFCNT_dec (hv); @@ -1189,6 +1261,9 @@ dec.depth = 0; dec.maxdepth = DEC_DEPTH (dec.json.flags); + if (dec.json.cb_object || dec.json.cb_sk_object) + dec.json.flags |= F_HOOK; + *dec.end = 0; // this should basically be a nop, too, but make sure it's there sv = decode_sv (&dec); @@ -1272,7 +1347,7 @@ { SV *pv = NEWSV (0, sizeof (JSON)); SvPOK_only (pv); - Zero (SvPVX (pv), 1, sizeof (JSON)); + Zero (SvPVX (pv), 1, JSON); ((JSON *)SvPVX (pv))->flags = F_DEFAULT; XPUSHs (sv_2mortal (sv_bless (newRV_noinc (pv), json_stash))); } @@ -1332,6 +1407,37 @@ XPUSHs (ST (0)); } +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 (!self->cb_sk_object) + self->cb_sk_object = newHV (); + + if (SvOK (cb)) + hv_store_ent (self->cb_sk_object, key, newSVsv (cb), 0); + else + { + 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)); +} + void encode (JSON *self, SV *scalar) PPCODE: XPUSHs (encode_json (scalar, self)); @@ -1349,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)