--- JSON-XS/XS.xs 2007/03/24 22:55:16 1.13 +++ JSON-XS/XS.xs 2007/04/03 23:34:17 1.23 @@ -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) @@ -71,7 +78,10 @@ return ((s[0] & 0x1f) << 6) | (s[1] & 0x3f); } else - return (UV)-1; + { + *clen = (STRLEN)-1; + return (UV)-1; + } } ///////////////////////////////////////////////////////////////////////////// @@ -83,9 +93,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 @@ -263,6 +273,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; @@ -338,6 +351,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))) @@ -415,6 +431,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) { @@ -443,22 +486,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 @@ -467,7 +495,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)"); @@ -478,7 +506,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); @@ -503,7 +531,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 @@ -522,12 +552,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]; @@ -646,18 +680,20 @@ ERR ("malformed UTF-8 character in JSON string"); do - { - *cur++ = *dec->cur++; - } + *cur++ = *dec->cur++; while (--clen); utf8 = 1; } - else if (!ch) - ERR ("unexpected end of string while parsing json string"); else - ERR ("invalid character encountered"); + { + --dec->cur; + if (!ch) + ERR ("unexpected end of string while parsing json string"); + else + ERR ("invalid character encountered while parsing json string"); + } } while (cur < buf + SHORT_STRING_LEN); @@ -780,7 +816,9 @@ { AV *av = newAV (); + DEC_INC_DEPTH; decode_ws (dec); + if (*dec->cur == ']') ++dec->cur; else @@ -808,10 +846,12 @@ ++dec->cur; } + DEC_DEC_DEPTH; return newRV_noinc ((SV *)av); fail: SvREFCNT_dec (av); + DEC_DEC_DEPTH; return 0; } @@ -820,7 +860,9 @@ { HV *hv = newHV (); + DEC_INC_DEPTH; decode_ws (dec); + if (*dec->cur == '}') ++dec->cur; else @@ -843,8 +885,8 @@ goto fail; } - //TODO: optimise hv_store_ent (hv, key, value, 0); + SvREFCNT_dec (key); decode_ws (dec); @@ -860,10 +902,12 @@ ++dec->cur; } + DEC_DEC_DEPTH; return newRV_noinc ((SV *)hv); fail: SvREFCNT_dec (hv); + DEC_DEC_DEPTH; return 0; } @@ -925,10 +969,12 @@ } static SV * -decode_json (SV *string, UV flags) +decode_json (SV *string, U32 flags) { SV *sv; + SvUPGRADE (string, SVt_PV); + if (flags & F_UTF8) sv_utf8_downgrade (string, 0); else @@ -937,11 +983,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) @@ -960,7 +1009,7 @@ pv_uni_display (uni, dec.cur, dec.end - dec.cur, 20, UNI_DISPLAY_QQ); LEAVE; - croak ("%s, at character offset %d (%s)", + croak ("%s, at character offset %d [\"%s\"]", dec.err, (int)offset, dec.cur != dec.end ? SvPV_nolen (uni) : "(end of string)"); @@ -984,14 +1033,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); } @@ -1028,6 +1076,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))); @@ -1039,10 +1105,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));