df4c9a2010-05-28Martin Stjernholm /* -*- c -*- || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. */
94e8ea2010-06-10Martin Stjernholm /* JSON tools.
df4c9a2010-05-28Martin Stjernholm  * * Ref: RFC 4627 *
8207a82010-06-10Martin Stjernholm  * Decoder comes from Public.Parser.JSON2 by Arne Gödeke.
94e8ea2010-06-10Martin Stjernholm  * Encoder created May 2010 by Martin Stjernholm.
df4c9a2010-05-28Martin Stjernholm  */
14008d2018-01-18Martin Nilsson #include "module.h"
df4c9a2010-05-28Martin Stjernholm #include "cyclic.h" #include "interpret.h" #include "pike_error.h" #include "pike_float.h" #include "pike_types.h"
6234972014-08-28Per Hedbor #include "module_support.h"
df4c9a2010-05-28Martin Stjernholm  #define DEFAULT_CMOD_STORAGE static DECLARATIONS /*! @module Standards */ /*! @module JSON */
65b3222017-10-29Martin Nilsson #define JSON_ASCII_ONLY (1<<0) #define JSON_HUMAN_READABLE (1<<1) #define JSON_PIKE_CANONICAL (1<<2)
df4c9a2010-05-28Martin Stjernholm 
65b3222017-10-29Martin Nilsson #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)
423b7b2010-06-10Arne Goedeke  static char *err_msg;
65b3222017-10-29Martin Nilsson #define PUSH_SPECIAL(X,Y) do {if (!(state->flags&JSON_VALIDATE)) { \ if (state->flags&JSON_NO_OBJ) { Y; } \ else push_object (get_val_##X()); \
423b7b2010-06-10Arne Goedeke } 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++; \ } while(0) #define PARSE_STRING(FPC) do { \ state->level++; \ if (state->flags&JSON_UTF8) \ i = _parse_JSON_string_utf8(str, FPC, pe, state); \ else \ i = _parse_JSON_string(str, FPC, pe, state); \ state->level--; \ if (state->flags&JSON_ERROR) { \ return i; \ } else if (state->level > 0) return i; \ c++; \ } while(0)
02de272011-09-21Martin Stjernholm #define IS_SURROGATE(x) ((x) >= 0xd800 && (x) < 0xe000) #define IS_HIGH_SURROGATE(x) ((x) >= 0xd800 && (x) < 0xdc00) #define IS_LOW_SURROGATE(x) ((x) >= 0xdc00 && (x) < 0xe000) #define IS_NUNICODE(x) ((x) < 0 || IS_SURROGATE (x) || (x) > 0x10ffff) #define IS_NUNICODE1(x) ((x) < 0 || IS_SURROGATE (x))
423b7b2010-06-10Arne Goedeke 
e383f22011-09-21Martin Stjernholm static void json_escape_string (struct string_builder *buf, int flags, 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 ||
8d73572017-10-29Martin Nilsson  (c >= 0x7f && flags & JSON_ASCII_ONLY) ||
e383f22011-09-21Martin Stjernholm  /* 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) { case '"': string_builder_putchar (buf, '"'); break; case '\\': string_builder_putchar (buf, '\\'); break; case '\b': string_builder_putchar (buf, 'b'); break; case '\f': string_builder_putchar (buf, 'f'); break; case '\n': string_builder_putchar (buf, 'n'); break; case '\r': string_builder_putchar (buf, 'r'); break; case '\t': string_builder_putchar (buf, 't'); break; default:
4552562011-09-21Martin Stjernholm  if (c <= 0xffff) { string_builder_putchar (buf, 'u'); string_builder_append_integer (buf, c, 16, APPEND_ZERO_PAD, 4, 4); } else { string_builder_putchar (buf, 'u'); c -= 0x10000; string_builder_append_integer (buf, (c >> 10) + 0xd800, 16, APPEND_ZERO_PAD, 4, 4); string_builder_strcat (buf, "\\u"); string_builder_append_integer (buf, (c & 0x3ff) + 0xdc00, 16, APPEND_ZERO_PAD, 4, 4); }
e383f22011-09-21Martin Stjernholm  break; } s = i + 1; } } if (s < i) { PCHARP piece = ADD_PCHARP (str, s); string_builder_append (buf, piece, i - s); } }
423b7b2010-06-10Arne Goedeke struct parser_state { unsigned int level; int flags; };
42c8572010-06-10Martin Stjernholm struct encode_context {
df4c9a2010-05-28Martin Stjernholm  struct string_builder buf;
9eb5022010-05-28Martin Stjernholm  int flags;
df4c9a2010-05-28Martin Stjernholm  int indent;
71108f2013-05-23Martin Baehr  struct svalue *callback;
df4c9a2010-05-28Martin Stjernholm };
42c8572010-06-10Martin Stjernholm static void json_encode_recur (struct encode_context *ctx, struct svalue *val);
81d0ba2010-05-29Martin Stjernholm 
42c8572010-06-10Martin Stjernholm static void encode_mapcont (struct encode_context *ctx, struct mapping *m)
81d0ba2010-05-29Martin Stjernholm /* Assumes there's at least one element. */ { struct string_builder *buf = &ctx->buf; int e, notfirst = 0; struct keypair *k; struct mapping_data *md = m->data; NEW_MAPPING_LOOP (md) { if (notfirst) { string_builder_putchar (buf, ','); if (ctx->indent >= 0) { int indent = ctx->indent; string_builder_putchar (buf, '\n'); string_builder_putchars (buf, ' ', indent); } } else { if (ctx->indent >= 0) { int indent = ctx->indent = ctx->indent + 2; string_builder_putchar (buf, '\n'); string_builder_putchars (buf, ' ', indent); } notfirst = 1; }
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(k->ind) != T_STRING)
81d0ba2010-05-29Martin Stjernholm  Pike_error ("Cannot json encode mapping with non-string index %O.\n", &k->ind); json_encode_recur (ctx, &k->ind); string_builder_putchar (buf, ':'); if (ctx->indent >= 0) string_builder_putchar (buf, ' '); json_encode_recur (ctx, &k->val); } }
42c8572010-06-10Martin Stjernholm static void encode_mapcont_canon (struct encode_context *ctx, struct mapping *m)
81d0ba2010-05-29Martin Stjernholm /* Assumes there's at least one element. */
df4c9a2010-05-28Martin Stjernholm { struct string_builder *buf = &ctx->buf;
81d0ba2010-05-29Martin Stjernholm  int i, notfirst = 0; struct array *inds = mapping_indices (m); int size = inds->size; ONERROR uwp; SET_ONERROR (uwp, do_free_array, inds);
a53e462010-05-30Martin Stjernholm  /* encode_value_canonic uses get_switch_order, but this sort is good * enough considering we only have to deal (correctly) with strings. */ sort_array_destructively (inds);
81d0ba2010-05-29Martin Stjernholm  for (i = 0; i < size; i++) { struct svalue *ind = ITEM (inds) + i; { /* Push the value on the stack so we know it's still there when * we get to formatting it. Probably not really necessary, but * better safe than sorry. */ struct svalue *val = low_mapping_lookup (m, ind); if (val) push_svalue (val); else /* The entry has disappeared since the array was created. * Ignore it and continue is the right thing to do. */ continue; } if (notfirst) { int indent = ctx->indent; string_builder_putchar (buf, ','); if (indent >= 0) { string_builder_putchar (buf, '\n'); string_builder_putchars (buf, ' ', indent); } } else { int indent = ctx->indent; if (indent >= 0) { ctx->indent = indent = indent + 2; string_builder_putchar (buf, '\n'); string_builder_putchars (buf, ' ', indent); } notfirst = 1; }
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(*ind) != T_STRING)
81d0ba2010-05-29Martin Stjernholm  Pike_error ("Cannot json encode mapping with non-string index %O.\n", ind); json_encode_recur (ctx, ind); string_builder_putchar (buf, ':'); if (ctx->indent >= 0) string_builder_putchar (buf, ' '); json_encode_recur (ctx, Pike_sp - 1); pop_stack(); } UNSET_ONERROR (uwp); free_array (inds); }
42c8572010-06-10Martin Stjernholm static void json_encode_recur (struct encode_context *ctx, struct svalue *val)
81d0ba2010-05-29Martin Stjernholm {
df4c9a2010-05-28Martin Stjernholm  DECLARE_CYCLIC(); check_c_stack (1024);
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(*val) <= MAX_COMPLEX && BEGIN_CYCLIC (val->u.ptr, 0))
df4c9a2010-05-28Martin Stjernholm  Pike_error ("Cyclic data structure - already visited %O.\n", val);
017b572011-10-28Henrik Grubbström (Grubba)  switch (TYPEOF(*val)) {
df4c9a2010-05-28Martin Stjernholm  case PIKE_T_STRING: {
81d0ba2010-05-29Martin Stjernholm  struct string_builder *buf = &ctx->buf;
df4c9a2010-05-28Martin Stjernholm  string_builder_putchar (buf, '"');
e383f22011-09-21Martin Stjernholm  json_escape_string (buf, ctx->flags, val->u.string);
df4c9a2010-05-28Martin Stjernholm  string_builder_putchar (buf, '"'); break; } case PIKE_T_INT:
951c672015-08-31Per Hedbor  if(SUBTYPEOF(*val)) string_builder_strcat(&ctx->buf,"null"); else string_builder_append_integer (&ctx->buf, val->u.integer, 10, APPEND_SIGNED, 0, 0);
df4c9a2010-05-28Martin Stjernholm  break; case PIKE_T_FLOAT: { FLOAT_TYPE f = val->u.float_number; char b[MAX_FLOAT_SPRINTF_LEN];
caf73f2012-07-27Martin Nilsson  if (PIKE_ISNAN (f) || PIKE_ISINF (f)) { string_builder_strcat(&ctx->buf, "null"); break; }
df4c9a2010-05-28Martin Stjernholm  format_pike_float (b, f);
81d0ba2010-05-29Martin Stjernholm  string_builder_strcat (&ctx->buf, b);
df4c9a2010-05-28Martin Stjernholm  break; } case PIKE_T_ARRAY: {
81d0ba2010-05-29Martin Stjernholm  struct string_builder *buf = &ctx->buf;
df4c9a2010-05-28Martin Stjernholm  string_builder_putchar (buf, '[');
81d0ba2010-05-29Martin Stjernholm  { struct array *a = val->u.array; int size = a->size; if (size) { int i; if (ctx->indent >= 0 && size > 1) { int indent = ctx->indent = ctx->indent + 2; string_builder_putchar (buf, '\n'); string_builder_putchars (buf, ' ', indent); } json_encode_recur (ctx, ITEM (a)); for (i = 1; i < size; i++) { string_builder_putchar (buf, ','); if (ctx->indent >= 0) { int indent = ctx->indent; string_builder_putchar (buf, '\n'); string_builder_putchars (buf, ' ', indent); } json_encode_recur (ctx, ITEM (a) + i); } if (ctx->indent >= 0 && size > 1) { int indent = ctx->indent = ctx->indent - 2;
df4c9a2010-05-28Martin Stjernholm  string_builder_putchar (buf, '\n'); string_builder_putchars (buf, ' ', indent); } } } string_builder_putchar (buf, ']'); break; } case PIKE_T_MAPPING: {
81d0ba2010-05-29Martin Stjernholm  string_builder_putchar (&ctx->buf, '{');
df4c9a2010-05-28Martin Stjernholm  check_mapping_for_destruct (val->u.mapping);
81d0ba2010-05-29Martin Stjernholm  if (m_sizeof (val->u.mapping)) {
8d73572017-10-29Martin Nilsson  if (ctx->flags & JSON_PIKE_CANONICAL)
81d0ba2010-05-29Martin Stjernholm  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);
df4c9a2010-05-28Martin Stjernholm  } }
81d0ba2010-05-29Martin Stjernholm  string_builder_putchar (&ctx->buf, '}');
df4c9a2010-05-28Martin Stjernholm  break; } case PIKE_T_OBJECT: { struct object *o = val->u.object; if (o->prog) { int fun = find_identifier ("encode_json", o->prog);
71108f2013-05-23Martin Baehr  if (fun < 0) { int args = 2; if (!ctx->callback) Pike_error ("Cannot json encode object %O " "without encode_json function.\n", val); if (TYPEOF(*ctx->callback) == PIKE_T_STRING) { string_builder_shared_strcat (&ctx->buf, ctx->callback->u.string); break; } push_svalue(val); push_int(ctx->flags); if (ctx->indent >= 0) { args++; push_int(ctx->indent); } apply_svalue(ctx->callback, args); } else {
9eb5022010-05-28Martin Stjernholm  int args = 1; push_int (ctx->flags);
df4c9a2010-05-28Martin Stjernholm  if (ctx->indent >= 0) { push_int (ctx->indent);
9eb5022010-05-28Martin Stjernholm  args++;
df4c9a2010-05-28Martin Stjernholm  }
9eb5022010-05-28Martin Stjernholm  apply_low (o, fun, args);
df4c9a2010-05-28Martin Stjernholm  }
71108f2013-05-23Martin Baehr  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;
df4c9a2010-05-28Martin Stjernholm  } }
2697ae2018-02-13Marcus Comstedt  /* FALLTHRU */
df4c9a2010-05-28Martin Stjernholm  default:
017b572011-10-28Henrik Grubbström (Grubba)  Pike_error ("Cannot json encode %s.\n", get_name_of_type (TYPEOF(*val)));
df4c9a2010-05-28Martin Stjernholm  }
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(*val) <= MAX_COMPLEX)
df4c9a2010-05-28Martin Stjernholm  END_CYCLIC(); }
dcea772010-05-28Martin Stjernholm /*! @decl constant ASCII_ONLY *! @decl constant HUMAN_READABLE
e66b1e2010-05-31Martin Stjernholm  *! @decl constant PIKE_CANONICAL
df4c9a2010-05-28Martin Stjernholm  *! *! 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
dcea772010-05-28Martin Stjernholm  *! be escaped. The flag value is 1.
81d0ba2010-05-29Martin Stjernholm  *!
4552562011-09-21Martin Stjernholm  *! Characters above U+FFFF are encoded using escaped surrogate
974d4a2015-08-22Martin Nilsson  *! pairs, as per @rfc{4627@}.
d7f6252010-05-30Martin Stjernholm  *!
df4c9a2010-05-28Martin Stjernholm  *! @item Standards.JSON.HUMAN_READABLE
1b41412010-06-10Martin Stjernholm  *! 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.
81d0ba2010-05-29Martin Stjernholm  *!
e66b1e2010-05-31Martin Stjernholm  *! @item Standards.JSON.PIKE_CANONICAL *! Make the output canonical, so that the same value always
81d0ba2010-05-29Martin Stjernholm  *! generates the same char-by-char equal string. In practice this
d7f6252010-05-30Martin Stjernholm  *! means that mapping elements are sorted on their indices. Note *! that the other flags take precedence, so e.g. the canonical form *! with @[HUMAN_READABLE] is not the same as the canonical form *! without it. The flag value is 4.
e66b1e2010-05-31Martin Stjernholm  *!
b648ff2010-05-31Martin Stjernholm  *! 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
e66b1e2010-05-31Martin Stjernholm  *! @[PIKE_CANONICAL].
df4c9a2010-05-28Martin Stjernholm  *! @enddl */
65b3222017-10-29Martin Nilsson /*! @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 */
c3175b2010-05-30Martin Stjernholm /*! @decl string encode (int|float|string|array|mapping|object val, @
71108f2013-05-23Martin Baehr  *! void|int flags, void|function|object|program|string callback)
df4c9a2010-05-28Martin Stjernholm  *! *! 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
71108f2013-05-23Martin Baehr  *! object that implements an @expr{encode_json@} callback or *! is handled by the supplied callback argument).
df4c9a2010-05-28Martin Stjernholm  *! *! @param flags
81d0ba2010-05-29Martin Stjernholm  *! Flag bit field to control formatting. See @[ASCII_ONLY],
e66b1e2010-05-31Martin Stjernholm  *! @[HUMAN_READABLE] and @[PIKE_CANONICAL] for further details.
df4c9a2010-05-28Martin Stjernholm  *!
71108f2013-05-23Martin Baehr  *! @param callback *! A function that will be called for types that can not be encoded *! otherwise. It will be called with the value to be encoded as the first *! argument, and optionally with flags and indent arguments. If a string is *! supplied, it will be used to replace the value verbatim. *! The callback must return a string or throw an error. *!
df4c9a2010-05-28Martin Stjernholm  *! @note *! 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
e383f22011-09-21Martin Stjernholm  *! string. See @[escape_string] for further details on string *! escaping.
06d6552011-09-21Martin Stjernholm  *!
e383f22011-09-21Martin Stjernholm  *! @seealso *! @[escape_string]
df4c9a2010-05-28Martin Stjernholm  */
c3175b2010-05-30Martin Stjernholm PIKEFUN string encode (int|float|string|array|mapping|object val,
a23b542014-07-07Per Hedbor  void|int flags, void|function|object|program|string callback, void|int base_indent )
df4c9a2010-05-28Martin Stjernholm  optflags OPT_TRY_OPTIMIZE; {
42c8572010-06-10Martin Stjernholm  struct encode_context ctx;
df4c9a2010-05-28Martin Stjernholm  ONERROR uwp;
9eb5022010-05-28Martin Stjernholm  ctx.flags = (flags ? flags->u.integer : 0);
8d73572017-10-29Martin Nilsson  ctx.indent = (ctx.flags & JSON_HUMAN_READABLE ? base_indent ? base_indent->u.integer : 0 : -1);
71108f2013-05-23Martin Baehr  ctx.callback = callback;
df4c9a2010-05-28Martin Stjernholm  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); }
e383f22011-09-21Martin Stjernholm /*! @decl string escape_string (string str, void|int flags) *! *! Escapes string data for use in a JSON string. *! *! 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. *! *! The characters U+2028 (LINE SEPARATOR) and U+2029 (PARAGRAPH *! SEPARATOR) are exceptions - they are encoded with \u escapes for *! compatibility. The reason is that those characters are not allowed *! in Javascript strings, so JSON parsers built in Javascript may *! have trouble with them otherwise. *! *! @param val *! The string to escape. *! *! @param flags *! Flag bit field to control formatting. @[ASCII_ONLY] is the only *! flag that has any effect for this function. *! *! @note *! The difference between using this function and @[encode] on a *! string is that @[encode] returns quotations marks around the *! result. *! *! @seealso *! @[encode] */ PIKEFUN string escape_string (string str, void|int flags) { struct string_builder buf; ONERROR uwp; init_string_builder (&buf, 0); SET_ONERROR (uwp, free_string_builder, &buf); json_escape_string (&buf, flags ? flags->u.integer : 0, str); UNSET_ONERROR (uwp); RETURN finish_string_builder (&buf); }
e42d9a2010-06-10Arne Goedeke #include "json_parser.c"
a9e2ef2010-06-10Arne Goedeke void low_validate(struct pike_string *data, int flags) { ptrdiff_t stop;
e42d9a2010-06-10Arne Goedeke  struct parser_state state;
a9e2ef2010-06-10Arne Goedeke  state.flags = flags|JSON_VALIDATE;
e42d9a2010-06-10Arne Goedeke  state.level = 0;
a9e2ef2010-06-10Arne Goedeke  stop = _parse_JSON(MKPCHARP_STR(data), 0, data->len, &state);
e42d9a2010-06-10Arne Goedeke 
a9e2ef2010-06-10Arne Goedeke  if (state.flags & JSON_ERROR || stop != data->len) { push_int((INT_TYPE)stop); } else { push_int(-1);
e42d9a2010-06-10Arne Goedeke  } return; }
6234972014-08-28Per Hedbor 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; }
a9e2ef2010-06-10Arne Goedeke void low_decode(struct pike_string *data, int flags) { ptrdiff_t stop;
e42d9a2010-06-10Arne Goedeke  struct parser_state state;
423b7b2010-06-10Arne Goedeke  err_msg = NULL;
e42d9a2010-06-10Arne Goedeke  state.level = 0;
a9e2ef2010-06-10Arne Goedeke  state.flags = flags;
e42d9a2010-06-10Arne Goedeke 
a9e2ef2010-06-10Arne Goedeke  stop = _parse_JSON(MKPCHARP_STR(data), 0, data->len, &state);
e42d9a2010-06-10Arne Goedeke 
a9e2ef2010-06-10Arne Goedeke  if (!(state.flags&JSON_ERROR)) { if (stop == data->len) { return; }
e42d9a2010-06-10Arne Goedeke  }
4b439b2010-06-10Martin Stjernholm  ref_push_string(data);
a9e2ef2010-06-10Arne Goedeke  push_int((INT_TYPE)stop);
423b7b2010-06-10Arne Goedeke  if (err_msg) { push_text (err_msg); apply (Pike_fp->current_object, "decode_error", 3);
e42d9a2010-06-10Arne Goedeke  }
423b7b2010-06-10Arne Goedeke  else apply (Pike_fp->current_object, "decode_error", 2);
e42d9a2010-06-10Arne Goedeke  return; }
a9e2ef2010-06-10Arne Goedeke  /*! @decl int validate(string s) *!
423b7b2010-06-10Arne Goedeke  *! Checks if a string is valid JSON.
13670c2015-05-25Martin Nilsson  *!
a9e2ef2010-06-10Arne Goedeke  *! @returns
1b41412010-06-10Martin Stjernholm  *! In case the string contains valid JSON @expr{-1@} is returned. It is then guaranteed to be parsed
a9e2ef2010-06-10Arne Goedeke  *! without errors by @[decode()].
423b7b2010-06-10Arne Goedeke  *! In case the string is not valid JSON, the error position is returned.
a9e2ef2010-06-10Arne Goedeke  */ PIKEFUN int validate(string data) { low_validate(data, 0); }
65b3222017-10-29Martin Nilsson /*! @decl array|mapping|string|float|int|object decode(string s, void|int flags)
a9e2ef2010-06-10Arne Goedeke  *!
423b7b2010-06-10Arne Goedeke  *! Decodes a JSON string.
13670c2015-05-25Martin Nilsson  *!
65b3222017-10-29Martin Nilsson  *! @param flags
1f33812017-11-21Stephen R. van den Berg  *! The flag @[NO_OBJECTS] can be used to output @expr{1@}, @expr{0@} *! and @expr{UNDEFINED@} instead of @[Val.true], @[Val.false] and
65b3222017-10-29Martin Nilsson  *! @[Val.null]. *!
a9e2ef2010-06-10Arne Goedeke  *! @throws *! Throws an exception in case the data contained in @expr{s@} is not valid *! JSON. */
65b3222017-10-29Martin Nilsson 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);
a9e2ef2010-06-10Arne Goedeke }
e42d9a2010-06-10Arne Goedeke /*! @decl int validate_utf8(string s) *!
423b7b2010-06-10Arne Goedeke  *! Checks if a string is valid utf8 encoded JSON.
13670c2015-05-25Martin Nilsson  *!
e42d9a2010-06-10Arne Goedeke  *! @returns
1b41412010-06-10Martin Stjernholm  *! In case the string contains valid JSON @expr{-1@} is returned. It is then guaranteed to be parsed
e42d9a2010-06-10Arne Goedeke  *! without errors by @[decode()]. *! In case the string is not valid JSON, the integer position inside the string
1b41412010-06-10Martin Stjernholm  *! where the error occurs is returned.
e42d9a2010-06-10Arne Goedeke  */ PIKEFUN int validate_utf8(string data) {
423b7b2010-06-10Arne Goedeke  if (data->size_shift) Pike_error("Strings wider than 1 byte are NOT valid UTF-8.\n");
a9e2ef2010-06-10Arne Goedeke  low_validate(data, JSON_UTF8);
e42d9a2010-06-10Arne Goedeke }
423b7b2010-06-10Arne Goedeke /*! @decl array|mapping|string|float|int|object decode_utf8(string s)
e42d9a2010-06-10Arne Goedeke  *!
423b7b2010-06-10Arne Goedeke  *! Decodes an utf8 encoded JSON string.
1b41412010-06-10Martin Stjernholm  *! Should give the same result as @expr{Standards.JSON.decode(utf8_to_string(s))@}.
13670c2015-05-25Martin Nilsson  *!
e42d9a2010-06-10Arne Goedeke  *! @throws *! Throws an exception in case the data contained in @expr{s@} is not valid *! JSON. */
423b7b2010-06-10Arne Goedeke PIKEFUN array|mapping|string|float|int|object decode_utf8(string data) { if (data->size_shift) { ref_push_string(data); push_int(0);
5e9fc02015-08-18Per Hedbor  push_static_text("Strings wider than 1 byte are NOT valid UTF-8.");
423b7b2010-06-10Arne Goedeke  apply (Pike_fp->current_object, "decode_error", 3); }
a9e2ef2010-06-10Arne Goedeke  low_decode(data, JSON_UTF8);
e42d9a2010-06-10Arne Goedeke }
df4c9a2010-05-28Martin Stjernholm /*! @endmodule */ /*! @endmodule */ PIKE_MODULE_INIT {
8d73572017-10-29Martin Nilsson  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);
65b3222017-10-29Martin Nilsson  add_integer_constant ("NO_OBJECTS", JSON_NO_OBJ, 0);
df4c9a2010-05-28Martin Stjernholm  INIT;
6234972014-08-28Per Hedbor  PIKE_MODULE_EXPORT(Standards.JSON, parse_json_pcharp );
df4c9a2010-05-28Martin Stjernholm } PIKE_MODULE_EXIT { EXIT; }