--- JSON-XS/XS.xs 2007/07/01 23:40:07 1.49 +++ JSON-XS/XS.xs 2007/08/13 16:06:25 1.58 @@ -2,10 +2,11 @@ #include "perl.h" #include "XSUB.h" -#include "assert.h" -#include "string.h" -#include "stdlib.h" -#include "stdio.h" +#include +#include +#include +#include +#include #if defined(__BORLANDC__) || defined(_MSC_VER) # define snprintf _snprintf // C compilers have this in stdio.h @@ -59,12 +60,19 @@ #define expect_false(expr) expect ((expr) != 0, 0) #define expect_true(expr) expect ((expr) != 0, 1) +#ifdef USE_ITHREADS +# define JSON_SLOW 1 +#else +# define JSON_SLOW 0 +#endif + static HV *json_stash, *json_boolean_stash; // JSON::XS:: static SV *json_true, *json_false; typedef struct { U32 flags; - SV *cb_object, *cb_sk_object; + SV *cb_object; + HV *cb_sk_object; } JSON; ///////////////////////////////////////////////////////////////////////////// @@ -307,8 +315,14 @@ for (i = 0; i <= len; ++i) { + SV **svp = av_fetch (av, i, 0); + encode_indent (enc); - encode_sv (enc, *av_fetch (av, i, 0)); + + if (svp) + encode_sv (enc, *svp); + else + encode_str (enc, "null", 4, 0); if (i < len) encode_comma (enc); @@ -472,12 +486,16 @@ if (expect_false (SvOBJECT (sv))) { - if (SvSTASH (sv) == json_boolean_stash) + HV *stash = !JSON_SLOW || json_boolean_stash + ? json_boolean_stash + : gv_stashpv ("JSON::XS::Boolean", 1); + + if (SvSTASH (sv) == 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 +508,28 @@ 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); + 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 +552,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)))); @@ -920,9 +949,11 @@ if (!is_nv) { + int len = dec->cur - start; + // special case the rather common 1..4-digit-int case, assumes 32 bit ints or so if (*start == '-') - switch (dec->cur - start) + switch (len) { case 2: return newSViv (-( start [1] - '0' * 1)); case 3: return newSViv (-( start [1] * 10 + start [2] - '0' * 11)); @@ -930,7 +961,7 @@ case 5: return newSViv (-(start [1] * 1000 + start [2] * 100 + start [3] * 10 + start [4] - '0' * 1111)); } else - switch (dec->cur - start) + switch (len) { case 1: return newSViv ( start [0] - '0' * 1); case 2: return newSViv ( start [0] * 10 + start [1] - '0' * 11); @@ -940,7 +971,7 @@ { UV uv; - int numtype = grok_number (start, dec->cur - start, &uv); + int numtype = grok_number (start, len, &uv); if (numtype & IS_NUMBER_IN_UV) if (numtype & IS_NUMBER_NEG) { @@ -949,12 +980,24 @@ } else return newSVuv (uv); - - // here would likely be the place for bigint support } + + len -= *start == '-' ? 1 : 0; + + // does not fit into IV or UV, try NV + if ((sizeof (NV) == sizeof (double) && DBL_DIG >= len) + #if defined (LDBL_DIG) + || (sizeof (NV) == sizeof (long double) && LDBL_DIG >= len) + #endif + ) + // fits into NV without loss of precision + return newSVnv (Atof (start)); + + // everything else fails, convert it to a string + return newSVpvn (start, dec->cur - start); } - // if we ever support bigint or bigfloat, this is the place for bigfloat + // loss of precision here return newSVnv (Atof (start)); fail: @@ -1097,42 +1140,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); @@ -1309,14 +1372,22 @@ PROTOTYPES: DISABLE +void CLONE (...) + CODE: + json_stash = 0; + json_boolean_stash = 0; + void new (char *klass) PPCODE: { + HV *stash = !JSON_SLOW || json_stash + ? json_stash + : gv_stashpv ("JSON::XS", 1); 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))); + XPUSHs (sv_2mortal (sv_bless (newRV_noinc (pv), stash))); } void ascii (JSON *self, int enable = 1) @@ -1374,18 +1445,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: { - if (!SvOK (cb)) - cb = 0; + SvREFCNT_dec (self->cb_object); + self->cb_object = SvOK (cb) ? newSVsv (cb) : 0; + + XPUSHs (ST (0)); +} - switch (ix) +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 { - 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 +1493,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)