--- JSON-XS/XS.xs 2007/07/01 14:08:03 1.47 +++ JSON-XS/XS.xs 2007/07/02 00:48:18 1.51 @@ -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 )) @@ -61,23 +62,15 @@ static HV *json_stash, *json_boolean_stash; // JSON::XS:: static SV *json_true, *json_false; -typedef struct json { +typedef struct { U32 flags; -} JSON__XS; + SV *cb_object, *cb_sk_object; +} JSON; ///////////////////////////////////////////////////////////////////////////// // utility functions -static UV * -SvJSON (SV *sv) -{ - if (!(SvROK (sv) && SvOBJECT (SvRV (sv)) && SvSTASH (SvRV (sv)) == json_stash)) - croak ("object is not of type JSON::XS"); - - return &SvUVX (SvRV (sv)); -} - -static void +inline void shrink (SV *sv) { sv_utf8_downgrade (sv, 1); @@ -122,7 +115,7 @@ char *cur; // SvPVX (sv) + current output position char *end; // SvEND (sv) SV *sv; // result scalar - struct json json; + JSON json; U32 indent; // indentation level U32 maxdepth; // max. indentation/recursion level } enc_t; @@ -481,10 +474,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 { @@ -501,10 +494,7 @@ if (to_json) { - dSP; - ENTER; - SAVETMPS; - PUSHMARK (SP); + 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 @@ -515,8 +505,7 @@ encode_sv (enc, POPs); - FREETMPS; - LEAVE; + FREETMPS; LEAVE; } else if (enc->json.flags & F_ALLOW_BLESSED) encode_str (enc, "null", 4, 0); @@ -537,10 +526,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)))); @@ -620,7 +612,7 @@ } static SV * -encode_json (SV *scalar, struct json *json) +encode_json (SV *scalar, JSON *json) { enc_t enc; @@ -658,7 +650,7 @@ char *cur; // current parser pointer char *end; // end of input string const char *err; // parse error, if != 0 - struct json json; + JSON json; U32 depth; // recursion depth U32 maxdepth; // recursion depth limit } dec_t; @@ -1019,6 +1011,7 @@ static SV * decode_hv (dec_t *dec) { + SV *sv; HV *hv = newHV (); DEC_INC_DEPTH; @@ -1102,7 +1095,54 @@ } DEC_DEC_DEPTH; - return newRV_noinc ((SV *)hv); + sv = newRV_noinc ((SV *)hv); + + // check filter callbacks + if (dec->json.flags & F_HOOK) + { + ENTER; SAVETMPS; + + if (dec->json.cb_sk_object && HvKEYS (hv) == 1) + { + int count; + + dSP; PUSHMARK (SP); + XPUSHs (sv_2mortal (sv)); + + PUTBACK; count = call_sv (dec->json.cb_sk_object, G_ARRAY); SPAGAIN; + + if (count == 1) + { + sv = newSVsv (POPs); + goto filter_ok; + } + + SvREFCNT_inc (sv); + } + + if (dec->json.cb_object) + { + int count; + + dSP; 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); + goto filter_ok; + } + + SvREFCNT_inc (sv); + } + +filter_ok: + FREETMPS; LEAVE; + } + + return sv; fail: SvREFCNT_dec (hv); @@ -1171,7 +1211,7 @@ } static SV * -decode_json (SV *string, struct json *json, UV *offset_return) +decode_json (SV *string, JSON *json, UV *offset_return) { dec_t dec; UV offset; @@ -1198,6 +1238,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); @@ -1276,13 +1319,17 @@ PROTOTYPES: DISABLE -SV *new (char *dummy) - CODE: - RETVAL = sv_bless (newRV_noinc (newSVuv (F_DEFAULT)), json_stash); - OUTPUT: - RETVAL +void new (char *klass) + PPCODE: +{ + SV *pv = NEWSV (0, sizeof (JSON)); + SvPOK_only (pv); + Zero (SvPVX (pv), 1, JSON); + ((JSON *)SvPVX (pv))->flags = F_DEFAULT; + XPUSHs (sv_2mortal (sv_bless (newRV_noinc (pv), json_stash))); +} -SV *ascii (SV *self, int enable = 1) +void ascii (JSON *self, int enable = 1) ALIAS: ascii = F_ASCII latin1 = F_LATIN1 @@ -1296,23 +1343,19 @@ shrink = F_SHRINK allow_blessed = F_ALLOW_BLESSED convert_blessed = F_CONV_BLESSED - CODE: + PPCODE: { - UV *uv = SvJSON (self); if (enable) - *uv |= ix; + self->flags |= ix; else - *uv &= ~ix; + self->flags &= ~ix; - RETVAL = newSVsv (self); + XPUSHs (ST (0)); } - OUTPUT: - RETVAL -SV *max_depth (SV *self, UV max_depth = 0x80000000UL) - CODE: +void max_depth (JSON *self, UV max_depth = 0x80000000UL) + PPCODE: { - UV *uv = SvJSON (self); UV log2 = 0; if (max_depth > 0x80000000UL) max_depth = 0x80000000UL; @@ -1320,17 +1363,14 @@ while ((1UL << log2) < max_depth) ++log2; - *uv = *uv & ~F_MAXDEPTH | (log2 << S_MAXDEPTH); + self->flags = self->flags & ~F_MAXDEPTH | (log2 << S_MAXDEPTH); - RETVAL = newSVsv (self); + XPUSHs (ST (0)); } - OUTPUT: - RETVAL -SV *max_size (SV *self, UV max_size = 0) - CODE: +void max_size (JSON *self, UV max_size = 0) + PPCODE: { - UV *uv = SvJSON (self); UV log2 = 0; if (max_size > 0x80000000UL) max_size = 0x80000000UL; @@ -1339,34 +1379,51 @@ while ((1UL << log2) < max_size) ++log2; - *uv = *uv & ~F_MAXSIZE | (log2 << S_MAXSIZE); + self->flags = self->flags & ~F_MAXSIZE | (log2 << S_MAXSIZE); - RETVAL = newSVsv (self); + XPUSHs (ST (0)); } - OUTPUT: - RETVAL -void encode (SV *self, SV *scalar) +void filter_json_object (JSON *self, SV *cb = &PL_sv_undef) + ALIAS: + filter_json_single_key_object = 1 PPCODE: { - struct json json = { *SvJSON (self) }; - XPUSHs (encode_json (scalar, &json)); + SV **svp; + + if (!SvOK (cb)) + cb = 0; + else + cb = newSVsv (cb); + + switch (ix) + { + case 0: svp = &self->cb_object ; break; + case 1: svp = &self->cb_sk_object; break; + } + + if (*svp) + SvREFCNT_dec (*svp); + + *svp = cb; + + XPUSHs (ST (0)); } -void decode (SV *self, SV *jsonstr) +void encode (JSON *self, SV *scalar) PPCODE: -{ - struct json json = { *SvJSON (self) }; - XPUSHs (decode_json (jsonstr, &json, 0)); -} + XPUSHs (encode_json (scalar, self)); + +void decode (JSON *self, SV *jsonstr) + PPCODE: + XPUSHs (decode_json (jsonstr, self, 0)); -void decode_prefix (SV *self, SV *jsonstr) +void decode_prefix (JSON *self, SV *jsonstr) PPCODE: { UV offset; - struct json json = { *SvJSON (self) }; EXTEND (SP, 2); - PUSHs (decode_json (jsonstr, &json, &offset)); + PUSHs (decode_json (jsonstr, self, &offset)); PUSHs (sv_2mortal (newSVuv (offset))); } @@ -1375,14 +1432,14 @@ void to_json (SV *scalar) PPCODE: { - struct json json = { F_DEFAULT | F_UTF8 }; + JSON json = { F_DEFAULT | F_UTF8 }; XPUSHs (encode_json (scalar, &json)); } void from_json (SV *jsonstr) PPCODE: { - struct json json = { F_DEFAULT | F_UTF8 }; + JSON json = { F_DEFAULT | F_UTF8 }; XPUSHs (decode_json (jsonstr, &json, 0)); }