pike.git / src / post_modules / JSON / json.cmod

version» Context lines:

pike.git/src/post_modules/JSON/json.cmod:5:   */      /* JSON tools.    *    * Ref: RFC 4627    *    * Decoder comes from Public.Parser.JSON2 by Arne Gödeke.    * Encoder created May 2010 by Martin Stjernholm.    */    - #include "array.h" + #include "module.h"   #include "cyclic.h" - #include "global.h" +    #include "interpret.h" - #include "mapping.h" - #include "module.h" +    #include "pike_error.h"   #include "pike_float.h"   #include "pike_types.h" - #include "stralloc.h" - #include "svalue.h" + #include "module_support.h"      #define DEFAULT_CMOD_STORAGE static      DECLARATIONS      /*! @module Standards */      /*! @module JSON */    - #define ASCII_ONLY 1 - #define HUMAN_READABLE 2 - #define PIKE_CANONICAL 4 + #define JSON_ASCII_ONLY (1<<0) + #define JSON_HUMAN_READABLE (1<<1) + #define JSON_PIKE_CANONICAL (1<<2)    - #define JSON_UTF8 1 - #define JSON_ERROR 2 - #define JSON_VALIDATE 4 + #define JSON_UTF8 (1<<3) + #define JSON_ERROR (1<<4) + #define JSON_VALIDATE (1<<5) + #define JSON_FIRST_VALUE (1<<6) + #define JSON_NO_OBJ (1<<7)      static char *err_msg;    - #define PUSH_SPECIAL(X) do {if (!(state->flags&JSON_VALIDATE)) { \ -  push_object (get_val_##X()); \ + #define PUSH_SPECIAL(X,Y) do {if (!(state->flags&JSON_VALIDATE)) { \ +  if (state->flags&JSON_NO_OBJ) { Y; } \ +  else push_object (get_val_##X()); \   } c++; if (state->level > 0) return p+1; } while(0)      #define PARSE(X, FPC) do { \    state->level++; \    i = _parse_JSON_##X(str, FPC, pe, state); \    state->level--; \    if (state->flags&JSON_ERROR) { \    return i; \    } else if (state->level > 0) return i; \    c++; \
pike.git/src/post_modules/JSON/json.cmod:79:    struct pike_string *val)   {    PCHARP str = MKPCHARP_STR (val);    ptrdiff_t l = val->len, i, s;    for (s = 0, i = 0; i < l; i++) {    p_wchar2 c = INDEX_PCHARP (str, i);    if (c < 0 || c > 0x10ffff)    Pike_error ("Cannot json encode non-unicode char "    "0x%"PRINTPIKEINT"x.\n", (INT_TYPE) c);    if (c == '"' || c == '\\' || c <= 0x1f || -  (c >= 0x7f && flags & ASCII_ONLY) || +  (c >= 0x7f && flags & JSON_ASCII_ONLY) ||    /* Let's escape U+2028 and U+2029 as well for compat with    * parsers built in javascript - see    * http://timelessrepo.com/json-isnt-a-javascript-subset. */    c == 0x2028 || c == 0x2029) {    if (s < i) {    PCHARP piece = ADD_PCHARP (str, s);    string_builder_append (buf, piece, i - s);    }    string_builder_putchar (buf, '\\');    switch (c) {
pike.git/src/post_modules/JSON/json.cmod:256:    switch (TYPEOF(*val)) {    case PIKE_T_STRING: {    struct string_builder *buf = &ctx->buf;    string_builder_putchar (buf, '"');    json_escape_string (buf, ctx->flags, val->u.string);    string_builder_putchar (buf, '"');    break;    }       case PIKE_T_INT: +  if(SUBTYPEOF(*val)) +  string_builder_strcat(&ctx->buf,"null"); +  else    string_builder_append_integer (&ctx->buf, val->u.integer,    10, APPEND_SIGNED, 0, 0);    break;       case PIKE_T_FLOAT: {    FLOAT_TYPE f = val->u.float_number;    char b[MAX_FLOAT_SPRINTF_LEN];    if (PIKE_ISNAN (f) || PIKE_ISINF (f))    {    string_builder_strcat(&ctx->buf, "null");
pike.git/src/post_modules/JSON/json.cmod:311:    }    }    string_builder_putchar (buf, ']');    break;    }       case PIKE_T_MAPPING: {    string_builder_putchar (&ctx->buf, '{');    check_mapping_for_destruct (val->u.mapping);    if (m_sizeof (val->u.mapping)) { -  if (ctx->flags & PIKE_CANONICAL) +  if (ctx->flags & JSON_PIKE_CANONICAL)    encode_mapcont_canon (ctx, val->u.mapping);    else    encode_mapcont (ctx, val->u.mapping);    if (ctx->indent >= 0) {    int indent = ctx->indent = ctx->indent - 2;    string_builder_putchar (&ctx->buf, '\n');    string_builder_putchars (&ctx->buf, ' ', indent);    }    }    string_builder_putchar (&ctx->buf, '}');
pike.git/src/post_modules/JSON/json.cmod:362:    apply_low (o, fun, args);    }    if (TYPEOF(Pike_sp[-1]) != PIKE_T_STRING)    Pike_error ("Expected string from %O->encode_json(), got %s.\n",    val, get_name_of_type (TYPEOF(Pike_sp[-1])));    string_builder_shared_strcat (&ctx->buf, Pike_sp[-1].u.string);    free_string ((--Pike_sp)->u.string);    break;    }    } +  /* FALLTHRU */       default:    Pike_error ("Cannot json encode %s.\n", get_name_of_type (TYPEOF(*val)));    }       if (TYPEOF(*val) <= MAX_COMPLEX)    END_CYCLIC();   }      /*! @decl constant ASCII_ONLY
pike.git/src/post_modules/JSON/json.cmod:384:    *!    *! Bit field flags for use with @[encode]:    *!    *! @dl    *! @item Standards.JSON.ASCII_ONLY    *! Use @expr{\uxxxx@} escapes for all non-ascii characters and DEL    *! (U+007f). The default is to escape only the characters that must    *! be escaped. The flag value is 1.    *!    *! Characters above U+FFFF are encoded using escaped surrogate -  *! pairs, as per RFC 4627. +  *! pairs, as per @rfc{4627@}.    *!    *! @item Standards.JSON.HUMAN_READABLE    *! Pretty print with indentation to make the result easier on human    *! eyes. The default is to use no extra whitespace at all. The flag    *! value is 2.    *!    *! @item Standards.JSON.PIKE_CANONICAL    *! Make the output canonical, so that the same value always    *! generates the same char-by-char equal string. In practice this    *! means that mapping elements are sorted on their indices. Note
pike.git/src/post_modules/JSON/json.cmod:409:    *! This canonical form is stable for the @[encode] function,    *! providing floats aren't used (their formatting is currently    *! affected by float size and libc formatting code). In the future    *! there may be a standardized canonical form which quite likely    *! will be different from this one. In that case a separate flag    *! has to be added so this one doesn't change - hence the name    *! @[PIKE_CANONICAL].    *! @enddl    */    + /*! @decl constant NO_OBJECTS +  *! +  *! Bit field flags for use with @[decode]: +  *! +  *! @dl +  *! @item Standards.JSON.NO_OBJECTS +  *! Do not decode @expr{"true"@}, @expr{"false"@} and @expr{"null"@} +  *! into @[Val.true], @[Val.false] and @[Val.null], but instead +  *! @expr{1@}, @expr{0@} and @expr{UNDEFINED@}. +  *! @enddl +  */ +    /*! @decl string encode (int|float|string|array|mapping|object val, @    *! void|int flags, void|function|object|program|string callback)    *!    *! Encodes a value to a JSON string.    *!    *! @param val    *! The value to encode. It can contain integers, floats (except the    *! special numbers NaN and infinity), strings, arrays, mappings    *! with string indices, and the special object values @[null],    *! @[true] and @[false] defined in this module (or really any
pike.git/src/post_modules/JSON/json.cmod:444:    *! 8-bit and wider characters in input strings are neither escaped    *! nor utf-8 encoded by default. @[string_to_utf8] can be used safely    *! on the returned string to get a valid transport encoded JSON    *! string. See @[escape_string] for further details on string    *! escaping.    *!    *! @seealso    *! @[escape_string]    */   PIKEFUN string encode (int|float|string|array|mapping|object val, -  void|int flags, void|function|object|program|string callback) +  void|int flags, void|function|object|program|string callback, +  void|int base_indent )    optflags OPT_TRY_OPTIMIZE;   {    struct encode_context ctx;    ONERROR uwp;    ctx.flags = (flags ? flags->u.integer : 0); -  ctx.indent = (ctx.flags & HUMAN_READABLE ? 0 : -1); +  ctx.indent = (ctx.flags & JSON_HUMAN_READABLE ? base_indent ? base_indent->u.integer : 0 : -1);    ctx.callback = callback;    init_string_builder (&ctx.buf, 0);    SET_ONERROR (uwp, free_string_builder, &ctx.buf);    json_encode_recur (&ctx, val);    UNSET_ONERROR (uwp);    RETURN finish_string_builder (&ctx.buf);   }      /*! @decl string escape_string (string str, void|int flags)    *!
pike.git/src/post_modules/JSON/json.cmod:520:       if (state.flags & JSON_ERROR || stop != data->len) {    push_int((INT_TYPE)stop);    } else {    push_int(-1);    }       return;   }    + ptrdiff_t parse_json_pcharp( PCHARP data, size_t len, int flags, char **err_out ) + { +  ptrdiff_t stop; +  struct parser_state state; +  +  err_msg = NULL; +  state.flags = flags; +  state.level = 0; +  stop = _parse_JSON(data, 0, len, &state ); +  if (state.flags & JSON_ERROR ) +  { +  if( err_out ) +  *err_out = err_msg; +  return -stop; +  } +  return stop; + } +    void low_decode(struct pike_string *data, int flags) {    ptrdiff_t stop;    struct parser_state state;       err_msg = NULL;       state.level = 0;    state.flags = flags;       stop = _parse_JSON(MKPCHARP_STR(data), 0, data->len, &state);
pike.git/src/post_modules/JSON/json.cmod:563:    *!    *! @returns    *! In case the string contains valid JSON @expr{-1@} is returned. It is then guaranteed to be parsed    *! without errors by @[decode()].    *! In case the string is not valid JSON, the error position is returned.    */   PIKEFUN int validate(string data) {    low_validate(data, 0);   }    - /*! @decl array|mapping|string|float|int|object decode(string s) + /*! @decl array|mapping|string|float|int|object decode(string s, void|int flags)    *!    *! Decodes a JSON string.    *! -  +  *! @param flags +  *! The flag @[NO_OBJECTS] can be used to output @expr{1@}, @expr{0@} +  *! and @expr{UNDEFINED@} instead of @[Val.true], @[Val.false] and +  *! @[Val.null]. +  *!    *! @throws    *! Throws an exception in case the data contained in @expr{s@} is not valid    *! JSON.    */ - PIKEFUN array|mapping|string|float|int|object decode(string data) { -  low_decode(data, 0); + PIKEFUN array|mapping|string|float|int|object decode(string data, +  void|int flags) { +  int f = (flags ? flags->u.integer : 0) & (JSON_UTF8|JSON_NO_OBJ); +  low_decode(data, f);   }      /*! @decl int validate_utf8(string s)    *!    *! Checks if a string is valid utf8 encoded JSON.    *!    *! @returns    *! In case the string contains valid JSON @expr{-1@} is returned. It is then guaranteed to be parsed    *! without errors by @[decode()].    *! In case the string is not valid JSON, the integer position inside the string
pike.git/src/post_modules/JSON/json.cmod:605:    *! Should give the same result as @expr{Standards.JSON.decode(utf8_to_string(s))@}.    *!    *! @throws    *! Throws an exception in case the data contained in @expr{s@} is not valid    *! JSON.    */   PIKEFUN array|mapping|string|float|int|object decode_utf8(string data) {    if (data->size_shift) {    ref_push_string(data);    push_int(0); -  push_text("Strings wider than 1 byte are NOT valid UTF-8."); +  push_static_text("Strings wider than 1 byte are NOT valid UTF-8.");    apply (Pike_fp->current_object, "decode_error", 3);    }       low_decode(data, JSON_UTF8);   }      /*! @endmodule */      /*! @endmodule */      PIKE_MODULE_INIT   { -  add_integer_constant ("ASCII_ONLY", ASCII_ONLY, 0); -  add_integer_constant ("HUMAN_READABLE", HUMAN_READABLE, 0); -  add_integer_constant ("PIKE_CANONICAL", PIKE_CANONICAL, 0); +  add_integer_constant ("ASCII_ONLY", JSON_ASCII_ONLY, 0); +  add_integer_constant ("HUMAN_READABLE", JSON_HUMAN_READABLE, 0); +  add_integer_constant ("PIKE_CANONICAL", JSON_PIKE_CANONICAL, 0); +  add_integer_constant ("NO_OBJECTS", JSON_NO_OBJ, 0);       INIT; -  +  +  PIKE_MODULE_EXPORT(Standards.JSON, parse_json_pcharp );   }      PIKE_MODULE_EXIT   {    EXIT;   }