--- JSON-XS/XS.xs 2007/07/02 00:48:18 1.51 +++ 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,7 +486,11 @@ 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)) encode_str (enc, "true", 4, 0); @@ -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; } @@ -923,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)); @@ -933,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); @@ -943,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) { @@ -952,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: @@ -1100,31 +1140,45 @@ // check filter callbacks if (dec->json.flags & F_HOOK) { - ENTER; SAVETMPS; - 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) + if (cb) { - sv = newSVsv (POPs); - goto filter_ok; - } + dSP; + int count; - SvREFCNT_inc (sv); + 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; @@ -1132,14 +1186,13 @@ if (count == 1) { sv = newSVsv (POPs); - goto filter_ok; + FREETMPS; LEAVE; + return sv; } SvREFCNT_inc (sv); + FREETMPS; LEAVE; } - -filter_ok: - FREETMPS; LEAVE; } return sv; @@ -1319,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) @@ -1385,27 +1446,32 @@ } void filter_json_object (JSON *self, SV *cb = &PL_sv_undef) - ALIAS: - filter_json_single_key_object = 1 PPCODE: { - SV **svp; + SvREFCNT_dec (self->cb_object); + self->cb_object = SvOK (cb) ? newSVsv (cb) : 0; - if (!SvOK (cb)) - cb = 0; - else - cb = newSVsv (cb); + XPUSHs (ST (0)); +} - switch (ix) - { - case 0: svp = &self->cb_object ; break; - case 1: svp = &self->cb_sk_object; break; - } +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 (*svp) - SvREFCNT_dec (*svp); + 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); - *svp = cb; + if (!HvKEYS (self->cb_sk_object)) + { + SvREFCNT_dec (self->cb_sk_object); + self->cb_sk_object = 0; + } + } XPUSHs (ST (0)); } @@ -1427,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)