--- JSON-XS/XS.xs 2007/03/22 16:40:16 1.1 +++ JSON-XS/XS.xs 2007/03/22 21:13:58 1.4 @@ -13,7 +13,9 @@ #define F_SPACE_BEFORE 0x00000010 #define F_SPACE_AFTER 0x00000020 #define F_JSON_RPC 0x00000040 +#define F_ALLOW_NONREF 0x00000080 +#define F_PRETTY F_INDENT | F_SPACE_BEFORE | F_SPACE_AFTER #define F_DEFAULT 0 #define INIT_SIZE 32 // initial scalar size to be allocated @@ -40,7 +42,7 @@ { char *cur; char *end; - char *err; + const char *err; UV flags; } dec_t; @@ -63,7 +65,7 @@ STRLEN cur = enc->cur - SvPVX (enc->sv); SvGROW (enc->sv, cur + len + 1); enc->cur = SvPVX (enc->sv) + cur; - enc->end = SvEND (enc->sv); + enc->end = SvPVX (enc->sv) + SvLEN (enc->sv); } } @@ -79,13 +81,44 @@ { char *end = str + len; + need (enc, len); + while (str < end) { unsigned char ch = *(unsigned char *)str; - if (ch >= 0x20 && ch < 0x80) // most common case + + if (ch == '"') + { + need (enc, len += 1); + *enc->cur++ = '\\'; + *enc->cur++ = '"'; + ++str; + } + else if (ch == '\\') + { + need (enc, len += 1); + *enc->cur++ = '\\'; + *enc->cur++ = '\\'; + ++str; + } + else if (ch >= 0x20 && ch < 0x80) // most common case { *enc->cur++ = ch; - str++; + ++str; + } + else if (ch == '\015') + { + need (enc, len += 1); + *enc->cur++ = '\\'; + *enc->cur++ = 'r'; + ++str; + } + else if (ch == '\012') + { + need (enc, len += 1); + *enc->cur++ = '\\'; + *enc->cur++ = 'n'; + ++str; } else { @@ -104,14 +137,11 @@ clen = 1; } - need (enc, len += 6); - - if (uch < 0xa0 || enc->flags & F_ASCII) + if (uch < 0x80 || enc->flags & F_ASCII) { if (uch > 0xFFFFUL) { - len += 6; - need (enc, len += 6); + need (enc, len += 11); sprintf (enc->cur, "\\u%04x\\u%04x", (uch - 0x10000) / 0x400 + 0xD800, (uch - 0x10000) % 0x400 + 0xDC00); @@ -119,19 +149,30 @@ } else { - sprintf (enc->cur, "\\u%04x", uch); - enc->cur += 6; + static char hexdigit [16] = "0123456789abcdef"; + need (enc, len += 5); + *enc->cur++ = '\\'; + *enc->cur++ = 'u'; + *enc->cur++ = hexdigit [ uch >> 12 ]; + *enc->cur++ = hexdigit [(uch >> 8) & 15]; + *enc->cur++ = hexdigit [(uch >> 4) & 15]; + *enc->cur++ = hexdigit [(uch >> 0) & 15]; } + + str += clen; } else if (is_utf8) { - memcpy (enc->cur, str, clen); - enc->cur += clen; + need (enc, len += clen); + while (clen--) + *enc->cur++ = *str++; } else - enc->cur = uvuni_to_utf8_flags (enc->cur, uch, 0); - - str += clen; + { + need (enc, 10); // never more than 11 bytes needed + enc->cur = uvuni_to_utf8_flags (enc->cur, uch, 0); + ++str; + } } --len; @@ -148,7 +189,7 @@ } \ SE -#define SPACE SB if (enc->flags & F_INDENT) { need (enc, 1); encode_ch (enc, ' '); } SE +#define SPACE SB need (enc, 1); encode_ch (enc, ' '); SE #define NL SB if (enc->flags & F_INDENT) { need (enc, 1); encode_ch (enc, '\n'); } SE #define COMMA SB \ encode_ch (enc, ','); \ @@ -192,7 +233,10 @@ { SV *sv = HeSVKEY (he); STRLEN len; - char *str = SvPV (sv, len); + char *str; + + SvGETMAGIC (sv); + str = SvPV (sv, len); encode_str (enc, str, len, SvUTF8 (sv)); } @@ -313,6 +357,8 @@ static void encode_sv (enc_t *enc, SV *sv) { + SvGETMAGIC (sv); + if (SvPOKp (sv)) { STRLEN len; @@ -360,6 +406,9 @@ static SV * encode_json (SV *scalar, UV flags) { + if (!(flags & F_ALLOW_NONREF) && !SvROK (scalar)) + croak ("hash- or arraref required (not a simple scalar, use allow_nonref to allow this)"); + enc_t enc; enc.flags = flags; enc.sv = sv_2mortal (NEWSV (0, INIT_SIZE)); @@ -399,11 +448,6 @@ static SV *decode_sv (dec_t *dec); -#define APPEND_CH(ch) SB \ - SvGROW (sv, cur + 1 + 1); \ - SvPVX (sv)[cur++] = (ch); \ - SE - static signed char decode_hexdigit[256]; static UV @@ -431,12 +475,28 @@ return (UV)-1; } +#define APPEND_GROW(n) SB \ + if (cur + (n) >= end) \ + { \ + STRLEN ofs = cur - SvPVX (sv); \ + SvGROW (sv, ofs + (n) + 1); \ + cur = SvPVX (sv) + ofs; \ + end = SvEND (sv); \ + } \ + SE + +#define APPEND_CH(ch) SB \ + APPEND_GROW (1); \ + *cur++ = (ch); \ + SE + static SV * decode_str (dec_t *dec) { SV *sv = NEWSV (0,2); - STRLEN cur = 0; int utf8 = 0; + char *cur = SvPVX (sv); + char *end = SvEND (sv); for (;;) { @@ -491,8 +551,8 @@ { utf8 = 1; - SvGROW (sv, cur + 4 + 1); // at most 4 bytes for 21 bits - cur = (char *)uvuni_to_utf8_flags (SvPVX (sv) + cur, hi, 0) - SvPVX (sv); + APPEND_GROW (4); // at most 4 bytes for 21 bits + cur = (char *)uvuni_to_utf8_flags (cur, hi, 0); } else APPEND_CH (hi); @@ -509,8 +569,9 @@ if (clen < 0) ERR ("malformed UTF-8 character in string, cannot convert to JSON"); - SvGROW (sv, cur + clen + 1); // at most 4 bytes for 21 bits - memcpy (SvPVX (sv) + cur, dec->cur, clen); + APPEND_GROW (clen); + memcpy (cur, dec->cur, clen); + cur += clen; dec->cur += clen; } else @@ -519,9 +580,9 @@ ++dec->cur; - SvPOK_only (sv); + SvCUR_set (sv, cur - SvPVX (sv)); - SvCUR_set (sv, cur); + SvPOK_only (sv); *SvEND (sv) = 0; if (utf8) @@ -773,8 +834,12 @@ dec.cur != dec.end ? SvPV_nolen (uni) : "(end of string)"); } - sv_dump (sv);//D - return sv_2mortal (sv); + sv = sv_2mortal (sv); + + if (!(dec.flags & F_ALLOW_NONREF) && !SvROK (sv)) + croak ("JSON object or array expected (but number, string, true, false or null found, use allow_nonref to allow this)"); + + return sv; } MODULE = JSON::XS PACKAGE = JSON::XS @@ -787,7 +852,7 @@ for (i = 10; i--; ) decode_hexdigit ['0' + i] = i; - for (i = 6; --i; ) + for (i = 7; i--; ) { decode_hexdigit ['a' + i] = 10 + i; decode_hexdigit ['A' + i] = 10 + i; @@ -796,6 +861,8 @@ json_stash = gv_stashpv ("JSON::XS", 1); } +PROTOTYPES: DISABLE + SV *new (char *dummy) CODE: RETVAL = sv_bless (newRV_noinc (newSVuv (F_DEFAULT)), json_stash); @@ -811,6 +878,8 @@ space_before = F_SPACE_BEFORE space_after = F_SPACE_AFTER json_rpc = F_JSON_RPC + pretty = F_PRETTY + allow_nonref = F_ALLOW_NONREF CODE: { UV *uv = SvJSON (self); @@ -828,7 +897,17 @@ PPCODE: XPUSHs (encode_json (scalar, *SvJSON (self))); -void decode (SV *self, SV *jsondata) +void decode (SV *self, SV *jsonstr) + PPCODE: + XPUSHs (decode_json (jsonstr, *SvJSON (self))); + +PROTOTYPES: ENABLE + +void to_json (SV *scalar) + PPCODE: + XPUSHs (encode_json (scalar, F_UTF8)); + +void from_json (SV *jsonstr) PPCODE: - XPUSHs (decode_json (jsondata, *SvJSON (self))); + XPUSHs (decode_json (jsonstr, F_UTF8));