--- JSON-XS/XS.xs 2007/03/24 22:10:08 1.12 +++ JSON-XS/XS.xs 2007/03/29 02:45:49 1.21 @@ -6,23 +6,30 @@ #include "string.h" #include "stdlib.h" -#define F_ASCII 0x00000001 -#define F_UTF8 0x00000002 -#define F_INDENT 0x00000004 -#define F_CANONICAL 0x00000008 -#define F_SPACE_BEFORE 0x00000010 -#define F_SPACE_AFTER 0x00000020 -#define F_ALLOW_NONREF 0x00000080 -#define F_SHRINK 0x00000100 +#define F_ASCII 0x00000001UL +#define F_UTF8 0x00000002UL +#define F_INDENT 0x00000004UL +#define F_CANONICAL 0x00000008UL +#define F_SPACE_BEFORE 0x00000010UL +#define F_SPACE_AFTER 0x00000020UL +#define F_ALLOW_NONREF 0x00000080UL +#define F_SHRINK 0x00000100UL +#define F_MAXDEPTH 0xf8000000UL +#define S_MAXDEPTH 27 + +#define DEC_DEPTH(flags) (1UL << ((flags & F_MAXDEPTH) >> S_MAXDEPTH)) + +// F_SELFCONVERT? <=> to_json/toJson +// F_BLESSED? <=> { $__class__$ => } #define F_PRETTY F_INDENT | F_SPACE_BEFORE | F_SPACE_AFTER -#define F_DEFAULT 0 +#define F_DEFAULT (12UL << S_MAXDEPTH) #define INIT_SIZE 32 // initial scalar size to be allocated #define INDENT_STEP 3 // spaces per indentation level #define UTF8_MAX_LEN 11 // for perls UTF-X: max. number of octets per character -#define SHORT_STRING_LEN 256 // special-case strings of up to this size +#define SHORT_STRING_LEN 512 // special-case strings of up to this size #define SB do { #define SE } while (0) @@ -55,6 +62,25 @@ } } +// decode an 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. +// note that we never call this function for a ascii codepoints +static UV +decode_utf8 (unsigned char *s, STRLEN len, STRLEN *clen) +{ + if (s[0] > 0xdf || s[0] < 0xc2) + return utf8n_to_uvuni (s, len, clen, UTF8_CHECK_ONLY); + else if (len > 1 && s[1] >= 0x80 && s[1] <= 0xbf) + { + *clen = 2; + return ((s[0] & 0x1f) << 6) | (s[1] & 0x3f); + } + else + return (UV)-1; +} + ///////////////////////////////////////////////////////////////////////////// // encoder @@ -64,9 +90,9 @@ char *cur; // SvPVX (sv) + current output position char *end; // SvEND (sv) SV *sv; // result scalar - UV flags; // F_* - int indent; // indentation level - int max_depth; // max. recursion level + U32 flags; // F_* + U32 indent; // indentation level + U32 maxdepth; // max. indentation/recursion level } enc_t; static void @@ -135,7 +161,8 @@ if (is_utf8) { - uch = utf8n_to_uvuni (str, end - str, &clen, UTF8_CHECK_ONLY); + //uch = utf8n_to_uvuni (str, end - str, &clen, UTF8_CHECK_ONLY); + uch = decode_utf8 (str, end - str, &clen); if (clen == (STRLEN)-1) croak ("malformed or illegal unicode character in string [%.11s], cannot convert to JSON", str); } @@ -243,6 +270,9 @@ { int i, len = av_len (av); + if (enc->indent >= enc->maxdepth) + croak ("data structure too deep (hit recursion limit)"); + encode_ch (enc, '['); encode_nl (enc); ++enc->indent; @@ -318,6 +348,9 @@ { int count, i; + 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))) @@ -395,6 +428,33 @@ --enc->indent; encode_indent (enc); encode_ch (enc, '}'); } +// encode objects, arrays and special \0=false and \1=true values. +static void +encode_rv (enc_t *enc, SV *sv) +{ + SvGETMAGIC (sv); + + svtype svt = SvTYPE (sv); + + if (svt == SVt_PVHV) + encode_hv (enc, (HV *)sv); + else if (svt == SVt_PVAV) + 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) + encode_str (enc, "true", 4, 0); + else + croak ("cannot encode reference to scalar '%s' unless the scalar is 0 or 1", + SvPV_nolen (sv_2mortal (newRV_inc (sv)))); + } + else + croak ("encountered %s, but JSON can only represent references to arrays or hashes", + SvPV_nolen (sv_2mortal (newRV_inc (sv)))); +} + static void encode_sv (enc_t *enc, SV *sv) { @@ -423,22 +483,7 @@ : snprintf (enc->cur, 64, "%"IVdf, (IV)SvIVX (sv)); } else if (SvROK (sv)) - { - SV *rv = SvRV (sv); - - if (enc->indent >= enc->max_depth) - croak ("data structure too deep (hit recursion limit)"); - - switch (SvTYPE (rv)) - { - case SVt_PVAV: encode_av (enc, (AV *)rv); break; - case SVt_PVHV: encode_hv (enc, (HV *)rv); break; - - default: - croak ("encountered %s, but JSON can only represent references to arrays or hashes", - SvPV_nolen (sv)); - } - } + encode_rv (enc, SvRV (sv)); else if (!SvOK (sv)) encode_str (enc, "null", 4, 0); else @@ -447,7 +492,7 @@ } static SV * -encode_json (SV *scalar, UV flags) +encode_json (SV *scalar, U32 flags) { if (!(flags & F_ALLOW_NONREF) && !SvROK (scalar)) croak ("hash- or arrayref expected (not a simple scalar, use allow_nonref to allow this)"); @@ -458,7 +503,7 @@ enc.cur = SvPVX (enc.sv); enc.end = SvEND (enc.sv); enc.indent = 0; - enc.max_depth = 0x7fffffffUL; + enc.maxdepth = DEC_DEPTH (flags); SvPOK_only (enc.sv); encode_sv (&enc, scalar); @@ -483,7 +528,9 @@ char *cur; // current parser pointer char *end; // end of input string const char *err; // parse error, if != 0 - UV flags; // F_* + U32 flags; // F_* + U32 depth; // recursion depth + U32 maxdepth; // recursion depth limit } dec_t; static void @@ -502,12 +549,16 @@ } #define ERR(reason) SB dec->err = reason; goto fail; SE + #define EXPECT_CH(ch) SB \ if (*dec->cur != ch) \ ERR (# ch " expected"); \ ++dec->cur; \ SE +#define DEC_INC_DEPTH if (++dec->depth > dec->maxdepth) ERR ("json datastructure exceeds maximum nesting level (set a higher max_depth)") +#define DEC_DEC_DEPTH --dec->depth + static SV *decode_sv (dec_t *dec); static signed char decode_hexdigit[256]; @@ -621,7 +672,7 @@ --dec->cur; STRLEN clen; - UV uch = utf8n_to_uvuni (dec->cur, dec->end - dec->cur, &clen, UTF8_CHECK_ONLY); + UV uch = decode_utf8 (dec->cur, dec->end - dec->cur, &clen); if (clen == (STRLEN)-1) ERR ("malformed UTF-8 character in JSON string"); @@ -760,7 +811,9 @@ { AV *av = newAV (); + DEC_INC_DEPTH; decode_ws (dec); + if (*dec->cur == ']') ++dec->cur; else @@ -788,10 +841,12 @@ ++dec->cur; } + DEC_DEC_DEPTH; return newRV_noinc ((SV *)av); fail: SvREFCNT_dec (av); + DEC_DEC_DEPTH; return 0; } @@ -800,7 +855,9 @@ { HV *hv = newHV (); + DEC_INC_DEPTH; decode_ws (dec); + if (*dec->cur == '}') ++dec->cur; else @@ -823,8 +880,8 @@ goto fail; } - //TODO: optimise hv_store_ent (hv, key, value, 0); + SvREFCNT_dec (key); decode_ws (dec); @@ -840,10 +897,12 @@ ++dec->cur; } + DEC_DEC_DEPTH; return newRV_noinc ((SV *)hv); fail: SvREFCNT_dec (hv); + DEC_DEC_DEPTH; return 0; } @@ -905,7 +964,7 @@ } static SV * -decode_json (SV *string, UV flags) +decode_json (SV *string, U32 flags) { SV *sv; @@ -917,11 +976,14 @@ SvGROW (string, SvCUR (string) + 1); // should basically be a NOP dec_t dec; - dec.flags = flags; - dec.cur = SvPVX (string); - dec.end = SvEND (string); - dec.err = 0; + dec.flags = flags; + dec.cur = SvPVX (string); + dec.end = SvEND (string); + dec.err = 0; + dec.depth = 0; + dec.maxdepth = DEC_DEPTH (dec.flags); + *dec.end = 0; // this should basically be a nop, too, but make sure its there sv = decode_sv (&dec); if (!sv) @@ -964,14 +1026,13 @@ int i; memset (decode_hexdigit, 0xff, 256); - for (i = 10; i--; ) - decode_hexdigit ['0' + i] = i; - for (i = 7; i--; ) - { - decode_hexdigit ['a' + i] = 10 + i; - decode_hexdigit ['A' + i] = 10 + i; - } + for (i = 0; i < 256; ++i) + decode_hexdigit [i] = + i >= '0' && i <= '9' ? i - '0' + : i >= 'a' && i <= 'f' ? i - 'a' + 10 + : i >= 'A' && i <= 'F' ? i - 'A' + 10 + : -1; json_stash = gv_stashpv ("JSON::XS", 1); } @@ -1008,6 +1069,24 @@ OUTPUT: RETVAL +SV *max_depth (SV *self, int max_depth = 0x80000000UL) + CODE: +{ + UV *uv = SvJSON (self); + UV log2 = 0; + + if (max_depth > 0x80000000UL) max_depth = 0x80000000UL; + + while ((1UL << log2) < max_depth) + ++log2; + + *uv = *uv & ~F_MAXDEPTH | (log2 << S_MAXDEPTH); + + RETVAL = newSVsv (self); +} + OUTPUT: + RETVAL + void encode (SV *self, SV *scalar) PPCODE: XPUSHs (encode_json (scalar, *SvJSON (self))); @@ -1019,10 +1098,14 @@ PROTOTYPES: ENABLE void to_json (SV *scalar) + ALIAS: + objToJson = 0 PPCODE: - XPUSHs (encode_json (scalar, F_UTF8)); + XPUSHs (encode_json (scalar, F_DEFAULT | F_UTF8)); void from_json (SV *jsonstr) + ALIAS: + jsonToObj = 0 PPCODE: - XPUSHs (decode_json (jsonstr, F_UTF8)); + XPUSHs (decode_json (jsonstr, F_DEFAULT | F_UTF8));