--- JSON-XS/XS.xs 2007/06/23 22:53:16 1.41 +++ JSON-XS/XS.xs 2007/06/25 22:11:39 1.46 @@ -12,27 +12,29 @@ #endif // some old perls do not have this, try to make it work, no -// guarentees, though. +// guarentees, though. if it breaks, you get to keep the pieces. #ifndef UTF8_MAXBYTES # define UTF8_MAXBYTES 13 #endif -#define F_ASCII 0x00000001UL -#define F_LATIN1 0x00000002UL -#define F_UTF8 0x00000004UL -#define F_INDENT 0x00000008UL -#define F_CANONICAL 0x00000010UL -#define F_SPACE_BEFORE 0x00000020UL -#define F_SPACE_AFTER 0x00000040UL -#define F_ALLOW_NONREF 0x00000100UL -#define F_SHRINK 0x00000200UL -#define F_MAXDEPTH 0xf8000000UL -#define S_MAXDEPTH 27 +#define F_ASCII 0x00000001UL +#define F_LATIN1 0x00000002UL +#define F_UTF8 0x00000004UL +#define F_INDENT 0x00000008UL +#define F_CANONICAL 0x00000010UL +#define F_SPACE_BEFORE 0x00000020UL +#define F_SPACE_AFTER 0x00000040UL +#define F_ALLOW_NONREF 0x00000100UL +#define F_SHRINK 0x00000200UL +#define F_ALLOW_BLESSED 0x00000400UL +#define F_CONV_BLESSED 0x00000800UL // NYI +#define F_MAXDEPTH 0xf8000000UL +#define S_MAXDEPTH 27 +#define F_MAXSIZE 0x01f00000UL +#define S_MAXSIZE 20 #define DEC_DEPTH(flags) (1UL << ((flags & F_MAXDEPTH) >> S_MAXDEPTH)) - -// F_SELFCONVERT? <=> to_json/toJson -// F_BLESSED? <=> { $__class__$ => } +#define DEC_SIZE(flags) (1UL << ((flags & F_MAXSIZE ) >> S_MAXSIZE )) #define F_PRETTY F_INDENT | F_SPACE_BEFORE | F_SPACE_AFTER #define F_DEFAULT (9UL << S_MAXDEPTH) @@ -56,7 +58,8 @@ #define expect_false(expr) expect ((expr) != 0, 0) #define expect_true(expr) expect ((expr) != 0, 1) -static HV *json_stash; // JSON::XS:: +static HV *json_stash, *json_boolean_stash; // JSON::XS:: +static SV *json_true, *json_false; ///////////////////////////////////////////////////////////////////////////// // utility functions @@ -470,7 +473,61 @@ SvGETMAGIC (sv); svt = SvTYPE (sv); - if (svt == SVt_PVHV) + if (expect_false (SvOBJECT (sv))) + { + if (SvSTASH (sv) == json_boolean_stash) + { + if (SvIV (sv) == 0) + encode_str (enc, "false", 5, 0); + else + encode_str (enc, "true", 4, 0); + } + else + { +#if 0 + if (0 && sv_derived_from (rv, "JSON::Literal")) + { + // not yet + } +#endif + if (enc->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); + + if (to_json) + { + 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. + PUTBACK; + assert (1 == call_sv ((SV *)GvCV (to_json), G_SCALAR)); + SPAGAIN; + + encode_sv (enc, POPs); + + FREETMPS; + LEAVE; + } + else if (enc->flags & F_ALLOW_BLESSED) + encode_str (enc, "null", 4, 0); + else + croak ("encountered object '%s', but neither allow_blessed enabled nor TO_JSON method available on it", + SvPV_nolen (sv_2mortal (newRV_inc (sv)))); + } + else if (enc->flags & F_ALLOW_BLESSED) + encode_str (enc, "null", 4, 0); + else + croak ("encountered object '%s', but neither allow_blessed nor convert_blessed settings are enabled", + SvPV_nolen (sv_2mortal (newRV_inc (sv)))); + } + } + else if (svt == SVt_PVHV) encode_hv (enc, (HV *)sv); else if (svt == SVt_PVAV) encode_av (enc, (AV *)sv); @@ -968,25 +1025,62 @@ else for (;;) { - SV *key, *value; - decode_ws (dec); EXPECT_CH ('"'); - key = decode_str (dec); - if (!key) - goto fail; + // heuristic: assume that + // a) decode_str + hv_store_ent are abysmally slow + // b) most hash keys are short, simple ascii text + // so try to "fast-match" such strings to avoid + // the overhead of hv_store_ent. + { + SV *value; + char *p = dec->cur; + char *e = p + 24; // only try up to 24 bytes - decode_ws (dec); EXPECT_CH (':'); + for (;;) + { + if (p == e || *p < 0x20 || *p >= 0x80 || *p == '\\') + { + // slow path, back up and use decode_str + SV *key = decode_str (dec); + if (!key) + goto fail; - value = decode_sv (dec); - if (!value) - { - SvREFCNT_dec (key); - goto fail; - } + decode_ws (dec); EXPECT_CH (':'); - hv_store_ent (hv, key, value, 0); - SvREFCNT_dec (key); + value = decode_sv (dec); + if (!value) + { + SvREFCNT_dec (key); + goto fail; + } + + hv_store_ent (hv, key, value, 0); + SvREFCNT_dec (key); + + break; + } + else if (*p == '"') + { + // fast path, got a simple key + char *key = dec->cur; + int len = p - key; + dec->cur = p + 1; + + decode_ws (dec); EXPECT_CH (':'); + + value = decode_sv (dec); + if (!value) + goto fail; + + hv_store (hv, key, len, value, 0); + + break; + } + + ++p; + } + } decode_ws (dec); @@ -1033,7 +1127,7 @@ if (dec->end - dec->cur >= 4 && !memcmp (dec->cur, "true", 4)) { dec->cur += 4; - return newSViv (1); + return SvREFCNT_inc (json_true); } else ERR ("'true' expected"); @@ -1044,7 +1138,7 @@ if (dec->end - dec->cur >= 5 && !memcmp (dec->cur, "false", 5)) { dec->cur += 5; - return newSViv (0); + return SvREFCNT_inc (json_false); } else ERR ("'false' expected"); @@ -1081,6 +1175,10 @@ SvGETMAGIC (string); SvUPGRADE (string, SVt_PV); + if (flags & F_MAXSIZE && SvCUR (string) > DEC_SIZE (flags)) + croak ("attempted decode of JSON text of %lu bytes size, but max_size is set to %lu", + (unsigned long)SvCUR (string), (unsigned long)DEC_SIZE (flags)); + if (flags & F_UTF8) sv_utf8_downgrade (string, 0); else @@ -1164,7 +1262,11 @@ : i >= 'A' && i <= 'F' ? i - 'A' + 10 : -1; - json_stash = gv_stashpv ("JSON::XS", 1); + json_stash = gv_stashpv ("JSON::XS" , 1); + json_boolean_stash = gv_stashpv ("JSON::XS::Boolean", 1); + + json_true = get_sv ("JSON::XS::true" , 1); SvREADONLY_on (json_true ); + json_false = get_sv ("JSON::XS::false", 1); SvREADONLY_on (json_false); } PROTOTYPES: DISABLE @@ -1177,16 +1279,18 @@ SV *ascii (SV *self, int enable = 1) ALIAS: - ascii = F_ASCII - latin1 = F_LATIN1 - utf8 = F_UTF8 - indent = F_INDENT - canonical = F_CANONICAL - space_before = F_SPACE_BEFORE - space_after = F_SPACE_AFTER - pretty = F_PRETTY - allow_nonref = F_ALLOW_NONREF - shrink = F_SHRINK + ascii = F_ASCII + latin1 = F_LATIN1 + utf8 = F_UTF8 + indent = F_INDENT + canonical = F_CANONICAL + space_before = F_SPACE_BEFORE + space_after = F_SPACE_AFTER + pretty = F_PRETTY + allow_nonref = F_ALLOW_NONREF + shrink = F_SHRINK + allow_blessed = F_ALLOW_BLESSED + convert_blessed = F_CONV_BLESSED CODE: { UV *uv = SvJSON (self); @@ -1215,6 +1319,25 @@ RETVAL = newSVsv (self); } + OUTPUT: + RETVAL + +SV *max_size (SV *self, UV max_size = 0) + CODE: +{ + UV *uv = SvJSON (self); + UV log2 = 0; + + if (max_size > 0x80000000UL) max_size = 0x80000000UL; + if (max_size == 1) max_size = 2; + + while ((1UL << log2) < max_size) + ++log2; + + *uv = *uv & ~F_MAXSIZE | (log2 << S_MAXSIZE); + + RETVAL = newSVsv (self); +} OUTPUT: RETVAL