--- JSON-XS/XS.xs 2007/07/02 00:29:38 1.50 +++ JSON-XS/XS.xs 2007/07/26 11:33:35 1.56 @@ -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 @@ -64,7 +65,8 @@ typedef struct { U32 flags; - SV *cb_object, *cb_sk_object; + SV *cb_object; + HV *cb_sk_object; } JSON; ///////////////////////////////////////////////////////////////////////////// @@ -307,8 +309,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); @@ -474,10 +482,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 { @@ -490,20 +498,29 @@ 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); + int count; + 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; } @@ -529,10 +546,10 @@ STRLEN len = 0; char *pv = svt ? SvPV (sv, len) : 0; - if (len == 1 && *pv == '0') - encode_str (enc, "false", 5, 0); - else if (len == 1 && *pv == '1') + 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)))); @@ -923,9 +940,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 +952,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 +962,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 +971,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,42 +1131,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); @@ -1377,18 +1428,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: +{ + 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 (!SvOK (cb)) - cb = 0; + if (!self->cb_sk_object) + self->cb_sk_object = newHV (); - switch (ix) + 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)); @@ -1411,6 +1476,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)