--- JSON-XS/XS.xs 2013/10/25 20:27:57 1.118 +++ JSON-XS/XS.xs 2019/02/24 04:21:05 1.137 @@ -8,6 +8,7 @@ #include #include #include +#include #if defined(__BORLANDC__) || defined(_MSC_VER) # define snprintf _snprintf // C compilers have this in stdio.h @@ -19,6 +20,17 @@ # define UTF8_MAXBYTES 13 #endif +// compatibility with perl <5.18 +#ifndef HvNAMELEN_get +# define HvNAMELEN_get(hv) strlen (HvNAME (hv)) +#endif +#ifndef HvNAMELEN +# define HvNAMELEN(hv) HvNAMELEN_get (hv) +#endif +#ifndef HvNAMEUTF8 +# define HvNAMEUTF8(hv) 0 +#endif + // three extra for rounding, sign, and end of string #define IVUV_MAXCHARS (sizeof (UV) * CHAR_BIT * 28 / 93 + 3) @@ -35,11 +47,12 @@ #define F_CONV_BLESSED 0x00000800UL #define F_RELAXED 0x00001000UL #define F_ALLOW_UNKNOWN 0x00002000UL +#define F_ALLOW_TAGS 0x00004000UL #define F_HOOK 0x00080000UL // some hooks exist, so slow-path processing #define F_PRETTY F_INDENT | F_SPACE_BEFORE | F_SPACE_AFTER -#define INIT_SIZE 32 // initial scalar size to be allocated +#define INIT_SIZE 64 // initial scalar size to be allocated #define INDENT_STEP 3 // spaces per indentation level #define SHORT_STRING_LEN 16384 // special-case strings of up to this size @@ -67,21 +80,26 @@ #define ERR_NESTING_EXCEEDED "json text or perl structure exceeds maximum nesting level (max_depth set too low?)" #ifdef USE_ITHREADS -# define JSON_SLOW 1 -# define JSON_STASH (json_stash ? json_stash : gv_stashpv ("JSON::XS", 1)) +# define JSON_STASH (expect_true (json_stash) ? json_stash : gv_stashpv ("JSON::XS", 1)) +# define BOOL_STASH (expect_true (bool_stash) ? bool_stash : gv_stashpv ("Types::Serialiser::Boolean", 1)) +# define GET_BOOL(value) (expect_true (bool_ ## value) ? bool_ ## value : get_bool ("Types::Serialiser::" # value)) #else -# define JSON_SLOW 0 # define JSON_STASH json_stash +# define BOOL_STASH bool_stash +# define GET_BOOL(value) bool_ ## value #endif // the amount of HEs to allocate on the stack, when sorting keys #define STACK_HES 64 -static HV *json_stash, *json_boolean_stash; // JSON::XS:: -static SV *json_true, *json_false; +static HV *json_stash, *bool_stash; // JSON::XS::, Types::Serialiser::Boolean:: +static SV *bool_false, *bool_true; +static SV *sv_json; enum { INCR_M_WS = 0, // initial whitespace skipping, must be 0 + INCR_M_TFN, // inside true/false/null + INCR_M_NUM, // inside number INCR_M_STR, // inside string INCR_M_BS, // inside backslash INCR_M_C0, // inside comment in initial whitespace sequence @@ -104,13 +122,16 @@ STRLEN incr_pos; // the current offset into the text int incr_nest; // {[]}-nesting level unsigned char incr_mode; + + SV *v_false, *v_true; } JSON; INLINE void json_init (JSON *json) { - Zero (json, 1, JSON); - json->max_depth = 512; + static const JSON init = { F_ALLOW_NONREF, 512 }; + + *json = init; } ///////////////////////////////////////////////////////////////////////////// @@ -142,7 +163,32 @@ } } -// decode an utf-8 character and return it, or (UV)-1 in +/* adds two STRLENs together, slow, and with paranoia */ +STRLEN +strlen_sum (STRLEN l1, STRLEN l2) +{ + size_t sum = l1 + l2; + + if (sum < (size_t)l2 || sum != (size_t)(STRLEN)sum) + croak ("JSON::XS: string size overflow"); + + return sum; +} + +/* similar to SvGROW, but somewhat safer and guarantees exponential realloc strategy */ +static char * +json_sv_grow (SV *sv, size_t len1, size_t len2) +{ + len1 = strlen_sum (len1, len2); + len1 = strlen_sum (len1, len1 >> 1); + + if (len1 > 4096 - 24) + len1 = (len1 | 4095) - 24; + + return SvGROW (sv, len1); +} + +// decode a utf-8 character and return it, or (UV)-1 in // case of an error. // we special-case "safe" characters from U+80 .. U+7FF, // but use the very good perl function to parse anything else. @@ -289,6 +335,49 @@ return neg ? -accum : accum; } + +// target of scalar reference is bool? -1 == nope, 0 == false, 1 == true +static int +ref_bool_type (SV *sv) +{ + svtype svt = SvTYPE (sv); + + if (svt < SVt_PVAV) + { + STRLEN len = 0; + char *pv = svt ? SvPV (sv, len) : 0; + + if (len == 1) + if (*pv == '1') + return 1; + else if (*pv == '0') + return 0; + } + + return -1; +} + +// returns whether scalar is not a reference in the sense of allow_nonref +static int +json_nonref (SV *scalar) +{ + if (!SvROK (scalar)) + return 1; + + scalar = SvRV (scalar); + + if (SvTYPE (scalar) >= SVt_PVMG) + { + if (SvSTASH (scalar) == bool_stash) + return 1; + + if (!SvOBJECT (scalar) && ref_bool_type (scalar) >= 0) + return 1; + } + + return 0; +} + ///////////////////////////////////////////////////////////////////////////// // encoder @@ -306,12 +395,12 @@ INLINE void need (enc_t *enc, STRLEN len) { - if (expect_false (enc->cur + len >= enc->end)) + if (expect_false ((uintptr_t)(enc->end - enc->cur) < len)) { STRLEN cur = enc->cur - (char *)SvPVX (enc->sv); - SvGROW (enc->sv, cur + (len < (cur >> 2) ? cur >> 2 : len) + 1); - enc->cur = SvPVX (enc->sv) + cur; - enc->end = SvPVX (enc->sv) + SvLEN (enc->sv) - 1; + char *buf = json_sv_grow (enc->sv, cur, len); + enc->cur = buf + cur; + enc->end = buf + SvLEN (enc->sv) - 1; } } @@ -337,13 +426,13 @@ { if (expect_false (ch == '"')) // but with slow exceptions { - need (enc, len += 1); + need (enc, len + 1); *enc->cur++ = '\\'; *enc->cur++ = '"'; } else if (expect_false (ch == '\\')) { - need (enc, len += 1); + need (enc, len + 1); *enc->cur++ = '\\'; *enc->cur++ = '\\'; } @@ -356,11 +445,11 @@ { switch (ch) { - case '\010': need (enc, len += 1); *enc->cur++ = '\\'; *enc->cur++ = 'b'; ++str; break; - case '\011': need (enc, len += 1); *enc->cur++ = '\\'; *enc->cur++ = 't'; ++str; break; - case '\012': need (enc, len += 1); *enc->cur++ = '\\'; *enc->cur++ = 'n'; ++str; break; - case '\014': need (enc, len += 1); *enc->cur++ = '\\'; *enc->cur++ = 'f'; ++str; break; - case '\015': need (enc, len += 1); *enc->cur++ = '\\'; *enc->cur++ = 'r'; ++str; break; + case '\010': need (enc, len + 1); *enc->cur++ = '\\'; *enc->cur++ = 'b'; ++str; break; + case '\011': need (enc, len + 1); *enc->cur++ = '\\'; *enc->cur++ = 't'; ++str; break; + case '\012': need (enc, len + 1); *enc->cur++ = '\\'; *enc->cur++ = 'n'; ++str; break; + case '\014': need (enc, len + 1); *enc->cur++ = '\\'; *enc->cur++ = 'f'; ++str; break; + case '\015': need (enc, len + 1); *enc->cur++ = '\\'; *enc->cur++ = 'r'; ++str; break; default: { @@ -386,7 +475,7 @@ if (uch >= 0x110000UL) croak ("out of range codepoint (0x%lx) encountered, unrepresentable in JSON", (unsigned long)uch); - need (enc, len += 11); + need (enc, len + 11); sprintf (enc->cur, "\\u%04x\\u%04x", (int)((uch - 0x10000) / 0x400 + 0xD800), (int)((uch - 0x10000) % 0x400 + 0xDC00)); @@ -394,7 +483,7 @@ } else { - need (enc, len += 5); + need (enc, len + 5); *enc->cur++ = '\\'; *enc->cur++ = 'u'; *enc->cur++ = PL_hexdigit [ uch >> 12 ]; @@ -412,7 +501,7 @@ } else if (is_utf8) { - need (enc, len += clen); + need (enc, len + clen); do { *enc->cur++ = *str++; @@ -421,7 +510,7 @@ } else { - need (enc, len += UTF8_MAXBYTES - 1); // never more than 11 bytes needed + need (enc, len + UTF8_MAXBYTES - 1); // never more than 11 bytes needed enc->cur = encode_utf8 (enc->cur, uch); ++str; } @@ -684,71 +773,95 @@ encode_rv (enc_t *enc, SV *sv) { svtype svt; + GV *method; SvGETMAGIC (sv); svt = SvTYPE (sv); if (expect_false (SvOBJECT (sv))) { - HV *stash = !JSON_SLOW || json_boolean_stash - ? json_boolean_stash - : gv_stashpv ("JSON::XS::Boolean", 1); + HV *stash = SvSTASH (sv); - if (SvSTASH (sv) == stash) + if (stash == bool_stash) { - if (SvIV (sv)) - encode_str (enc, "true", 4, 0); - else - encode_str (enc, "false", 5, 0); + if (SvIV (sv)) encode_str (enc, "true" , 4, 0); + else encode_str (enc, "false", 5, 0); } - else + else if ((enc->json.flags & F_ALLOW_TAGS) && (method = gv_fetchmethod_autoload (stash, "FREEZE", 0))) { -#if 0 - if (0 && sv_derived_from (rv, "JSON::Literal")) - { - // not yet - } -#endif - if (enc->json.flags & F_CONV_BLESSED) + int count; + dSP; + + ENTER; SAVETMPS; + PUSHMARK (SP); + EXTEND (SP, 2); + // we re-bless the reference to get overload and other niceties right + PUSHs (sv_bless (sv_2mortal (newRV_inc (sv)), stash)); + PUSHs (sv_json); + + PUTBACK; + count = call_sv ((SV *)GvCV (method), G_ARRAY); + SPAGAIN; + + // catch this surprisingly common error + if (SvROK (TOPs) && SvRV (TOPs) == sv) + croak ("%s::FREEZE method returned same object as was passed instead of a new one", HvNAME (SvSTASH (sv))); + + encode_ch (enc, '('); + encode_ch (enc, '"'); + encode_str (enc, HvNAME (stash), HvNAMELEN (stash), HvNAMEUTF8 (stash)); + encode_ch (enc, '"'); + encode_ch (enc, ')'); + encode_ch (enc, '['); + + if (count) { - // we re-bless the reference to get overload and other niceties right - GV *to_json = gv_fetchmethod_autoload (SvSTASH (sv), "TO_JSON", 0); + int i; - if (to_json) + for (i = 0; i < count - 1; ++i) { - dSP; + encode_sv (enc, SP[i + 1 - count]); + encode_ch (enc, ','); + } - ENTER; SAVETMPS; PUSHMARK (SP); - XPUSHs (sv_bless (sv_2mortal (newRV_inc (sv)), SvSTASH (sv))); + encode_sv (enc, TOPs); + SP -= count; + } - // calling with G_SCALAR ensures that we always get a 1 return value - PUTBACK; - call_sv ((SV *)GvCV (to_json), G_SCALAR); - SPAGAIN; + encode_ch (enc, ']'); - // 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))); + FREETMPS; LEAVE; + } + else if ((enc->json.flags & F_CONV_BLESSED) && (method = gv_fetchmethod_autoload (stash, "TO_JSON", 0))) + { + dSP; - sv = POPs; - PUTBACK; + ENTER; SAVETMPS; + PUSHMARK (SP); + // we re-bless the reference to get overload and other niceties right + XPUSHs (sv_bless (sv_2mortal (newRV_inc (sv)), stash)); + + // calling with G_SCALAR ensures that we always get a 1 return value + PUTBACK; + call_sv ((SV *)GvCV (method), G_SCALAR); + SPAGAIN; + + // 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))); - encode_sv (enc, sv); + sv = POPs; + PUTBACK; - FREETMPS; LEAVE; - } - else if (enc->json.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->json.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)))); + encode_sv (enc, sv); + + FREETMPS; LEAVE; } + else if (enc->json.flags & F_ALLOW_BLESSED) + encode_str (enc, "null", 4, 0); + else + croak ("encountered object '%s', but neither allow_blessed, convert_blessed nor allow_tags settings are enabled (or TO_JSON/FREEZE method missing)", + SvPV_nolen (sv_2mortal (newRV_inc (sv)))); } else if (svt == SVt_PVHV) encode_hv (enc, (HV *)sv); @@ -756,12 +869,11 @@ encode_av (enc, (AV *)sv); else if (svt < SVt_PVAV) { - STRLEN len = 0; - char *pv = svt ? SvPV (sv, len) : 0; + int bool_type = ref_bool_type (sv); - if (len == 1 && *pv == '1') + if (bool_type == 1) encode_str (enc, "true", 4, 0); - else if (len == 1 && *pv == '0') + else if (bool_type == 0) encode_str (enc, "false", 5, 0); else if (enc->json.flags & F_ALLOW_UNKNOWN) encode_str (enc, "null", 4, 0); @@ -854,7 +966,7 @@ { enc_t enc; - if (!(json->flags & F_ALLOW_NONREF) && !SvROK (scalar)) + if (!(json->flags & F_ALLOW_NONREF) && json_nonref (scalar)) croak ("hash- or arrayref expected (not a simple scalar, use allow_nonref to allow this)"); enc.json = *json; @@ -1071,6 +1183,8 @@ utf8 = 1; } + else if (ch == '\t' && dec->json.flags & F_RELAXED) + *cur++ = ch; else { --dec_cur; @@ -1090,8 +1204,8 @@ { STRLEN cur = SvCUR (sv); - if (SvLEN (sv) <= cur + len) - SvGROW (sv, cur + (len < (cur >> 2) ? cur >> 2 : len) + 1); + if (SvLEN (sv) - cur <= len) + json_sv_grow (sv, cur, len); memcpy (SvPVX (sv) + SvCUR (sv), buf, len); SvCUR_set (sv, SvCUR (sv) + len); @@ -1392,7 +1506,7 @@ sv = newRV_noinc ((SV *)hv); // check filter callbacks - if (dec->json.flags & F_HOOK) + if (expect_false (dec->json.flags & F_HOOK)) { if (dec->json.cb_sk_object && HvKEYS (hv) == 1) { @@ -1402,7 +1516,7 @@ he = hv_iternext (hv); hv_iterinit (hv); - // the next line creates a mortal sv each time its called. + // the next line creates a mortal sv each time it's called. // might want to optimise this for common cases. cb = hv_fetch_ent (dec->json.cb_sk_object, hv_iterkeysv (he), 0, 0); @@ -1411,7 +1525,8 @@ dSP; int count; - ENTER; SAVETMPS; PUSHMARK (SP); + ENTER; SAVETMPS; + PUSHMARK (SP); XPUSHs (HeVAL (he)); sv_2mortal (sv); @@ -1423,6 +1538,8 @@ FREETMPS; LEAVE; return sv; } + else if (count) + croak ("filter_json_single_key_object callbacks must not return more than one scalar"); SvREFCNT_inc (sv); FREETMPS; LEAVE; @@ -1434,19 +1551,19 @@ dSP; int count; - 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); - FREETMPS; LEAVE; - return sv; - } + sv = newSVsv (POPs); + else if (count == 0) + SvREFCNT_inc (sv); + else + croak ("filter_json_object callbacks must not return more than one scalar"); - SvREFCNT_inc (sv); FREETMPS; LEAVE; } } @@ -1460,6 +1577,89 @@ } static SV * +decode_tag (dec_t *dec) +{ + SV *tag = 0; + SV *val = 0; + + if (!(dec->json.flags & F_ALLOW_TAGS)) + ERR ("malformed JSON string, neither array, object, number, string or atom"); + + ++dec->cur; + + decode_ws (dec); + + tag = decode_sv (dec); + if (!tag) + goto fail; + + if (!SvPOK (tag)) + ERR ("malformed JSON string, (tag) must be a string"); + + decode_ws (dec); + + if (*dec->cur != ')') + ERR (") expected after tag"); + + ++dec->cur; + + decode_ws (dec); + + val = decode_sv (dec); + if (!val) + goto fail; + + if (!SvROK (val) || SvTYPE (SvRV (val)) != SVt_PVAV) + ERR ("malformed JSON string, tag value must be an array"); + + { + AV *av = (AV *)SvRV (val); + int i, len = av_len (av) + 1; + HV *stash = gv_stashsv (tag, 0); + SV *sv; + + if (!stash) + ERR ("cannot decode perl-object (package does not exist)"); + + GV *method = gv_fetchmethod_autoload (stash, "THAW", 0); + + if (!method) + ERR ("cannot decode perl-object (package does not have a THAW method)"); + + dSP; + + ENTER; SAVETMPS; + PUSHMARK (SP); + EXTEND (SP, len + 2); + // we re-bless the reference to get overload and other niceties right + PUSHs (tag); + PUSHs (sv_json); + + for (i = 0; i < len; ++i) + PUSHs (*av_fetch (av, i, 1)); + + PUTBACK; + call_sv ((SV *)GvCV (method), G_SCALAR); + SPAGAIN; + + SvREFCNT_dec (tag); + SvREFCNT_dec (val); + sv = SvREFCNT_inc (POPs); + + PUTBACK; + + FREETMPS; LEAVE; + + return sv; + } + +fail: + SvREFCNT_dec (tag); + SvREFCNT_dec (val); + return 0; +} + +static SV * decode_sv (dec_t *dec) { // the beauty of JSON: you need exactly one character lookahead @@ -1469,37 +1669,40 @@ case '"': ++dec->cur; return decode_str (dec); case '[': ++dec->cur; return decode_av (dec); case '{': ++dec->cur; return decode_hv (dec); + case '(': return decode_tag (dec); case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return decode_num (dec); - case 't': - if (dec->end - dec->cur >= 4 && !memcmp (dec->cur, "true", 4)) + case 'f': + if (dec->end - dec->cur >= 5 && !memcmp (dec->cur, "false", 5)) { - dec->cur += 4; -#if JSON_SLOW - json_true = get_bool ("JSON::XS::true"); -#endif - return newSVsv (json_true); + dec->cur += 5; + + if (expect_false (!dec->json.v_false)) + dec->json.v_false = GET_BOOL (false); + + return newSVsv (dec->json.v_false); } else - ERR ("'true' expected"); + ERR ("'false' expected"); break; - case 'f': - if (dec->end - dec->cur >= 5 && !memcmp (dec->cur, "false", 5)) + case 't': + if (dec->end - dec->cur >= 4 && !memcmp (dec->cur, "true", 4)) { - dec->cur += 5; -#if JSON_SLOW - json_false = get_bool ("JSON::XS::false"); -#endif - return newSVsv (json_false); + dec->cur += 4; + + if (expect_false (!dec->json.v_true)) + dec->json.v_true = GET_BOOL (true); + + return newSVsv (dec->json.v_true); } else - ERR ("'false' expected"); + ERR ("'true' expected"); break; @@ -1515,7 +1718,7 @@ break; default: - ERR ("malformed JSON string, neither array, object, number, string or atom"); + ERR ("malformed JSON string, neither tag, array, object, number, string or atom"); break; } @@ -1524,7 +1727,7 @@ } static SV * -decode_json (SV *string, JSON *json, char **offset_return) +decode_json (SV *string, JSON *json, STRLEN *offset_return) { dec_t dec; SV *sv; @@ -1533,9 +1736,12 @@ * makes perl ignore the magic in subsequent accesses. * also make a copy of non-PV values, to get them into a clean * state (SvPV should do that, but it's buggy, see below). + * + * SvIsCOW_shared_hash works around a bug in perl (possibly 5.16), + * as reported by Reini Urban. */ /*SvGETMAGIC (string);*/ - if (SvMAGICAL (string) || !SvPOK (string)) + if (SvMAGICAL (string) || !SvPOK (string) || SvIsCOW_shared_hash (string)) string = sv_2mortal (newSVsv (string)); SvUPGRADE (string, SVt_PV); @@ -1584,14 +1790,13 @@ sv = decode_sv (&dec); if (offset_return) - *offset_return = dec.cur; - - if (!(offset_return || !sv)) + *offset_return = dec.cur - SvPVX (string); + else if (sv) { // check for trailing garbage decode_ws (&dec); - if (*dec.cur) + if (dec.cur != dec.end) { dec.err = "garbage after JSON object"; SvREFCNT_dec (sv); @@ -1620,7 +1825,7 @@ sv = sv_2mortal (sv); - if (!(dec.json.flags & F_ALLOW_NONREF) && !SvROK (sv)) + if (!(dec.json.flags & F_ALLOW_NONREF) && json_nonref (sv)) croak ("JSON text must be an object or array (but found number, string, true, false or null, use allow_nonref to allow this)"); return sv; @@ -1639,9 +1844,17 @@ for (;;) { - //printf ("loop pod %d *p<%c><%s>, mode %d nest %d\n", p - SvPVX (self->incr_text), *p, p, self->incr_mode, self->incr_nest);//D switch (self->incr_mode) { + // reached end of a scalar, see if we are inside a nested structure or not + end_of_scalar: + self->incr_mode = INCR_M_JSON; + + if (self->incr_nest) // end of a scalar inside array, object or tag + goto incr_m_json; + else // end of scalar outside structure, json text ends here + goto interrupt; + // only used for initial whitespace skipping case INCR_M_WS: for (;;) @@ -1693,6 +1906,40 @@ break; + // inside true/false/null + case INCR_M_TFN: + incr_m_tfn: + for (;;) + switch (*p++) + { + case 'r': case 'u': case 'e': // tRUE, falsE, nUll + case 'a': case 'l': case 's': // fALSe, nuLL + // allowed + break; + + default: + --p; + goto end_of_scalar; + } + + // inside a number + case INCR_M_NUM: + incr_m_num: + for (;;) + switch (*p++) + { + case 'e': case 'E': case '.': case '+': + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + // allowed + break; + + default: + --p; + goto end_of_scalar; + } + // inside a string case INCR_M_STR: incr_m_str: @@ -1701,12 +1948,7 @@ if (*p == '"') { ++p; - self->incr_mode = INCR_M_JSON; - - if (!self->incr_nest) - goto interrupt; - - goto incr_m_json; + goto end_of_scalar; } else if (*p == '\\') { @@ -1746,12 +1988,28 @@ } break; + // the following three blocks handle scalars. this makes the parser + // more strict than required inside arrays or objects, and could + // be moved to a special case on the toplevel (except strings) + case 't': + case 'f': + case 'n': + self->incr_mode = INCR_M_TFN; + goto incr_m_tfn; + + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + self->incr_mode = INCR_M_NUM; + goto incr_m_num; + case '"': self->incr_mode = INCR_M_STR; goto incr_m_str; case '[': case '{': + case '(': if (++self->incr_nest > self->max_depth) croak (ERR_NESTING_EXCEEDED); break; @@ -1762,6 +2020,10 @@ goto interrupt; break; + case ')': + --self->incr_nest; + break; + case '#': self->incr_mode = INCR_M_C1; goto incr_m_c; @@ -1795,11 +2057,13 @@ : i >= 'A' && i <= 'F' ? i - 'A' + 10 : -1; - json_stash = gv_stashpv ("JSON::XS" , 1); - json_boolean_stash = gv_stashpv ("JSON::XS::Boolean", 1); + json_stash = gv_stashpv ("JSON::XS" , 1); + bool_stash = gv_stashpv ("Types::Serialiser::Boolean", 1); + bool_false = get_bool ("Types::Serialiser::false"); + bool_true = get_bool ("Types::Serialiser::true"); - json_true = get_bool ("JSON::XS::true"); - json_false = get_bool ("JSON::XS::false"); + sv_json = newSVpv ("JSON", 0); + SvREADONLY_on (sv_json); CvNODEBUG_on (get_cv ("JSON::XS::incr_text", 0)); /* the debugger completely breaks lvalue subs */ } @@ -1808,8 +2072,13 @@ void CLONE (...) CODE: - json_stash = 0; - json_boolean_stash = 0; + // as long as these writes are atomic, the race should not matter + // as existing threads either already use 0, or use the old value, + // which is sitll correct for the initial thread. + json_stash = 0; + bool_stash = 0; + bool_false = 0; + bool_true = 0; void new (char *klass) PPCODE: @@ -1823,6 +2092,21 @@ ))); } +void boolean_values (JSON *self, SV *v_false = 0, SV *v_true = 0) + PPCODE: + self->v_false = newSVsv (v_false); + self->v_true = newSVsv (v_true); + XPUSHs (ST (0)); + +void get_boolean_values (JSON *self) + PPCODE: + if (self->v_false && self->v_true) + { + EXTEND (SP, 2); + PUSHs (self->v_false); + PUSHs (self->v_true); + } + void ascii (JSON *self, int enable = 1) ALIAS: ascii = F_ASCII @@ -1839,6 +2123,7 @@ convert_blessed = F_CONV_BLESSED relaxed = F_RELAXED allow_unknown = F_ALLOW_UNKNOWN + allow_tags = F_ALLOW_TAGS PPCODE: { if (enable) @@ -1864,6 +2149,7 @@ get_convert_blessed = F_CONV_BLESSED get_relaxed = F_RELAXED get_allow_unknown = F_ALLOW_UNKNOWN + get_allow_tags = F_ALLOW_TAGS PPCODE: XPUSHs (boolSV (self->flags & ix)); @@ -1922,23 +2208,21 @@ void encode (JSON *self, SV *scalar) PPCODE: - PUTBACK; scalar = encode_json (scalar, self); SPAGAIN; - XPUSHs (scalar); + PUTBACK; XPUSHs (encode_json (scalar, self)); void decode (JSON *self, SV *jsonstr) PPCODE: - PUTBACK; jsonstr = decode_json (jsonstr, self, 0); SPAGAIN; - XPUSHs (jsonstr); + PUTBACK; XPUSHs (decode_json (jsonstr, self, 0)); void decode_prefix (JSON *self, SV *jsonstr) PPCODE: { SV *sv; - char *offset; - PUTBACK; sv = decode_json (jsonstr, self, &offset); SPAGAIN; + STRLEN offset; + PUTBACK; sv = decode_json (jsonstr, self, &offset); EXTEND (SP, 2); PUSHs (sv); - PUSHs (sv_2mortal (newSVuv (ptr_to_index (jsonstr, offset)))); + PUSHs (sv_2mortal (newSVuv (ptr_to_index (jsonstr, SvPV_nolen (jsonstr) + offset)))); } void incr_parse (JSON *self, SV *jsonstr = 0) @@ -1982,8 +2266,8 @@ const char *str = SvPV (jsonstr, len); STRLEN cur = SvCUR (self->incr_text); - if (SvLEN (self->incr_text) <= cur + len) - SvGROW (self->incr_text, cur + (len < (cur >> 2) ? cur >> 2 : len) + 1); + if (SvLEN (self->incr_text) - cur <= len) + json_sv_grow (self->incr_text, cur, len); Move (str, SvEND (self->incr_text), len, char); SvCUR_set (self->incr_text, SvCUR (self->incr_text) + len); @@ -1995,7 +2279,7 @@ do { SV *sv; - char *offset; + STRLEN offset; if (!INCR_DONE (self)) { @@ -2018,14 +2302,14 @@ } } - PUTBACK; sv = decode_json (self->incr_text, self, &offset); SPAGAIN; + PUTBACK; sv = decode_json (self->incr_text, self, &offset); XPUSHs (sv); - self->incr_pos -= offset - SvPVX (self->incr_text); + self->incr_pos -= offset; self->incr_nest = 0; self->incr_mode = 0; - sv_chop (self->incr_text, offset); + sv_chop (self->incr_text, SvPVX (self->incr_text) + offset); } while (GIMME_V == G_ARRAY); } @@ -2066,6 +2350,8 @@ void DESTROY (JSON *self) CODE: + SvREFCNT_dec (self->v_false); + SvREFCNT_dec (self->v_true); SvREFCNT_dec (self->cb_sk_object); SvREFCNT_dec (self->cb_object); SvREFCNT_dec (self->incr_text); @@ -2078,8 +2364,7 @@ JSON json; json_init (&json); json.flags |= F_UTF8; - PUTBACK; scalar = encode_json (scalar, &json); SPAGAIN; - XPUSHs (scalar); + PUTBACK; XPUSHs (encode_json (scalar, &json)); } void decode_json (SV *jsonstr) @@ -2088,7 +2373,6 @@ JSON json; json_init (&json); json.flags |= F_UTF8; - PUTBACK; jsonstr = decode_json (jsonstr, &json, 0); SPAGAIN; - XPUSHs (jsonstr); + PUTBACK; XPUSHs (decode_json (jsonstr, &json, 0)); }