--- JSON-XS/XS.xs 2007/07/01 14:08:03 1.47 +++ JSON-XS/XS.xs 2007/08/27 02:03:23 1.64 @@ -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 @@ -27,11 +28,14 @@ #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_RELAXED 0x00001000UL + #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 )) @@ -58,26 +62,27 @@ #define expect_false(expr) expect ((expr) != 0, 0) #define expect_true(expr) expect ((expr) != 0, 1) +#ifdef USE_ITHREADS +# define JSON_SLOW 1 +# define JSON_STASH (json_stash ? json_stash : gv_stashpv ("JSON::XS", 1)) +#else +# define JSON_SLOW 0 +# define JSON_STASH json_stash +#endif + 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; + HV *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 +127,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; @@ -314,8 +319,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); @@ -328,7 +339,7 @@ } static void -encode_he (enc_t *enc, HE *he) +encode_hk (enc_t *enc, HE *he) { encode_ch (enc, '"'); @@ -351,7 +362,6 @@ if (enc->json.flags & F_SPACE_BEFORE) encode_space (enc); encode_ch (enc, ':'); if (enc->json.flags & F_SPACE_AFTER ) encode_space (enc); - encode_sv (enc, HeVAL (he)); } // compare hash entries, used when all keys are bytestrings @@ -366,8 +376,8 @@ STRLEN la = HeKLEN (a); STRLEN lb = HeKLEN (b); - if (!(cmp = memcmp (HeKEY (a), HeKEY (b), la < lb ? la : lb))) - cmp = la - lb; + if (!(cmp = memcmp (HeKEY (b), HeKEY (a), lb < la ? lb : la))) + cmp = lb - la; return cmp; } @@ -376,29 +386,45 @@ static int he_cmp_slow (const void *a, const void *b) { - return sv_cmp (HeSVKEY_force (*(HE **)a), HeSVKEY_force (*(HE **)b)); + return sv_cmp (HeSVKEY_force (*(HE **)b), HeSVKEY_force (*(HE **)a)); } static void encode_hv (enc_t *enc, HV *hv) { - int count, i; + HE *he; + int count; if (enc->indent >= enc->maxdepth) croak ("data structure too deep (hit recursion limit)"); encode_ch (enc, '{'); encode_nl (enc); ++enc->indent; - if ((count = hv_iterinit (hv))) + // for canonical output we have to sort by keys first + // actually, this is mostly due to the stupid so-called + // security workaround added somewhere in 5.8.x. + // that randomises hash orderings + if (enc->json.flags & F_CANONICAL) { - // for canonical output we have to sort by keys first - // actually, this is mostly due to the stupid so-called - // security workaround added somewhere in 5.8.x. - // that randomises hash orderings - if (enc->json.flags & F_CANONICAL) + int count = hv_iterinit (hv); + + if (SvMAGICAL (hv)) + { + // need to count by iterating. could improve by dynamically building the vector below + // but I don't care for the speed of this special case. + // note also that we will run into undefined behaviour when the two iterations + // do not result in the same count, something I might care for in some later release. + + count = 0; + while (hv_iternext (hv)) + ++count; + + hv_iterinit (hv); + } + + if (count) { - int fast = 1; - HE *he; + int i, fast = 1; #if defined(__BORLANDC__) || defined(_MSC_VER) HE **hes = _alloca (count * sizeof (HE)); #else @@ -435,36 +461,37 @@ LEAVE; } - for (i = 0; i < count; ++i) + while (count--) { encode_indent (enc); - encode_he (enc, hes [i]); + he = hes [count]; + encode_hk (enc, he); + encode_sv (enc, expect_false (SvMAGICAL (hv)) ? hv_iterval (hv, he) : HeVAL (he)); - if (i < count - 1) + if (count) encode_comma (enc); } - - encode_nl (enc); } - else - { - HE *he = hv_iternext (hv); - + } + else + { + if (hv_iterinit (hv) || SvMAGICAL (hv)) + if ((he = hv_iternext (hv))) for (;;) { encode_indent (enc); - encode_he (enc, he); + encode_hk (enc, he); + encode_sv (enc, expect_false (SvMAGICAL (hv)) ? hv_iterval (hv, he) : HeVAL (he)); if (!(he = hv_iternext (hv))) break; encode_comma (enc); } - - encode_nl (enc); - } } + encode_nl (enc); + --enc->indent; encode_indent (enc); encode_ch (enc, '}'); } @@ -479,12 +506,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 { @@ -497,26 +528,30 @@ 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); + + 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; - FREETMPS; - LEAVE; + encode_sv (enc, sv); + + FREETMPS; LEAVE; } else if (enc->json.flags & F_ALLOW_BLESSED) encode_str (enc, "null", 4, 0); @@ -537,10 +572,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 +658,7 @@ } static SV * -encode_json (SV *scalar, struct json *json) +encode_json (SV *scalar, JSON *json) { enc_t enc; @@ -658,21 +696,41 @@ 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; inline void +decode_comment (dec_t *dec) +{ + // only '#'-style comments allowed a.t.m. + + while (*dec->cur && *dec->cur != 0x0a && *dec->cur != 0x0d) + ++dec->cur; +} + +inline void decode_ws (dec_t *dec) { for (;;) { char ch = *dec->cur; - if (ch > 0x20 - || (ch != 0x20 && ch != 0x0a && ch != 0x0d && ch != 0x09)) - break; + if (ch > 0x20) + { + if (expect_false (ch == '#')) + { + if (dec->json.flags & F_RELAXED) + decode_comment (dec); + else + break; + } + else + break; + } + else if (ch != 0x20 && ch != 0x0a && ch != 0x0d && ch != 0x09) + break; // parse error, but let higher level handle it, gives better error messages ++dec->cur; } @@ -931,9 +989,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)); @@ -941,7 +1001,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); @@ -951,7 +1011,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) { @@ -960,12 +1020,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: @@ -1005,6 +1077,14 @@ ERR (", or ] expected while parsing array"); ++dec->cur; + + decode_ws (dec); + + if (*dec->cur == ']' && dec->json.flags & F_RELAXED) + { + ++dec->cur; + break; + } } DEC_DEC_DEPTH; @@ -1019,6 +1099,7 @@ static SV * decode_hv (dec_t *dec) { + SV *sv; HV *hv = newHV (); DEC_INC_DEPTH; @@ -1029,7 +1110,7 @@ else for (;;) { - decode_ws (dec); EXPECT_CH ('"'); + EXPECT_CH ('"'); // heuristic: assume that // a) decode_str + hv_store_ent are abysmally slow. @@ -1053,6 +1134,7 @@ decode_ws (dec); EXPECT_CH (':'); + decode_ws (dec); value = decode_sv (dec); if (!value) { @@ -1074,6 +1156,7 @@ decode_ws (dec); EXPECT_CH (':'); + decode_ws (dec); value = decode_sv (dec); if (!value) goto fail; @@ -1099,10 +1182,78 @@ ERR (", or } expected while parsing object/hash"); ++dec->cur; + + decode_ws (dec); + + if (*dec->cur == '}' && dec->json.flags & F_RELAXED) + { + ++dec->cur; + break; + } } DEC_DEC_DEPTH; - return newRV_noinc ((SV *)hv); + sv = newRV_noinc ((SV *)hv); + + // check filter callbacks + if (dec->json.flags & F_HOOK) + { + if (dec->json.cb_sk_object && HvKEYS (hv) == 1) + { + HE *cb, *he; + + hv_iterinit (hv); + he = hv_iternext (hv); + hv_iterinit (hv); + + // 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 (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; + + 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); + FREETMPS; LEAVE; + return sv; + } + + SvREFCNT_inc (sv); + FREETMPS; LEAVE; + } + } + + return sv; fail: SvREFCNT_dec (hv); @@ -1113,8 +1264,6 @@ static SV * decode_sv (dec_t *dec) { - decode_ws (dec); - // the beauty of JSON: you need exactly one character lookahead // to parse anything. switch (*dec->cur) @@ -1132,6 +1281,9 @@ if (dec->end - dec->cur >= 4 && !memcmp (dec->cur, "true", 4)) { dec->cur += 4; +#if JSON_SLOW + json_true = get_sv ("JSON::XS::true", 1); SvREADONLY_on (json_true); +#endif return SvREFCNT_inc (json_true); } else @@ -1143,6 +1295,9 @@ if (dec->end - dec->cur >= 5 && !memcmp (dec->cur, "false", 5)) { dec->cur += 5; +#if JSON_SLOW + json_false = get_sv ("JSON::XS::false", 1); SvREADONLY_on (json_false); +#endif return SvREFCNT_inc (json_false); } else @@ -1171,7 +1326,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,7 +1353,12 @@ 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 + + decode_ws (&dec); sv = decode_sv (&dec); if (!(offset_return || !sv)) @@ -1276,13 +1436,22 @@ PROTOTYPES: DISABLE -SV *new (char *dummy) +void CLONE (...) CODE: - RETVAL = sv_bless (newRV_noinc (newSVuv (F_DEFAULT)), json_stash); - OUTPUT: - RETVAL + json_stash = 0; + json_boolean_stash = 0; -SV *ascii (SV *self, int enable = 1) +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))); +} + +void ascii (JSON *self, int enable = 1) ALIAS: ascii = F_ASCII latin1 = F_LATIN1 @@ -1296,23 +1465,20 @@ shrink = F_SHRINK allow_blessed = F_ALLOW_BLESSED convert_blessed = F_CONV_BLESSED - CODE: + relaxed = F_RELAXED + 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 +1486,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,50 +1502,77 @@ 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) PPCODE: { - struct json json = { *SvJSON (self) }; - XPUSHs (encode_json (scalar, &json)); + SvREFCNT_dec (self->cb_object); + self->cb_object = SvOK (cb) ? newSVsv (cb) : 0; + + XPUSHs (ST (0)); } -void decode (SV *self, SV *jsonstr) +void filter_json_single_key_object (JSON *self, SV *key, SV *cb = &PL_sv_undef) PPCODE: { - struct json json = { *SvJSON (self) }; - XPUSHs (decode_json (jsonstr, &json, 0)); + 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 + { + 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)); } -void decode_prefix (SV *self, SV *jsonstr) +void encode (JSON *self, SV *scalar) + PPCODE: + XPUSHs (encode_json (scalar, self)); + +void decode (JSON *self, SV *jsonstr) + PPCODE: + XPUSHs (decode_json (jsonstr, self, 0)); + +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))); } +void DESTROY (JSON *self) + CODE: + SvREFCNT_dec (self->cb_sk_object); + SvREFCNT_dec (self->cb_object); + PROTOTYPES: ENABLE 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)); }